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
//! # ADFGX Cipher
//!
//! Implements the functionality for the ADFGX cipher.
//!
//! > The ADFGX cipher was a field cipher used by the German Army during World War I. It is closely
//! related to the ADFGXC cipher (i.e. ADFGX is the predecessor to ADFGVX).
//!
//! > The cipher was a fractionating transposition cipher which combined a modified Polybius square
//! with a single columnar transposition.
//!
//! > The cipher is named after the five possible letters used in the ciphertext: A, D, F, G 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 ADFGX cipher implementation.
pub struct ADFGX {
    key: String,
    keyword: String,
}

impl ADFGX {
    /// Takes the key and keyword for the ADFGX cipher and returns a
    /// corresponding ADFGX struct.
    /// 
    /// # Panics
    /// * If `key` is not 25 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(), 25, "`key` must be 25 chars in length");

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

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

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

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

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