gronsfeld_cracker/
lib.rs

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            // Alphabet and key are known
24
25            // ALphabet and key digits are known
26            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}