kryptos/ciphers/
vigenere.rs

1/// Vigenere Cipher
2///
3/// The struct is generated through the new() function.
4///
5pub struct Vigenere {
6    key: &'static str,
7}
8
9impl Vigenere {
10    /// Initializes a vigenere cipher with a supplied key.
11    ///
12    /// # Examples
13    ///
14    /// ```
15    /// use kryptos::ciphers::vigenere::Vigenere;
16    ///
17    /// let v = Vigenere::new("secret").unwrap();
18    /// ```
19    ///
20    /// # Errors
21    ///
22    /// Will return Err() if the key is not alphabetic.
23    ///
24    pub fn new(key: &'static str) -> Result<Self, String> {
25        for c in key.chars() {
26            if c.is_alphabetic() {
27                continue;
28            }
29            return Err(String::from("Key must be alphabetic"));
30        }
31        Ok(Vigenere { key })
32    }
33
34    /// Enciphers a message with a vignere cipher.
35    ///
36    /// # Examples
37    ///
38    /// ```
39    /// use kryptos::ciphers::vigenere::Vigenere;
40    ///
41    /// let v = Vigenere::new("blaise").unwrap();
42    /// assert_eq!(
43    ///     "tsh ggy ilvm qsv hhqktfc",
44    ///     v.encipher("shh you have you whisper").unwrap()
45    /// );
46    /// ```
47    ///
48    pub fn encipher(&self, plaintext: &str) -> Result<String, &'static str> {
49        Vigenere::transpose(&self.convert_key().unwrap(), plaintext)
50    }
51
52    /// Deciphers a message with a vignere cipher.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use kryptos::ciphers::vigenere::Vigenere;
58    ///
59    /// let v = Vigenere::new("blaise").unwrap();
60    /// assert_eq!(
61    ///     "whispering can still be heard by others",
62    ///     v.decipher("xsiahistno ueo dtqdp cp hmsve my wllfcs").unwrap()
63    /// );
64    /// ```
65    ///
66    pub fn decipher(&self, ciphertext: &str) -> Result<String, &'static str> {
67        let mut filter = Vec::new();
68
69        for n in self.convert_key().unwrap() {
70            filter.push((26 - n) % 26);
71        }
72        Vigenere::transpose(&filter, ciphertext)
73    }
74
75    // Uses the converted key to perform the encipher or decipher of a message.
76    //
77    fn transpose(filter: &[u8], text: &str) -> Result<String, &'static str> {
78        let mut filter_index = 0;
79        let mut result = String::new();
80
81        for c in text.chars() {
82            match c as u8 {
83                65..=90 => {
84                    result.push(
85                        (((c as u8 - 65 + filter[filter_index % filter.len()]) % 26) + 65) as char,
86                    );
87                    filter_index += 1;
88                }
89                97..=122 => {
90                    result.push(
91                        (((c as u8 - 97 + filter[filter_index % filter.len()]) % 26) + 97) as char,
92                    );
93                    filter_index += 1;
94                }
95                _ => result.push(c),
96            }
97        }
98        Ok(result)
99    }
100
101    // Converts a key into a vector of u8.
102    //
103    fn convert_key(&self) -> Result<Vec<u8>, String> {
104        self.key
105            .chars()
106            .map(|c| match c as u8 {
107                65..=90 => Ok((c as u8 - 65) % 26),
108                97..=122 => Ok((c as u8 - 97) % 26),
109                _ => Err(String::from("Invalid character in key")),
110            })
111            .collect()
112    }
113}
114
115#[cfg(test)]
116mod tests {
117    use super::Vigenere;
118
119    #[test]
120    fn valid_key() {
121        assert!(Vigenere::new("secret").is_ok());
122    }
123
124    #[test]
125    fn invalid_key() {
126        assert!(Vigenere::new("s3cr3t").is_err());
127    }
128
129    #[test]
130    fn key_conversion() {
131        let v = vec![3, 8, 5, 5, 8, 4];
132        let x = Vigenere::new("diffie").unwrap();
133        assert_eq!(v, x.convert_key().unwrap());
134    }
135
136    #[test]
137    fn invalid_key_conversion() {
138        assert!(Vigenere::convert_key(&Vigenere { key: "dif.fie" }).is_err());
139    }
140
141    #[test]
142    fn encipher() {
143        let v = Vigenere::new("blaise").unwrap();
144        assert_eq!(
145            "tsh ggy ilvm qsv hhqktfc",
146            v.encipher("shh you have you whisper").unwrap()
147        );
148    }
149
150    #[test]
151    fn with_punctuation() {
152        let v = Vigenere::new("blaise").unwrap();
153        assert_eq!(
154            "tsh! ggy ilvm qsv hhqktfc",
155            v.encipher("shh! you have you whisper").unwrap()
156        );
157    }
158
159    #[test]
160    fn with_unicode() {
161        let v = Vigenere::new("babbage").unwrap();
162        assert_eq!(
163            "Eo zpu 🖤 yidrfu mkwtahfs?",
164            v.encipher("Do you 🖤 secret messages?").unwrap()
165        );
166    }
167
168    #[test]
169    fn decipher() {
170        let v = Vigenere::new("blaise").unwrap();
171        assert_eq!(
172            "whispering can still be heard by others",
173            v.decipher("xsiahistno ueo dtqdp cp hmsve my wllfcs")
174                .unwrap()
175        );
176    }
177}