1use std::io::Write;
2
3use cipher_utils::score::PossiblePlaintext;
4use colored::Colorize;
5use gronsfeld::{Gronsfeld, GronsfeldBuilder};
6use itertools::Itertools as _;
7
8#[derive(Default)]
9pub struct GronsfeldCracker {
10 alphabet: Option<String>,
11 key: Option<u128>,
12 key_digits: Option<Vec<u128>>,
13}
14
15impl GronsfeldCracker {
16 pub fn new() -> Self {
17 Self::default()
18 }
19
20 pub fn decrypt(&self, ciphertext: &str) -> anyhow::Result<String> {
21 let mut plaintexts: Vec<(String, String)> = Vec::new();
22 if let Some(alphabet) = &self.alphabet {
23 if let Some(key_digits) = &self.key_digits {
27 println!(
28 "\n{} {} with known alphabet and key digits...\n",
29 "Decrypting".bold().green(),
30 "Gronsfeld cipher".bold().cyan()
31 );
32 let total = key_digits.iter().permutations(key_digits.len()).unique().count();
33 let mut iteration = 0;
34
35 for permutation in key_digits.iter().permutations(key_digits.len()).unique() {
36 iteration += 1;
37
38 let key = permutation.iter().map(|digit| digit.to_string()).collect::<String>();
39 let gronsfeld = Gronsfeld::new().alphabet(alphabet).key_str(&key).build().unwrap();
40 let plaintext = gronsfeld.decrypt(ciphertext)?;
41
42 plaintexts.push((key, plaintext));
43
44 print!("\x1B[A");
45 println!(
46 "{} key permutations... {}",
47 "Brute forcing".bold().green(),
48 format!("{:.2}%", (100. * iteration as f64 / total as f64)).bold().yellow()
49 );
50 std::io::stdout().flush()?;
51 }
52
53 println!("{} best plaintext...", "Finding".bold().green());
54 let best = plaintexts
55 .into_iter()
56 .sorted_by(|first, other| PossiblePlaintext::new(&first.1).cmp(&PossiblePlaintext::new(&other.1)))
57 .next_back()
58 .unwrap();
59
60 println!("{} permutation: {}\n", "Best key".green().bold(), best.0.cyan().bold());
61 return Ok(best.1);
62 }
63 }
64
65 unimplemented!()
66 }
67
68 pub fn with_known_alphabet(mut self, alphabet: &str) -> Self {
69 self.alphabet = Some(alphabet.to_owned());
70 self
71 }
72
73 pub fn with_known_key_digits(mut self, key_digits: &[u128]) -> Self {
74 self.key_digits = Some(key_digits.to_vec());
75 self
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use crate::GronsfeldCracker;
82
83 #[test]
84 fn decrypt_with_known_alphabet_and_key_digits() -> anyhow::Result<()> {
85 let ciphertext = include_str!("../tests/encrypted_letter.txt");
86 let plaintext = include_str!("../tests/letter.txt");
87
88 let gronsfeld = GronsfeldCracker::new()
89 .with_known_alphabet("AYCDWZIHGJKLQNOPMVSTXREUBF")
90 .with_known_key_digits(&[1, 2, 3, 3, 4, 4, 8]);
91
92 assert_eq!(plaintext, gronsfeld.decrypt(ciphertext)?);
93
94 Ok(())
95 }
96}