33import java .util .Arrays ;
44import java .util .HashMap ;
55import java .util .Map ;
6+
67/**
7- * The ADFGVX cipher is a historically significant cipher used by
8- * the German Army during World War I. It is a fractionating transposition
9- * cipher that combines a Polybius square substitution with a columnar
10- * transposition. It's named after the six letters (A, D, F, G, V, X)
11- * that it uses in its substitution process.
12- * https://en.wikipedia.org/wiki/ADFGVX_cipher
8+ * The ADFGVX cipher is a fractionating transposition cipher that was used by
9+ * the German Army during World War I. It combines a **Polybius square substitution**
10+ * with a **columnar transposition** to enhance encryption strength.
11+ * <p>
12+ * The name "ADFGVX" refers to the six letters (A, D, F, G, V, X) used as row and
13+ * column labels in the Polybius square. This cipher was designed to secure
14+ * communication and create complex, hard-to-break ciphertexts.
15+ * <p>
16+ * Learn more: <a href="https://en.wikipedia.org/wiki/ADFGVX_cipher">ADFGVX Cipher - Wikipedia</a>.
17+ * <p>
18+ * Example usage:
19+ * <pre>
20+ * ADFGVXCipher cipher = new ADFGVXCipher();
21+ * String encrypted = cipher.encrypt("attack at 1200am", "PRIVACY");
22+ * String decrypted = cipher.decrypt(encrypted, "PRIVACY");
23+ * </pre>
1324 *
1425 * @author bennybebo
1526 */
1627public class ADFGVXCipher {
1728
29+ // Constants used in the Polybius square
1830 private static final char [] POLYBIUS_LETTERS = {'A' , 'D' , 'F' , 'G' , 'V' , 'X' };
1931 private static final char [][] POLYBIUS_SQUARE = {{'N' , 'A' , '1' , 'C' , '3' , 'H' }, {'8' , 'T' , 'B' , '2' , 'O' , 'M' }, {'E' , '5' , 'W' , 'R' , 'P' , 'D' }, {'4' , 'F' , '6' , 'G' , '7' , 'I' }, {'9' , 'J' , '0' , 'K' , 'L' , 'Q' }, {'S' , 'U' , 'V' , 'X' , 'Y' , 'Z' }};
32+
33+ // Maps for fast substitution lookups
2034 private static final Map <String , Character > POLYBIUS_MAP = new HashMap <>();
2135 private static final Map <Character , String > REVERSE_POLYBIUS_MAP = new HashMap <>();
2236
37+ // Static block to initialize the lookup tables from the Polybius square
2338 static {
2439 for (int i = 0 ; i < POLYBIUS_SQUARE .length ; i ++) {
2540 for (int j = 0 ; j < POLYBIUS_SQUARE [i ].length ; j ++) {
@@ -30,26 +45,41 @@ public class ADFGVXCipher {
3045 }
3146 }
3247
33- // Encrypts the plaintext using the ADFGVX cipher
48+ /**
49+ * Encrypts a given plaintext using the ADFGVX cipher with the provided keyword.
50+ * Steps:
51+ * 1. Substitute each letter in the plaintext with a pair of ADFGVX letters.
52+ * 2. Perform a columnar transposition on the fractionated text using the keyword.
53+ *
54+ * @param plaintext The message to be encrypted (can contain letters and digits).
55+ * @param key The keyword for columnar transposition.
56+ * @return The encrypted message as ciphertext.
57+ */
3458 public String encrypt (String plaintext , String key ) {
35- plaintext = plaintext .toUpperCase ().replaceAll ("[^A-Z0-9]" , "" );
59+ plaintext = plaintext .toUpperCase ().replaceAll ("[^A-Z0-9]" , "" ); // Sanitize input
3660 StringBuilder fractionatedText = new StringBuilder ();
3761
38- // Step 1: Polybius square substitution
3962 for (char c : plaintext .toCharArray ()) {
4063 fractionatedText .append (REVERSE_POLYBIUS_MAP .get (c ));
4164 }
4265
43- // Step 2: Columnar transposition
4466 return columnarTransposition (fractionatedText .toString (), key );
4567 }
4668
47- // Decrypts the ciphertext using the ADFGVX cipher
69+ /**
70+ * Decrypts a given ciphertext using the ADFGVX cipher with the provided keyword.
71+ * Steps:
72+ * 1. Reverse the columnar transposition performed during encryption.
73+ * 2. Substitute each pair of ADFGVX letters with the corresponding plaintext letter.
74+ * The resulting text is the decrypted message.
75+ *
76+ * @param ciphertext The encrypted message.
77+ * @param key The keyword used during encryption.
78+ * @return The decrypted plaintext message.
79+ */
4880 public String decrypt (String ciphertext , String key ) {
49- // Step 1: Reverse the columnar transposition
5081 String fractionatedText = reverseColumnarTransposition (ciphertext , key );
5182
52- // Step 2: Polybius square substitution
5383 StringBuilder plaintext = new StringBuilder ();
5484 for (int i = 0 ; i < fractionatedText .length (); i += 2 ) {
5585 String pair = fractionatedText .substring (i , i + 2 );
@@ -59,14 +89,21 @@ public String decrypt(String ciphertext, String key) {
5989 return plaintext .toString ();
6090 }
6191
92+ /**
93+ * Helper method: Performs columnar transposition during encryption
94+ *
95+ * @param text The fractionated text to be transposed
96+ * @param key The keyword for columnar transposition
97+ * @return The transposed text
98+ */
6299 private String columnarTransposition (String text , String key ) {
63100 int numRows = (int ) Math .ceil ((double ) text .length () / key .length ());
64101 char [][] table = new char [numRows ][key .length ()];
65- for (char [] row : table ) {
66- Arrays .fill (row , '_' ); // Fill with underscores to handle empty cells
102+ for (char [] row : table ) { // Fill empty cells with underscores
103+ Arrays .fill (row , '_' );
67104 }
68105
69- // Fill the table row by row
106+ // Populate the table row by row
70107 for (int i = 0 ; i < text .length (); i ++) {
71108 table [i / key .length ()][i % key .length ()] = text .charAt (i );
72109 }
@@ -88,6 +125,13 @@ private String columnarTransposition(String text, String key) {
88125 return ciphertext .toString ();
89126 }
90127
128+ /**
129+ * Helper method: Reverses the columnar transposition during decryption
130+ *
131+ * @param ciphertext The transposed text to be reversed
132+ * @param key The keyword used during encryption
133+ * @return The reversed text
134+ */
91135 private String reverseColumnarTransposition (String ciphertext , String key ) {
92136 int numRows = (int ) Math .ceil ((double ) ciphertext .length () / key .length ());
93137 char [][] table = new char [numRows ][key .length ()];
@@ -96,19 +140,19 @@ private String reverseColumnarTransposition(String ciphertext, String key) {
96140 Arrays .sort (sortedKey );
97141
98142 int index = 0 ;
99- // Fill the table column by column according to the sorted key order
143+ // Populate the table column by column according to the sorted key
100144 for (char keyChar : sortedKey ) {
101145 int column = key .indexOf (keyChar );
102146 for (int row = 0 ; row < numRows ; row ++) {
103147 if (index < ciphertext .length ()) {
104148 table [row ][column ] = ciphertext .charAt (index ++);
105149 } else {
106- table [row ][column ] = '_' ; // Fill empty cells with an underscore
150+ table [row ][column ] = '_' ;
107151 }
108152 }
109153 }
110154
111- // Read the table row by row to get the fractionated text
155+ // Read the table row by row to reconstruct the fractionated text
112156 StringBuilder fractionatedText = new StringBuilder ();
113157 for (char [] row : table ) {
114158 for (char cell : row ) {
0 commit comments