3
3
import java .util .Arrays ;
4
4
import java .util .HashMap ;
5
5
import java .util .Map ;
6
+
6
7
/**
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>
13
24
*
14
25
* @author bennybebo
15
26
*/
16
27
public class ADFGVXCipher {
17
28
29
+ // Constants used in the Polybius square
18
30
private static final char [] POLYBIUS_LETTERS = {'A' , 'D' , 'F' , 'G' , 'V' , 'X' };
19
31
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
20
34
private static final Map <String , Character > POLYBIUS_MAP = new HashMap <>();
21
35
private static final Map <Character , String > REVERSE_POLYBIUS_MAP = new HashMap <>();
22
36
37
+ // Static block to initialize the lookup tables from the Polybius square
23
38
static {
24
39
for (int i = 0 ; i < POLYBIUS_SQUARE .length ; i ++) {
25
40
for (int j = 0 ; j < POLYBIUS_SQUARE [i ].length ; j ++) {
@@ -30,26 +45,41 @@ public class ADFGVXCipher {
30
45
}
31
46
}
32
47
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
+ */
34
58
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
36
60
StringBuilder fractionatedText = new StringBuilder ();
37
61
38
- // Step 1: Polybius square substitution
39
62
for (char c : plaintext .toCharArray ()) {
40
63
fractionatedText .append (REVERSE_POLYBIUS_MAP .get (c ));
41
64
}
42
65
43
- // Step 2: Columnar transposition
44
66
return columnarTransposition (fractionatedText .toString (), key );
45
67
}
46
68
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
+ */
48
80
public String decrypt (String ciphertext , String key ) {
49
- // Step 1: Reverse the columnar transposition
50
81
String fractionatedText = reverseColumnarTransposition (ciphertext , key );
51
82
52
- // Step 2: Polybius square substitution
53
83
StringBuilder plaintext = new StringBuilder ();
54
84
for (int i = 0 ; i < fractionatedText .length (); i += 2 ) {
55
85
String pair = fractionatedText .substring (i , i + 2 );
@@ -59,14 +89,21 @@ public String decrypt(String ciphertext, String key) {
59
89
return plaintext .toString ();
60
90
}
61
91
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
+ */
62
99
private String columnarTransposition (String text , String key ) {
63
100
int numRows = (int ) Math .ceil ((double ) text .length () / key .length ());
64
101
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 , '_' );
67
104
}
68
105
69
- // Fill the table row by row
106
+ // Populate the table row by row
70
107
for (int i = 0 ; i < text .length (); i ++) {
71
108
table [i / key .length ()][i % key .length ()] = text .charAt (i );
72
109
}
@@ -88,6 +125,13 @@ private String columnarTransposition(String text, String key) {
88
125
return ciphertext .toString ();
89
126
}
90
127
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
+ */
91
135
private String reverseColumnarTransposition (String ciphertext , String key ) {
92
136
int numRows = (int ) Math .ceil ((double ) ciphertext .length () / key .length ());
93
137
char [][] table = new char [numRows ][key .length ()];
@@ -96,19 +140,19 @@ private String reverseColumnarTransposition(String ciphertext, String key) {
96
140
Arrays .sort (sortedKey );
97
141
98
142
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
100
144
for (char keyChar : sortedKey ) {
101
145
int column = key .indexOf (keyChar );
102
146
for (int row = 0 ; row < numRows ; row ++) {
103
147
if (index < ciphertext .length ()) {
104
148
table [row ][column ] = ciphertext .charAt (index ++);
105
149
} else {
106
- table [row ][column ] = '_' ; // Fill empty cells with an underscore
150
+ table [row ][column ] = '_' ;
107
151
}
108
152
}
109
153
}
110
154
111
- // Read the table row by row to get the fractionated text
155
+ // Read the table row by row to reconstruct the fractionated text
112
156
StringBuilder fractionatedText = new StringBuilder ();
113
157
for (char [] row : table ) {
114
158
for (char cell : row ) {
0 commit comments