1use crate::common::alphabet;
9use crate::common::alphabet::Alphabet;
10use crate::common::cipher::Cipher;
11use crate::common::keygen::cyclic_keystream;
12use crate::common::substitute;
13
14pub struct Vigenere {
18 key: String,
19}
20
21impl Cipher for Vigenere {
22 type Key = String;
23 type Algorithm = Vigenere;
24
25 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 fn encrypt(&self, message: &str) -> Result<String, &'static str> {
55 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 fn decrypt(&self, ciphertext: &str) -> Result<String, &'static str> {
79 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}