1use crate::columnar_transposition::ColumnarTransposition;
7use crate::common::cipher::Cipher;
8use crate::common::{alphabet, keygen};
9use crate::Polybius;
10use std::string::String;
11
12const ADFGVX_CHARS: [char; 6] = ['A', 'D', 'F', 'G', 'V', 'X'];
13
14pub struct ADFGVX {
16 polybius_cipher: Polybius,
17 columnar_cipher: ColumnarTransposition,
18}
19
20impl Cipher for ADFGVX {
21 type Key = (String, String, Option<char>);
22 type Algorithm = ADFGVX;
23
24 fn new(key: (String, String, Option<char>)) -> ADFGVX {
41 let p_key = keygen::keyed_alphabet(&key.0, &alphabet::ALPHANUMERIC, false);
43
44 ADFGVX {
45 polybius_cipher: Polybius::new((p_key, ADFGVX_CHARS, ADFGVX_CHARS)),
46 columnar_cipher: ColumnarTransposition::new((key.1, key.2)),
47 }
48 }
49
50 fn encrypt(&self, message: &str) -> Result<String, &'static str> {
81 let step_one = self.polybius_cipher.encrypt(message)?;
83 self.columnar_cipher.encrypt(&step_one)
85 }
86
87 fn decrypt(&self, ciphertext: &str) -> Result<String, &'static str> {
117 let step_one = self.columnar_cipher.decrypt(ciphertext)?;
119 self.polybius_cipher.decrypt(&step_one)
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn encrypt_simple() {
130 let a = ADFGVX::new((
131 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
132 String::from("GERMAN"),
133 None,
134 ));
135
136 let cipher_text = concat!(
137 "gfxffgxgDFAXDAVGDgxvadaaxxXFDDFGGGFdfaxdavgdVDAGFAX",
138 "VVxfddfgggfVVVAGFFAvvvagffaGXVADAAXXvdagfaxvvGFXFFGXG"
139 );
140 assert_eq!(
141 cipher_text,
142 a.encrypt("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
143 .unwrap()
144 );
145 }
146
147 #[test]
148 fn encrypt_with_space_padding() {
149 let a = ADFGVX::new((
150 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
151 String::from("GERMAN"),
152 Some(' '),
153 ));
154
155 let cipher_text = concat!(
158 "gfxffgxgDFAXDAVGD gxvadaaxxXFDDFGGGFdfaxdavgdVDAGFAX",
159 "VVxfddfgggfVVVAGFFA vvvagffaGXVADAAXX vdagfaxvvGFXFFGXG "
160 );
161 assert_eq!(
162 cipher_text,
163 a.encrypt("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
164 .unwrap()
165 );
166 }
167
168 #[test]
169 fn decrypt_message() {
170 let a = ADFGVX::new((
171 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
172 String::from("GERMAN"),
173 None,
174 ));
175
176 let cipher_text = concat!(
177 "gfxffgxgDFAXDAVGDgxvadaaxxXFDDFGGGFdfaxdavgdVDAGFAX",
178 "VVxfddfgggfVVVAGFFAvvvagffaGXVADAAXXvdagfaxvvGFXFFGXG"
179 );
180 assert_eq!(
181 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
182 a.decrypt(cipher_text).unwrap()
183 );
184 }
185
186 #[test]
187 fn decrypt_with_space_padding() {
188 let a = ADFGVX::new((
189 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
190 String::from("GERMAN"),
191 Some(' '),
192 ));
193
194 let cipher_text = concat!(
197 "gfxffgxgDFAXDAVGD gxvadaaxxXFDDFGGGFdfaxdavgdVDAGFAX",
198 "VVxfddfgggfVVVAGFFA vvvagffaGXVADAAXX vdagfaxvvGFXFFGXG "
199 );
200 assert_eq!(
201 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
202 a.decrypt(cipher_text).unwrap()
203 );
204 }
205
206 #[test]
207 fn simple() {
208 let a = ADFGVX::new((
209 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
210 String::from("VICTORY"),
211 None,
212 ));
213
214 let plain_text = concat!(
215 "We attack at dawn, not later when it is light, ",
216 "or at some strange time of the clock. Only at dawn."
217 );
218 assert_eq!(
219 a.decrypt(&a.encrypt(plain_text).unwrap()).unwrap(),
220 plain_text
221 );
222 }
223
224 #[test]
225 fn simple_with_padding() {
226 let a = ADFGVX::new((
227 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
228 String::from("VICTORY"),
229 Some('\u{0}'),
230 ));
231
232 let plain_text = concat!(
233 "We attack at dawn, not later when it is light, ",
234 "or at some strange time of the clock. Only at dawn."
235 );
236 assert_eq!(
237 a.decrypt(&a.encrypt(plain_text).unwrap()).unwrap(),
238 plain_text
239 );
240 }
241
242 #[test]
243 fn plaintext_with_padding() {
244 let a = ADFGVX::new((
245 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
246 String::from("VICTORY"),
247 Some(' '),
248 ));
249
250 let plain_text = "This will fail because of spaces.";
251 assert!(a.encrypt(plain_text).is_err());
252 }
253
254 #[test]
255 fn with_utf8() {
256 let plain_text = "Attack 🗡️ the east wall";
257 let a = ADFGVX::new((
258 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
259 String::from("GERMAN"),
260 None,
261 ));
262
263 assert_eq!(
264 plain_text,
265 a.decrypt(&a.encrypt(plain_text).unwrap()).unwrap()
266 );
267 }
268
269 #[test]
270 fn with_utf8_with_padding() {
271 let plain_text = "Attack 🗡️ the east wall";
272 let a = ADFGVX::new((
273 String::from("ph0qg64mea1yl2nofdxkr3cvs5zw7bj9uti8"),
274 String::from("GERMAN"),
275 Some('\u{0}'),
276 ));
277
278 assert_eq!(
279 plain_text,
280 a.decrypt(&a.encrypt(plain_text).unwrap()).unwrap()
281 );
282 }
283
284 #[test]
285 #[should_panic]
286 fn invalid_key_phrase() {
287 ADFGVX::new((String::from("F@il"), String::from("GERMAN"), None));
288 }
289}