-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcriptofep.pl
More file actions
591 lines (529 loc) · 29.7 KB
/
criptofep.pl
File metadata and controls
591 lines (529 loc) · 29.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
#!/usr/-bin/perl
#
# CriptoFEP - The Main Controller
# This script serves as the main entry point and command-line interface
# for the CriptoFEP toolkit. It handles argument parsing, validation,
# and dispatches tasks to the appropriate backend modules.
#
# --- PRAGMAS AND CORE MODULES ---
# Enforce modern Perl best practices and prevent common errors.
use strict;
use warnings;
# A robust module for parsing command-line options.
use Getopt::Long;
# --- FULL UNICODE (UTF-8) SUPPORT ---
# Allow the source code itself to contain UTF-8 characters.
use utf8;
# Set the default encoding for standard I/O (STDIN, STDOUT) and file operations to UTF-8.
use open ':std', ':encoding(UTF-8)';
# Import the 'decode' function to explicitly handle UTF-8 from command-line arguments.
use Encode qw(decode);
# --- LOAD CUSTOM MODULES ---
# Specify the 'lib' directory as a source for our custom modules.
use lib 'lib';
# Ciphers (Algorithms for hiding information, usually requiring a key)
use CriptoFEP::Cesar qw(cesar_encrypt cesar_decrypt);
use CriptoFEP::Atbash qw(atbash_cipher);
use CriptoFEP::Atbah qw(atbah_cipher);
use CriptoFEP::Albam qw(albam_cipher);
use CriptoFEP::Scytale qw(scytale_encrypt scytale_decrypt);
use CriptoFEP::Polybius qw(polybius_encrypt polybius_decrypt);
use CriptoFEP::Rot13 qw(rot13_cipher);
use CriptoFEP::Rot47 qw(rot47_cipher);
use CriptoFEP::Bacon qw(bacon_encrypt bacon_decrypt);
use CriptoFEP::XOR qw(xor_encrypt xor_decrypt);
use CriptoFEP::Vigenere qw(vigenere_encrypt vigenere_decrypt);
use CriptoFEP::Playfair qw(playfair_encrypt playfair_decrypt);
use CriptoFEP::RailFence qw(rail_fence_encrypt rail_fence_decrypt);
use CriptoFEP::Affine qw(affine_encrypt affine_decrypt);
use CriptoFEP::ADFGX qw(adfgx_encrypt adfgx_decrypt);
use CriptoFEP::ADFGVX qw(adfgvx_encrypt adfgvx_decrypt);
use CriptoFEP::Multiplicative qw(multiplicative_encrypt multiplicative_decrypt);
use CriptoFEP::KeyboardShift qw(keyboard_shift_encrypt keyboard_shift_decrypt);
use CriptoFEP::Columnar qw(columnar_encrypt columnar_decrypt);
use CriptoFEP::DoubleColumnar qw(double_columnar_encrypt double_columnar_decrypt);
use CriptoFEP::AMSCO qw(amsco_encrypt amsco_decrypt);
use CriptoFEP::CaesarBox qw(caesar_box_encrypt caesar_box_decrypt);
use CriptoFEP::Bifid qw(bifid_encrypt bifid_decrypt);
use CriptoFEP::Trifid qw(trifid_encrypt trifid_decrypt);
use CriptoFEP::TwoSquare qw(two_square_encrypt two_square_decrypt);
use CriptoFEP::ThreeSquare qw(three_square_encrypt three_square_decrypt);
use CriptoFEP::FourSquare qw(four_square_encrypt four_square_decrypt);
use CriptoFEP::Redefence qw(redefence_encrypt redefence_decrypt);
use CriptoFEP::Route qw(route_encrypt route_decrypt);
use CriptoFEP::Skip qw(skip_encrypt skip_decrypt);
use CriptoFEP::TurningGrille qw(turning_grille_encrypt turning_grille_decrypt);
use CriptoFEP::VIC qw(vic_encrypt vic_decrypt);
use CriptoFEP::VigenereStandard qw(vigenere_standard_encrypt vigenere_standard_decrypt);
use CriptoFEP::Digrafid qw(digrafid_encrypt digrafid_decrypt);
use CriptoFEP::Morbit qw(morbit_encrypt morbit_decrypt);
use CriptoFEP::Affine qw(affine_encrypt affine_decrypt);
use CriptoFEP::Beaufort qw(beaufort_cipher);
use CriptoFEP::Hill qw(hill_encrypt hill_decrypt hill_validate_key);
use CriptoFEP::Grandpre qw(grandpre_encrypt grandpre_decrypt);
use CriptoFEP::Porta qw(porta_cipher);
use CriptoFEP::Trithemius qw(trithemius_encrypt trithemius_decrypt);
# Encodings (Systems for representing information, no secret key required)
use CriptoFEP::Morse qw(morse_encode morse_decode);
use CriptoFEP::A1Z26 qw(a1z26_encode a1z26_decode);
use CriptoFEP::NATO qw(nato_encode nato_decode);
use CriptoFEP::TapCode qw(tap_code_encode tap_code_decode);
use CriptoFEP::Navajo qw(navajo_encode navajo_decode);
use CriptoFEP::AltCode qw(alt_code_encode alt_code_decode);
use CriptoFEP::T9 qw(t9_encode t9_decode);
use CriptoFEP::BrailleGS8 qw(braille_encode braille_decode);
use CriptoFEP::Base32 qw(base32_encode base32_decode);
use CriptoFEP::Base64 qw(base64_encode base64_decode);
use CriptoFEP::Base16 qw(base16_encode base16_decode );
use CriptoFEP::Base10 qw(base10_encode base10_decode );
use CriptoFEP::Base8 qw(base8_encode base8_decode );
use CriptoFEP::Base2 qw(base2_encode base2_decode );
use CriptoFEP::UrlEncode qw(url_encode url_decode);
#Analysis
use CriptoFEP::Analyzer qw(analyze_frequency analyze_ic find_key_length analyze_ngrams poly_solve);
# --- CIPHER DATA STRUCTURE ---
# A hash of hashes that organizes all our ciphers and their functions.
# This acts as a dispatch table, mapping the command-line name to its
# corresponding functions and metadata (e.g., if it requires a key).
my %ciphers = (
'cesar' => { encrypt => \&cesar_encrypt, decrypt => \&cesar_decrypt, needs_key => 0, , info => \&CriptoFEP::Cesar::info },
'atbash' => { encrypt => \&atbash_cipher, decrypt => \&atbash_cipher, needs_key => 0, , info => \&CriptoFEP::Atbash::info },
'atbah' => { encrypt => \&atbah_cipher, decrypt => \&atbah_cipher, needs_key => 0, , info => \&CriptoFEP::Atbah::info },
'affine' => { encrypt => \&affine_cipher, decrypt => \&affine_cipher, needs_key => 0, , info => \&CriptoFEP::Affine::info },
'albam' => { encrypt => \&albam_cipher, decrypt => \&albam_cipher, needs_key => 0, info => \&CriptoFEP::Albam::info },
'scytale' => { encrypt => \&scytale_encrypt, decrypt => \&scytale_decrypt, needs_key => 0, info => \&CriptoFEP::Scytale::info},
'polybius' => { encrypt => \&polybius_encrypt, decrypt => \&polybius_decrypt, needs_key => 0, info => \&CriptoFEP::Polybius::info },
'rot13' => { encrypt => \&rot13_cipher, decrypt => \&rot13_cipher, needs_key => 0, info => \&CriptoFEP::Rot13::info },
'rot47' => { encrypt => \&rot47_cipher, decrypt => \&rot47_cipher, needs_key => 0, info => \&CriptoFEP::Rot47::info },
'bacon' => { encrypt => \&bacon_encrypt, decrypt => \&bacon_decrypt, needs_key => 0, info => \&CriptoFEP::Bacon::info },
'xor' => { encrypt => \&xor_encrypt, decrypt => \&xor_decrypt, needs_key => 1, info => \&CriptoFEP::XOR::info },
'vigenere-autokey' => { encrypt => \&vigenere_encrypt, decrypt => \&vigenere_decrypt, needs_key => 1, info => \&CriptoFEP::Vigenere::info },
'playfair' => { encrypt => \&playfair_encrypt, decrypt => \&playfair_decrypt, needs_key => 1, info => \&CriptoFEP::Playfair::info },
'railfence' => { encrypt => \&rail_fence_encrypt, decrypt => \&rail_fence_decrypt, needs_key => 1, info => \&CriptoFEP::RailFence::info},
'affine' => { encrypt => \&affine_encrypt, decrypt => \&affine_decrypt, needs_key => 1, info => \&CriptoFEP::Affine::info },
'adfgx' => { encrypt => \&adfgx_encrypt, decrypt => \&adfgx_decrypt, needs_key => 1, info => \&CriptoFEP::ADFGX::info },
'adfgvx' => { encrypt => \&adfgvx_encrypt, decrypt => \&adfgvx_decrypt, needs_key => 1, info => \&CriptoFEP::ADFGVX::info },
'multiplicative' => { encrypt => \&multiplicative_encrypt, decrypt => \&multiplicative_decrypt, needs_key => 1, info => \&CriptoFEP::Multiplicative::info },
'keyboardshift' => { encrypt => \&keyboard_shift_encrypt, decrypt => \&keyboard_shift_decrypt, needs_key => 0, info => \&CriptoFEP::KeyboardShift::info },
'columnar' => { encrypt => \&columnar_encrypt, decrypt => \&columnar_decrypt, needs_key => 1, info => \&CriptoFEP::Columnar::info },
'doublecolumnar' => { encrypt => \&double_columnar_encrypt, decrypt => \&double_columnar_decrypt, needs_key => 1, info => \&CriptoFEP::DoubleColumnar::info },
'amsco' => { encrypt => \&amsco_encrypt, decrypt => \&amsco_decrypt, needs_key => 1, info => \&CriptoFEP::AMSCO::info },
'caesarbox' => { encrypt => \&caesar_box_encrypt, decrypt => \&caesar_box_decrypt, needs_key => 1, info => \&CriptoFEP::CaesarBox::info },
'bifid' => { encrypt => \&bifid_encrypt, decrypt => \&bifid_decrypt, needs_key => 1, info => \&CriptoFEP::Bifid::info },
'trifid' => { encrypt => \&trifid_encrypt, decrypt => \&trifid_decrypt, needs_key => 1, info => \&CriptoFEP::Trifid::info },
'twosquare' => { encrypt => \&two_square_encrypt, decrypt => \&two_square_decrypt, needs_key => 1, info => \&CriptoFEP::TwoSquare::info },
'threesquare' => { encrypt => \&three_square_encrypt, decrypt => \&three_square_decrypt, needs_key => 1, info => \&CriptoFEP::ThreeSquare::info },
'foursquare' => { encrypt => \&four_square_encrypt, decrypt => \&four_square_decrypt, needs_key => 1, info => \&CriptoFEP::FourSquare::info },
'redefence' => { encrypt => \&redefence_encrypt, decrypt => \&redefence_decrypt, needs_key => 1, info => \&CriptoFEP::Redefence::info },
'route' => { encrypt => \&route_encrypt, decrypt => \&route_decrypt, needs_key => 1, info => \&CriptoFEP::Route::info },
'skip' => { encrypt => \&skip_encrypt, decrypt => \&skip_decrypt, needs_key => 1, info => \&CriptoFEP::Skip::info },
'turninggrille' => { encrypt => \&turning_grille_encrypt, decrypt => \&turning_grille_decrypt, needs_key => 0, info => \&CriptoFEP::TurningGrille::info },
'vic' => { encrypt => \&vic_encrypt, decrypt => \&vic_decrypt, needs_key => 1, info => \&CriptoFEP::VIC::info },
'vigenere' => { encrypt => \&vigenere_standard_encrypt, decrypt => \&vigenere_standard_decrypt, needs_key => 1, info => \&CriptoFEP::VigenereStandard::info },
'digrafid' => { encrypt => \&digrafid_encrypt, decrypt => \&digrafid_decrypt, needs_key => 1, info => \&CriptoFEP::Digrafid::info },
'morbit' => { encrypt => \&morbit_encrypt, decrypt => \&morbit_decrypt, needs_key => 0, info => \&CriptoFEP::Morbit::info },
'beaufort' => { encrypt => \&beaufort_cipher, decrypt => \&beaufort_cipher, needs_key => 1, info => \&CriptoFEP::Beaufort::info },
'hill' => { encrypt => \&hill_encrypt, decrypt => \&hill_decrypt, needs_key => 1, info => \&CriptoFEP::Hill::info },
'grandpre' => { encrypt => \&grandpre_encrypt, decrypt => \&grandpre_decrypt, needs_key => 1, info => \&CriptoFEP::Grandpre::info },
'porta' => { encrypt => \&porta_cipher, decrypt => \&porta_cipher, needs_key => 1, info => \&CriptoFEP::Porta::info },
'trithemius' => { encrypt => \&trithemius_encrypt, decrypt => \&trithemius_decrypt, needs_key => 0, info => \&CriptoFEP::Trithemius::info },
);
# --- ENCODING DATA STRUCTURE ---
# A similar dispatch table for all supported encodings.
my %encodings = (
'morse' => { encode => \&morse_encode, decode => \&morse_decode, info => \&CriptoFEP::Morse::info },
'a1z26' => { encode => \&a1z26_encode, decode => \&a1z26_decode, info => \&CriptoFEP::A1Z26::info },
'nato' => { encode => \&nato_encode, decode => \&nato_decode, info => \&CriptoFEP::NATO::info },
'tapcode' => { encode => \&tap_code_encode,decode => \&tap_code_decode, info => \&CriptoFEP::TapCode::info},
'navajo' => { encode => \&navajo_encode, decode => \&navajo_decode, info => \&CriptoFEP::Navajo::info },
'altcode' => { encode => \&alt_code_encode,decode => \&alt_code_decode, info => \&CriptoFEP::AltCode::info},
't9' => { encode => \&t9_encode, decode => \&t9_decode, info => \&CriptoFEP::T9::info },
'braillegs8' => { encode => \&braille_encode, decode => \&braille_decode, info => \&CriptoFEP::BrailleGS8::info },
'base32' => { encode => \&base32_encode, decode => \&base32_decode, info => \&CriptoFEP::Base32::info },
'base64' => { encode => \&base64_encode, decode => \&base64_decode, info => \&CriptoFEP::Base64::info },
'base16' => { encode => \&base16_encode, decode => \&base16_decode, info => \&CriptoFEP::Base16::info },
'base10' => { encode => \&base10_encode, decode => \&base10_decode, info => \&CriptoFEP::Base10::info },
'base8' => { encode => \&base8_encode, decode => \&base8_decode, info => \&CriptoFEP::Base8::info },
'base2' => { encode => \&base2_encode, decode => \&base2_decode, info => \&CriptoFEP::Base2::info },
'hexadecimal' => { encode => \&base16_encode, decode => \&base16_decode, info => \&CriptoFEP::Base16::info },
'decimal' => { encode => \&base10_encode, decode => \&base10_decode, info => \&CriptoFEP::Base10::info },
'octal' => { encode => \&base8_encode, decode => \&base8_decode, info => \&CriptoFEP::Base8::info },
'binary' => { encode => \&base2_encode, decode => \&base2_decode, info => \&CriptoFEP::Base2::info },
'url' => { encode => \&url_encode, decode => \&url_decode, info => \&CriptoFEP::UrlEncode::info},
);
# --- USER INTERFACE ---
# Displays the application banner.
sub banner { print "\n=== CriptoFEP :: Classic Cipher Toolkit ===\n\n"; }
# Displays the help message.
sub help {
# Use qq() for a clean, multi-line string that preserves formatting.
print qq(NAME
criptofep - A command-line tool for classic cryptography and encodings.
SYNOPSIS
# Cipher Mode
perl $0 -c <cipher> [-e|-d] [options...] ["text" | --in <file>] [--out <file>]
# Encoding Mode
perl $0 -m <mapping> [-enc|-dec] ["text" | --in <file>] [--out <file>]
DESCRIPTION
A versatile toolkit to encrypt, decrypt, encode, and decode text using a
variety of classic algorithms. It supports direct command-line input as
well as file-based operations.
OPTIONS
GENERAL:
-h, --help Display this help message and exit.
--list-ciphers Display all ciphers
--list-encodings Display all encodings
--info Display detailed information about a specific cipher or encoding.
--in <FILE> Read input text from the specified file.
--out <FILE> Write the output to the specified file.
MODE SELECTION (Choose one):
-c, --cipher <NAME> Enter Cipher Mode and specify the cipher to use.
-m, --mapping <NAME> Enter Encoding Mode and specify the mapping to use.
-a, --analyze <TOOL> Select Analysis Mode (freq, ic, poly-detect, digram, tigram, poly-solve).
ACTIONS (Choose one per mode):
-e, --encrypt In Cipher Mode, encrypt the input text.
-d, --decrypt In Cipher Mode, decrypt the input text.
-enc, --encode In Encoding Mode, encode the input text.
-dec, --decode In Encoding Mode, decode the input text.
--info Display detailed information about the selected algorithm.
--lang <lg> Specify the language profile for analysis (e.g., en, pt, fr).
CIPHER-SPECIFIC KEYS:
-k, --key <KEY> Provide the primary secret key.
-k2, --key2 <KEY> Provide the second key (for 'doublecolumnar', 'twosquare', 'foursquare').
-k3, --key3 <KEY> Provide the third key (for 'threesquare').
--grid-key <KEY> Provide the grid generation key (for 'adfgx', 'adfgvx').
--pattern-key <PATTERN> Provide the pattern key (for 'amsco', e.g., "1221").
--validate-key hill <KEY> Validate key for hill(e.g., --validate-key hill "ABCF").
--date <DATE> Provide the date (for 'vic' cipher).
AVAILABLE CIPHERS
); # End of the first block of text
# Print dynamically generated lists for maintainability.
print " " . join(', ', sort keys %ciphers) . "\n\n";
print "AVAILABLE ENCODINGS\n";
print " " . join(', ', sort keys %encodings) . "\n\n";
print qq(EXAMPLES
# Encrypt a string using the Affine cipher
perl $0 -c affine -e -k "5,8" "affine cipher example"
# Decrypt a string using Double Columnar with two keys
perl $0 -c doublecolumnar -d -k "GERMAN" -k2 "SECRET" "TADWTNKA C TAA"
# Encode the content of a file using NATO and save to another file
perl $0 -m nato --encode --in message.txt --out nato_encoded.txt
# Decode a string using BrailleGS8
perl $0 -m braillegs8 -dec "⠉⠗⠊⠏⠞⠕⠋⠑⠏"
# Get information about the Caesar cipher
perl $0 -c cesar --info
# Encrypt using the complex VIC cipher
perl $0 -c vic -e -k \"A SIN TO SIN\" --date \"171025\" \"ATTACK AT DAWN\"
);
}
# --- LISTING SUBROUTINE ---
# A "private" function to list algorithms in a formatted table.
sub _list_algorithms {
my ($title, $hash_ref, $has_key_info) = @_;
print "$title:\n";
# Print the table header. The printf function formats text into columns.
if ($has_key_info) {
printf "%-18s %-15s %s\n", "Name", "Requires Key?", "Description";
printf "%-18s %-15s %s\n", "------------------", "---------------", "--------------------------------------------------";
} else {
printf "%-18s %s\n", "Name", "Description";
printf "%-18s %s\n", "------------------", "--------------------------------------------------";
}
# Iterate over all algorithms in the hash, in alphabetical order.
foreach my $name (sort keys %{$hash_ref}) {
my $info = $hash_ref->{$name};
# Extract the first line of the description from each module's info() function.
my $description = "No description available.";
if ($info->{info}) {
my $info_text = $info->{info}->();
# This regex captures the first line immediately following "DESCRIPTION:".
if ($info_text =~ /DESCRIPTION:\s*\n\s*(.*?)\n/s) {
$description = $1;
$description =~ s/\.$//; # Remove trailing period for cleaner output.
}
}
# Print the formatted table row.
if ($has_key_info) {
my $needs_key = $info->{needs_key} ? "Yes" : "No";
printf "%-18s %-15s %s\n", $name, $needs_key, $description;
} else {
printf "%-18s %s\n", $name, $description;
}
}
print "\n";
}
# --- MAIN LOGIC ---
# Declare variables that will be populated by GetOptions.
my ($cipher_name, $encrypt_flag, $decrypt_flag, $key_input, $key2_input, $key3_input, $grid_key_input, $pattern_key_input, $date_input);
my ($mapping_name, $encode_flag, $decode_flag);
my ($file_in, $file_out, $show_help, $info_flag);
my ($list_ciphers_flag, $list_encodings_flag);
my ($analyze_flag, $lang_input);
my $validate_key_cipher;
my $klen_input;
# Parse command-line arguments using Getopt::Long, mapping them to variables.
GetOptions(
'c|cipher=s' => \$cipher_name,
'e|encrypt' => \$encrypt_flag,
'd|decrypt' => \$decrypt_flag,
'k|key=s' => \$key_input,
'k2|key2=s' => \$key2_input,
'k3|key3=s' => \$key3_input,
'grid-key=s' => \$grid_key_input,
'pattern-key=s' => \$pattern_key_input,
'm|mapping=s' => \$mapping_name,
'enc|encode' => \$encode_flag,
'dec|decode' => \$decode_flag,
'info' => \$info_flag,
'in=s' => \$file_in,
'out=s' => \$file_out,
'h|help' => \$show_help,
'date=s' => \$date_input,
'list-ciphers' => \$list_ciphers_flag,
'list-encodings' => \$list_encodings_flag,
'a|analyze=s' => \$analyze_flag,
'lang=s' => \$lang_input,
'validate-key=s' => \$validate_key_cipher,
'klen=i' => \$klen_input,
);
# Display the application banner on every run.
banner();
# --- LISTING MODE ---
# These commands have priority and will exit the program after execution.
if ($list_ciphers_flag) {
_list_algorithms("Available Ciphers", \%ciphers, 1);
exit 0;
}
elsif ($list_encodings_flag) {
_list_algorithms("Available Encodings", \%encodings, 0);
exit 0;
}
# ----------------------------------------
elsif (defined $validate_key_cipher) {
my $key_to_validate = shift @ARGV;
die "ERROR: No key provided for validation.\n" unless defined $key_to_validate;
$key_to_validate = decode('UTF-8', $key_to_validate);
my $result;
if (lc($validate_key_cipher) eq 'hill') {
$result = hill_validate_key($key_to_validate);
}
else {
die "ERROR: Key validation for '$validate_key_cipher' is not supported (yet).\n";
}
print $result;
exit 0;
}
# --- ANALYSIS MODE ---
elsif (defined $analyze_flag) {
my $text_input;
if (defined $file_in) {
open my $fh, '<', $file_in or die "ERROR: Could not open input file '$file_in': $!\n";
read $fh, $text_input, -s $fh; close $fh;
chomp($text_input);
print "Read from file: '$file_in'\n";
} else {
$text_input = shift @ARGV;
}
die "ERROR: No input text provided for analysis.\n" unless defined $text_input;
$text_input = decode('UTF-8', $text_input) unless defined $file_in;
# --- Fim da Lógica de Entrada ---
my $analysis_result;
my $command_info = "Analysis: $analyze_flag";
if ($analyze_flag eq 'freq') {
$analysis_result = analyze_frequency($text_input, $lang_input);
$command_info .= ", Language: " . ($lang_input // 'pt');
}
elsif ($analyze_flag eq 'ic') {
$analysis_result = analyze_ic($text_input, $lang_input);
$command_info .= ", Language: " . ($lang_input // 'pt');
}
elsif ($analyze_flag eq 'poly-detect') {
$analysis_result = find_key_length($text_input, $lang_input);
$command_info .= ", Language: " . ($lang_input // 'pt');
}
elsif ($analyze_flag eq 'digram') {
$analysis_result = analyze_ngrams($text_input, 2);
}
elsif ($analyze_flag eq 'trigram') {
$analysis_result = analyze_ngrams($text_input, 3);
}
elsif ($analyze_flag eq 'poly-solve') {
die "ERROR: The 'poly-solve' analysis requires a key length (ex: --klen 5).\n"
unless defined $klen_input;
$analysis_result = poly_solve($text_input, $klen_input, $lang_input);
$command_info .= ", KeyLength: $klen_input, Language: " . ($lang_input // 'pt');
}
else {
die "ERROR: Unknown analysis type '$analyze_flag'. Supported: 'freq', 'ic', 'poly-detect', 'digram', 'trigram', 'poly-solve'.\n";
}
print "$command_info\n";
if (defined $file_out) {
open my $fh, '>', $file_out or die "ERROR: Could not write to output file '$file_out': $!\n";
print $fh $analysis_result; # Escreve o relatório completo no ficheiro
close $fh;
print "Analysis report successfully saved to '$file_out'.\n\n";
} else {
print $analysis_result;
}
exit 0;
# --- FIM DA CORREÇÃO ---
}
# --- INFO MODE ---
# This mode also has priority, displaying help for a specific algorithm.
if ($info_flag) {
my $target_name = $cipher_name // $mapping_name;
die "ERROR: You must specify a cipher (-c) or mapping (-m) to get info about.\n" unless $target_name;
my $info_ref;
if ($cipher_name && exists $ciphers{$cipher_name}{info}) {
$info_ref = $ciphers{$cipher_name}{info};
}
elsif ($mapping_name && exists $encodings{$mapping_name}{info}) {
$info_ref = $encodings{$mapping_name}{info};
}
if ($info_ref) {
# Call the info() subroutine from the respective module.
print $info_ref->();
} else {
print "No detailed information available for '$target_name'.\n";
}
exit;
}
# --- CIPHER MODE ---
if ($cipher_name) {
# --- Input Validation ---
die "ERROR: You cannot specify a cipher (-c) and a mapping (-m) at the same time.\n" if $mapping_name;
die "ERROR: You must specify an action: --encrypt (-e) or --decrypt (-d).\n" unless $encrypt_flag || $decrypt_flag;
die "ERROR: --encrypt and --decrypt options cannot be used together.\n" if $encrypt_flag && $decrypt_flag;
die "ERROR: Cipher '$cipher_name' is unknown.\n" unless exists $ciphers{$cipher_name};
# Determine the action and the corresponding function reference from the dispatch table.
my $action = $encrypt_flag ? 'encrypt' : 'decrypt';
my $cipher_info = $ciphers{$cipher_name};
my $function_ref = $cipher_info->{$action};
# --- Get Input Text (from file or command line) ---
my $text_input;
if (defined $file_in) {
open my $fh, '<', $file_in or die "ERROR: Could not open input file '$file_in': $!\n";
read $fh, $text_input, -s $fh; close $fh;
chomp($text_input);
print "Read from file: '$file_in'\n";
} else {
$text_input = shift @ARGV;
}
die "ERROR: No input text provided.\n" unless defined $text_input;
# Ensure command-line input is treated as UTF-8.
$text_input = decode('UTF-8', $text_input) unless defined $file_in;
# --- Execute Cipher ---
my $final_result;
my $command_info = "Cipher: $cipher_name, Action: $action";
# Special handling for ciphers requiring multiple, specific keys.
if ($cipher_name eq 'amsco') {
die "ERROR: The 'amsco' cipher requires a primary key (-k) and a pattern key (--pattern-key).\n"
unless defined $key_input && defined $pattern_key_input;
die "ERROR: The pattern key for 'amsco' must only contain '1's and '2's.\n"
if $pattern_key_input =~ /[^12]/;
$final_result = $function_ref->($text_input, [$key_input, $pattern_key_input]);
$command_info .= ", Key: \"$key_input\", Pattern: \"$pattern_key_input\"";
}
elsif ($cipher_name eq 'vic') {
die "ERROR: The 'vic' cipher requires a phrase (-k) and a date (--date).\n"
unless defined $key_input && defined $date_input;
$final_result = $function_ref->($text_input, [$key_input, $date_input]);
$command_info .= ", Phrase: \"$key_input\", Date: \"$date_input\"";
}
elsif ($cipher_name eq 'doublecolumnar' || $cipher_name eq 'twosquare' || $cipher_name eq 'foursquare' || $cipher_name eq 'grandpre') {
die "ERROR: The '$cipher_name' cipher requires a primary key (-k) and a second key (--key2).\n"
unless defined $key_input && defined $key2_input;
# Pass both keys as an array reference.
$final_result = $function_ref->($text_input, [$key_input, $key2_input]);
$command_info .= ", Key 1: \"$key_input\", Key 2: \"$key2_input\"";
}
elsif ($cipher_name eq 'adfgvx' || $cipher_name eq 'adfgx' ) {
die "ERROR: The '$cipher_name' cipher requires a transposition key (-k) and a grid key (--grid-key).\n" unless defined $key_input && defined $grid_key_input;
$final_result = $function_ref->($text_input, [$grid_key_input, $key_input]);
$command_info .= ", Transposition Key: \"$key_input\", Grid Key: \"$grid_key_input\"";
}
elsif ($cipher_name eq 'threesquare') {
die "ERROR: The 'threesquare' cipher requires three keys (-k, -k2, -k3).\n"
unless defined $key_input && defined $key2_input && defined $key3_input;
$final_result = $function_ref->($text_input, [$key_input, $key2_input, $key3_input]);
$command_info .= ", Key 1: \"$key_input\", Key 2: \"$key2_input\", Key 3: \"$key3_input\"";
}
# General handling for all other ciphers that need a single key.
elsif ($cipher_info->{needs_key}) {
die "ERROR: The '$cipher_name' cipher requires a key (-k).\n" unless defined $key_input;
# Specific key validations based on the cipher's requirements.
if ($cipher_name eq 'railfence' || $cipher_name eq 'caesarbox' || $cipher_name eq 'railfence' || $cipher_name eq 'route' || $cipher_name eq 'skip') {
unless ($key_input =~ /^\d+$/ && $key_input > 1) {
die "ERROR: The key for '$cipher_name' must be an integer greater than 1.\n";
}
}
elsif ($cipher_name eq 'affine') {
unless ($key_input =~ /^\d+,\d+$/) {
die "ERROR: The key for 'affine' must be in the format 'a,b'.\n";
}
}
elsif ($cipher_name eq 'multiplicative') {
unless ($key_input =~ /^\d+$/ && grep { $_ == $key_input } (1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25)) {
die "ERROR: The key for 'multiplicative' cipher must be an integer coprime with 26.\n" .
" Possible values: 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25.\n";
}
}
$final_result = $function_ref->($text_input, $key_input);
$command_info .= ", Key: \"$key_input\"";
}
# Handling for keyless ciphers.
else {
$final_result = $function_ref->($text_input);
}
print "$command_info\n";
# --- Output Result ---
# Write to a file if --out is specified, otherwise print to the console.
if (defined $file_out) {
open my $fh, '>', $file_out or die "ERROR: Could not write to output file '$file_out': $!\n";
print $fh $final_result; close $fh;
print "Output successfully saved to '$file_out'.\n\n";
} else {
print "Result => [$final_result]\n\n";
}
}
# --- ENCODING MODE ---
elsif ($mapping_name) {
# --- Input Validation ---
die "ERROR: You must specify an action: --encode or --decode.\n" unless $encode_flag || $decode_flag;
die "ERROR: --encode and --decode options cannot be used together.\n" if $encode_flag && $decode_flag;
die "ERROR: Encoding '$mapping_name' is unknown.\n" unless exists $encodings{$mapping_name};
# Determine action and function reference.
my $action = $encode_flag ? 'encode' : 'decode';
my $mapping_info = $encodings{$mapping_name};
my $function_ref = $mapping_info->{$action};
# Get input text.
my $text_input;
if (defined $file_in) {
open my $fh, '<', $file_in or die "ERROR: Could not open input file '$file_in': $!\n";
read $fh, $text_input, -s $fh; close $fh;
chomp($text_input);
print "Read from file: '$file_in'\n";
} else {
$text_input = shift @ARGV;
}
die "ERROR: No input text provided.\n" unless defined $text_input;
$text_input = decode('UTF-8', $text_input) unless defined $file_in;
# Execute encoding/decoding.
my $final_result = $function_ref->($text_input);
my $command_info = "Encoding: $mapping_name, Action: $action";
print "$command_info\n";
# Output result.
if (defined $file_out) {
open my $fh, '>', $file_out or die "ERROR: Could not write to output file '$file_out': $!\n";
print $fh $final_result; close $fh;
print "Output successfully saved to '$file_out'.\n\n";
} else {
print "Result => [$final_result]\n\n";
}
}
# --- NO MODE CHOSEN ---
# If no cipher or mapping was specified, or if -h was used, display help and exit.
else {
help();
exit;
}