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,19 @@ 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
67- }
102+ for (char [] row : table ) Arrays .fill (row , '_' ); // Fill empty cells with underscores
68103
69- // Fill the table row by row
104+ // Populate the table row by row
70105 for (int i = 0 ; i < text .length (); i ++) {
71106 table [i / key .length ()][i % key .length ()] = text .charAt (i );
72107 }
@@ -88,6 +123,13 @@ private String columnarTransposition(String text, String key) {
88123 return ciphertext .toString ();
89124 }
90125
126+ /**
127+ * Helper method: Reverses the columnar transposition during decryption
128+ *
129+ * @param ciphertext The transposed text to be reversed
130+ * @param key The keyword used during encryption
131+ * @return The reversed text
132+ */
91133 private String reverseColumnarTransposition (String ciphertext , String key ) {
92134 int numRows = (int ) Math .ceil ((double ) ciphertext .length () / key .length ());
93135 char [][] table = new char [numRows ][key .length ()];
@@ -96,19 +138,19 @@ private String reverseColumnarTransposition(String ciphertext, String key) {
96138 Arrays .sort (sortedKey );
97139
98140 int index = 0 ;
99- // Fill the table column by column according to the sorted key order
141+ // Populate the table column by column according to the sorted key
100142 for (char keyChar : sortedKey ) {
101143 int column = key .indexOf (keyChar );
102144 for (int row = 0 ; row < numRows ; row ++) {
103145 if (index < ciphertext .length ()) {
104146 table [row ][column ] = ciphertext .charAt (index ++);
105147 } else {
106- table [row ][column ] = '_' ; // Fill empty cells with an underscore
148+ table [row ][column ] = '_' ;
107149 }
108150 }
109151 }
110152
111- // Read the table row by row to get the fractionated text
153+ // Read the table row by row to reconstruct the fractionated text
112154 StringBuilder fractionatedText = new StringBuilder ();
113155 for (char [] row : table ) {
114156 for (char cell : row ) {
0 commit comments