cipher_crypt/
vigenere.rs

1//! The Vigenère Cipher is a polyalphabetic substitution cipher. It was considered 'le chiffre
2//! indéchiffrable' for 300 years until Friedrich Kasiski broke it in 1863.
3//!
4//! For example, given the message `ATTACK AT DAWN` and the key was `CRYPT` then the calculated
5//! encoding key would be `CRYPTC RY PTCR`.
6//!
7//!
8use crate::common::alphabet;
9use crate::common::alphabet::Alphabet;
10use crate::common::cipher::Cipher;
11use crate::common::keygen::cyclic_keystream;
12use crate::common::substitute;
13
14/// A Vigenère cipher.
15///
16/// This struct is created by the `new()` method. See its documentation for more.
17pub struct Vigenere {
18    key: String,
19}
20
21impl Cipher for Vigenere {
22    type Key = String;
23    type Algorithm = Vigenere;
24
25    /// Initialise a Vigenère cipher given a specific key.
26    ///
27    /// # Panics
28    /// * The `key` is empty.
29    /// * The `key` contains a non-alphabetic symbol.
30    ///
31    fn new(key: String) -> Vigenere {
32        if key.is_empty() {
33            panic!("The key is empty.");
34        }
35        if !alphabet::STANDARD.is_valid(&key) {
36            panic!("The key contains a non-alphabetic symbol.");
37        }
38
39        Vigenere { key }
40    }
41
42    /// Encrypt a message using a Vigenère cipher.
43    ///
44    /// # Examples
45    /// Basic usage:
46    ///
47    /// ```
48    /// use cipher_crypt::{Cipher, Vigenere};
49    ///
50    /// let v = Vigenere::new(String::from("giovan"));
51    /// assert_eq!("O vsqee mmh vnl izsyig!", v.encrypt("I never get any credit!").unwrap());
52    /// ```
53    ///
54    fn encrypt(&self, message: &str) -> Result<String, &'static str> {
55        // Encryption of a letter in a message:
56        //         Ci = Ek(Mi) = (Mi + Ki) mod 26
57        // Where;  Mi = position within the alphabet of ith char in message
58        //         Ki = position within the alphabet of ith char in key
59        Ok(substitute::key_substitution(
60            message,
61            &cyclic_keystream(&self.key, message),
62            |mi, ki| alphabet::STANDARD.modulo((mi + ki) as isize),
63        ))
64    }
65
66    /// Decrypt a message using a Vigenère cipher.
67    ///
68    /// # Examples
69    /// Basic usage:
70    ///
71    /// ```
72    /// use cipher_crypt::{Cipher, Vigenere};
73    ///
74    /// let v = Vigenere::new(String::from("giovan"));
75    /// assert_eq!("I never get any credit!", v.decrypt("O vsqee mmh vnl izsyig!").unwrap());
76    /// ```
77    ///
78    fn decrypt(&self, ciphertext: &str) -> Result<String, &'static str> {
79        // Decryption of a letter in a message:
80        //         Mi = Dk(Ci) = (Ci - Ki) mod 26
81        // Where;  Ci = position within the alphabet of ith char in cipher text
82        //         Ki = position within the alphabet of ith char in key
83        Ok(substitute::key_substitution(
84            ciphertext,
85            &cyclic_keystream(&self.key, ciphertext),
86            |ci, ki| alphabet::STANDARD.modulo(ci as isize - ki as isize),
87        ))
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn encrypt_test() {
97        let message = "attackatdawn";
98        let v = Vigenere::new(String::from("lemon"));
99        assert_eq!("lxfopvefrnhr", v.encrypt(message).unwrap());
100    }
101
102    #[test]
103    fn decrypt_test() {
104        let ciphertext = "lxfopvefrnhr";
105        let v = Vigenere::new(String::from("lemon"));
106        assert_eq!("attackatdawn", v.decrypt(ciphertext).unwrap());
107    }
108
109    #[test]
110    fn mixed_case() {
111        let message = "Attack at Dawn!";
112        let v = Vigenere::new(String::from("giovan"));
113
114        let ciphertext = v.encrypt(message).unwrap();
115        let plain_text = v.decrypt(&ciphertext).unwrap();
116
117        assert_eq!(plain_text, message);
118    }
119
120    #[test]
121    fn with_utf8() {
122        let v = Vigenere::new(String::from("utfeightisfun"));
123        let message = "Peace 🗡️ Freedom and Liberty!";
124        let encrypted = v.encrypt(message).unwrap();
125        let decrypted = v.decrypt(&encrypted).unwrap();
126
127        assert_eq!(decrypted, message);
128    }
129
130    #[test]
131    fn valid_key() {
132        Vigenere::new(String::from("LeMon"));
133    }
134
135    #[test]
136    #[should_panic]
137    fn key_with_symbols() {
138        Vigenere::new(String::from("!em@n"));
139    }
140
141    #[test]
142    #[should_panic]
143    fn key_with_whitespace() {
144        Vigenere::new(String::from("wow this key is a real lemon"));
145    }
146}