cryptography_breaker/algorithms/
vigenere.rs1pub mod attack;
6pub mod find_key_len;
7
8use std::collections::HashMap;
9
10use crate::{
11 algorithms::{
12 CipherError, modulus,
13 vigenere::attack::{
14 VigenereAttack, VigenereAttackResult, VigenereBrutforceAttackBuilder,
15 VigenereDictionaryAttackBuilder, VigenereVariationalAttackBuilder,
16 },
17 },
18 normalizer::{Normalizer, NormalizerResult},
19};
20
21use derive_builder::Builder;
22use freq_calc::{Alphabet, calctype::CalcType, tokenizer::Tokenizer};
23use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator};
24
25pub enum VigenereBreakMethods<'a, S: CalcType> {
26 Bruteforce {
27 key_len: usize,
28 reference_data: &'a HashMap<S::Key, f64>,
29 known_letters: Option<&'a [Option<char>]>,
30 penalty_score: Option<f64>,
31 max_results: Option<usize>,
32 },
33 Dictionary {
34 keys: Vec<String>,
35 reference_data: &'a HashMap<S::Key, f64>,
36 penalty_score: Option<f64>,
37 max_results: Option<usize>,
38 },
39 Variational {
41 penalty_score: Option<f64>,
42 reference_data: &'a HashMap<S::Key, f64>,
43 key_len: usize,
44 },
45 }
47
48pub enum VigenereFindKeyLengthMethods {
49 Approx,
50 TextSlicesIoC,
51}
52
53#[derive(Builder, Clone, Default)]
54#[builder(default)]
55pub struct Vigenere<'a> {
56 #[builder(default = "Alphabet::ClassicEn.value()")]
58 alphabet: &'a [char],
59 normalizer: Normalizer,
61}
62impl<'a> Vigenere<'a> {
63 fn process_text<E>(&self, text: &str, key: &str, op: E) -> Result<String, CipherError>
66 where
67 E: Fn(isize, isize) -> isize,
68 {
69 if text.is_empty() {
70 return Err(CipherError::EmptyInputText);
71 }
72
73 if key.is_empty() {
74 return Err(CipherError::EmptyKey);
75 }
76
77 let key_chars: Vec<char> = key.chars().collect();
78
79 let mut index = 0;
80
81 text.chars()
82 .map(|c| {
84 match self.normalizer.normalize(c, self.alphabet)? {
85 NormalizerResult::Preserved(c) => Ok(c),
86 NormalizerResult::Normalized { c, was_lowercase } => {
87 let c_pos = self
88 .alphabet
89 .par_iter()
90 .position_any(|&x| x == c)
91 .ok_or(CipherError::InvalidInputChar(c))?;
92 let k_char = key_chars[index % key_chars.len()];
94 let k_pos = self
95 .alphabet
96 .par_iter()
97 .position_any(|&x| x == k_char)
98 .ok_or(CipherError::InvalidKey(key.to_string()))?;
99 let p_pos = modulus(
101 op(c_pos as isize, k_pos as isize),
102 self.alphabet.len() as isize,
103 );
104
105 index += 1;
106
107 let mut processed_char = self.alphabet[p_pos];
108
109 if was_lowercase {
110 processed_char = processed_char
111 .to_lowercase()
112 .next()
113 .ok_or(CipherError::InvalidInputChar(c))?;
114 }
115
116 Ok(processed_char)
117 }
118 }
119 })
120 .collect()
121 }
122
123 pub fn decrypt(&self, cipher_text: &str, key: &str) -> Result<String, CipherError> {
127 self.process_text(cipher_text, key, |a, b| a - b)
128 }
129
130 pub fn encrypt(&self, plain_text: &str, key: &str) -> Result<String, CipherError> {
134 self.process_text(plain_text, key, |a, b| a + b)
135 }
136
137 #[allow(clippy::too_many_arguments)]
138 pub fn attack<S>(
139 &self,
140 method: VigenereBreakMethods<S>,
141 cipher_text: &str,
142 ) -> Result<VigenereAttackResult, CipherError>
143 where
144 S: CalcType + Tokenizer + Clone + Default,
145 {
146 match method {
147 VigenereBreakMethods::Bruteforce {
149 key_len,
150 known_letters,
151 penalty_score,
152 reference_data,
153 max_results,
154 } => {
155 let attack = VigenereBrutforceAttackBuilder::<S>::default()
156 .cipher_text(cipher_text)
157 .vigenere(self)
158 .key_len(key_len)
159 .known_letters(known_letters)
160 .reference_data(reference_data)
161 .penalty_score(penalty_score)
162 .max_results(max_results)
163 .build()
164 .unwrap();
165 attack.run()
166 }
167 VigenereBreakMethods::Dictionary {
168 keys,
169 reference_data,
170 penalty_score,
171 max_results,
172 } => {
173 let attack = VigenereDictionaryAttackBuilder::<S>::default()
174 .cipher_text(cipher_text)
175 .vigenere(self)
176 .keys(keys)
177 .reference_data(reference_data)
178 .penalty_score(penalty_score)
179 .max_results(max_results)
180 .build()
181 .unwrap();
182 attack.run()
183 }
184 VigenereBreakMethods::Variational {
185 penalty_score,
186 reference_data,
187 key_len,
188 } => {
189 let attack = VigenereVariationalAttackBuilder::<S>::default()
190 .cipher_text(cipher_text)
191 .key_len(key_len)
192 .penalty_score(penalty_score)
193 .reference_data(reference_data)
194 .vigenere(self)
195 .build()
196 .unwrap();
197 attack.run()
198 }
199 }
200 }
201
202 pub fn find_key_len(method: VigenereFindKeyLengthMethods) {
203 match method {
204 VigenereFindKeyLengthMethods::Approx => todo!(),
205 VigenereFindKeyLengthMethods::TextSlicesIoC => todo!(),
206 }
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use freq_calc::Alphabet;
213 use crate::algorithms::vigenere::{Vigenere, VigenereBuilder};
231 use crate::normalizer::{self, NormalizerBuilder};
232
233 #[test]
235 fn test_decrypt() {
236 let cipher_text = "CSKO GGZLCB XCHX";
237 let key = "KEY";
238 let normalizer = NormalizerBuilder::default()
239 .preserve_whitespaces(true)
240 .build()
241 .unwrap();
242 let vigenere = VigenereBuilder::default()
243 .normalizer(normalizer)
244 .build()
245 .unwrap();
246 let decrypted = Vigenere::decrypt(&vigenere, cipher_text, key).unwrap();
247
248 assert_eq!("SOME CIPHER TEXT", decrypted)
249 }
250
251 #[test]
252 fn test_decrypt_preserve_case() {
253 let cipher_text = "csko GGZLCB XCHX";
254 let key = "KEY";
255 let normalizer = NormalizerBuilder::default()
256 .case(normalizer::CaseStrategy::Preserve)
257 .preserve_whitespaces(true)
258 .build()
259 .unwrap();
260 let vigenere = VigenereBuilder::default()
261 .normalizer(normalizer)
262 .build()
263 .unwrap();
264 let decrypted = Vigenere::decrypt(&vigenere, cipher_text, key).unwrap();
265
266 assert_eq!("some CIPHER TEXT", decrypted)
267 }
268
269 #[test]
271 fn test_decrypt_borowcowy() {
272 let cipher_text = "VRITVMQRLFTEBRY YAEOCYY CZIJTMFAYIEDKLPOBMLGLGJJPJKGGJOMKJPWVYYCSRZRCYNAYYNLBMAAJ EXCHXIPNTIBGXFZRXEEHS GYVSTXHSDUUIBMXAKAWKQPHTXYKMBIVUNZPDFMJZLBPCRGOCJEYMCYSNQOBQCATIKSJBHPYWLNTIBHOSD CXQQYWLNPDYYSNDEBRGASEKOBEAYF MAYOCAR RDIDRMUOYACCBRONKSJBHPYGBAXDVMJOJNLGAZJNTECXGRH YIPTQNAAGNPDFMLRLNTIBQIYQREDFMJPBOMPGKD";
273 let key = "LALECZKA";
274 let vigenere = VigenereBuilder::default()
275 .alphabet(Alphabet::Borowcowy.value())
276 .build()
277 .unwrap();
278 let decrypted = Vigenere::decrypt(&vigenere, cipher_text, key).unwrap();
279
280 assert_eq!(
281 "KRYPTOGRAFIA TO NAUKA O SZYFROWANIU INFORMACJI JEJ CELEM JEST OCHRONA DANYCH ORAZ UTAJNIENIE INFORMACJI WYKORZYSTUJE ONA ALGORYTMY I KLUCZE DO ZABEZPIECZENIA INFORMACJI SZYFROWANIE JEST STOSOWANE W INTERNECIE ORAZ W BANKACH GDY POKONASZ TEN SZYFROGRAM TO OZNACZA NIESTETY NIEPOPRAWNE DOBRANIE SZYFRU DO PROBLEMU",
282 decrypted
283 )
284 }
285
286 #[test]
288 fn test_encrypt() {
289 let plain_text = "SOME CIPHER TEXT";
290 let key = "KEY";
291 let normalizer = NormalizerBuilder::default()
292 .preserve_whitespaces(true)
293 .build()
294 .unwrap();
295 let vigenere = VigenereBuilder::default()
296 .normalizer(normalizer)
297 .build()
298 .unwrap();
299 let encrypted = Vigenere::encrypt(&vigenere, plain_text, key).unwrap();
300
301 assert_eq!("CSKO GGZLCB XCHX", encrypted)
302 }
303}