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
//! # ADFGVX Cipher
//!
//! Implements the functionality for the ADFGVX cipher.
//!
//! The following is an excerpt from [Wikipedia](https://en.wikipedia.org/wiki/ADFGVX_cipher).
//! > In cryptography, the ADFGVX cipher was a field cipher used by the German Army on the Western
//! Front during World War I. ADFGVX was in fact an extension of an earlier cipher called ADFGX.
//!
//! > Invented by Lieutenant Fritz Nebel (1891–1977) and introduced in March 1918, the cipher was a
//! fractionating transposition cipher which combined a modified Polybius square with a single
//! columnar transposition.W
//!
//! > The cipher is named after the six possible letters used in the ciphertext: A, D, F, G, V and
//! X. The letters were chosen deliberately because they are very different from one another in the
//! Morse code. That reduced the possibility of operator error.

use crate::{Cipher, CipherResult, ColumnarTransposition, PolybiusSquare};

/// An ADFGVX cipher implementation.
pub struct ADFGVX {
    key: String,
    keyword: String,
}

impl ADFGVX {
    /// Takes the key and keyword for the ADFGVX cipher and returns a
    /// corresponding ADFGVX struct.
    /// 
    /// # Panics
    /// * If `key` is not 36 chars in length.
    /// * If `key` contains repeated chars.
    /// * If `key` is not valid ascii.
    /// * If `keyword` is not valid ascii.
    pub fn new(key: &str, keyword: &str) -> Self {
        assert_eq!(key.len(), 36, "`key` must be 36 chars in length");

        Self {
            key: String::from(key),
            keyword: String::from(keyword),
        }
    }
}

impl Cipher for ADFGVX {
    /// Enciphers the given plaintext (a str reference) using the ADFGVX cipher
    /// and returns the ciphertext as a `CipherResult`.
    ///
    /// # Example
    /// ```
    /// use ciphers::{Cipher, ADFGVX};
    ///
    /// let adfgvx = ADFGVX::new("PH0QG64MEA1YL2NOFDXKR3CVS5ZW7BJ9UTI8", "GERMAN");
    ///
    /// let ctext = adfgvx.encipher("DEFENDTHEEASTWALLOFTHECASTLE");
    /// assert_eq!(ctext.unwrap(), "FFDVDFADFXFGFGAVFAFFDXDXFFDVDFFDGGAGVGVXFAGGDGADFADVFXGX");
    /// ```
    fn encipher(&self, ptext: &str) -> CipherResult {
        let ps = PolybiusSquare::new(&self.key, "ADFGVX");
        let ct = ColumnarTransposition::new(&self.keyword);

        ct.encipher(&ps.encipher(ptext)?)
    }

    /// Deciphers the given ciphertext (a str reference) using the ADFGVX cipher
    /// and returns the plaintext as a `CipherResult`.
    ///
    /// # Example
    /// ```
    /// use ciphers::{Cipher, ADFGVX};
    ///
    /// let adfgvx = ADFGVX::new("PH0QG64MEA1YL2NOFDXKR3CVS5ZW7BJ9UTI8", "GERMAN");
    ///
    /// let ptext = adfgvx.decipher("FFDVDFADFXFGFGAVFAFFDXDXFFDVDFFDGGAGVGVXFAGGDGADFADVFXGX");
    /// assert_eq!(ptext.unwrap(), "DEFENDTHEEASTWALLOFTHECASTLE");
    /// ```
    fn decipher(&self, ctext: &str) -> CipherResult {
        let ps = PolybiusSquare::new(&self.key, "ADFGVX");
        let ct = ColumnarTransposition::new(&self.keyword);

        ps.decipher(&ct.decipher(ctext)?)
    }
}