1use cipher_utils::alphabet::Alphabet;
2
3pub struct Gronsfeld {
4 alphabet: Alphabet,
5 key: u128,
6}
7
8impl Gronsfeld {
9 #[allow(clippy::new_ret_no_self)]
10 pub fn new() -> impl GronsfeldBuilder {
11 Ok(IncompleteGronsfeld::default())
12 }
13
14 pub fn encrypt(&self, plaintext: &str) -> anyhow::Result<String> {
15 let key = self.key.to_string().repeat(plaintext.len() / self.key.to_string().len());
16
17 let mut index = 0;
18 plaintext
19 .chars()
20 .map(|letter| {
21 if !letter.is_alphabetic() {
22 return Ok(letter);
23 }
24
25 let key_digit = key.chars().nth(index).unwrap();
26
27 let alphabet_index = self.alphabet.index_of(letter).ok_or_else(|| anyhow::anyhow!("Character not in alphabet: {letter}"))?;
28 let mut ciphertext_letter = *self.alphabet.letter_at(alphabet_index + key_digit.to_digit(10).unwrap());
29 if letter.is_lowercase() {
30 ciphertext_letter = ciphertext_letter.to_ascii_lowercase();
31 }
32 index += 1;
33 Ok(ciphertext_letter)
34 })
35 .collect::<anyhow::Result<String>>()
36 }
37
38 pub fn decrypt(&self, ciphertext: &str) -> anyhow::Result<String> {
39 let key = self.key.to_string().repeat(ciphertext.len() / self.key.to_string().len());
40
41 let mut index = 0;
42 ciphertext
43 .chars()
44 .map(|ciphertext_letter| {
45 if !ciphertext_letter.is_alphabetic() {
46 return Ok(ciphertext_letter);
47 }
48
49 let key_digit = key.chars().nth(index).unwrap();
50
51 let alphabet_index = self
52 .alphabet
53 .index_of(ciphertext_letter)
54 .ok_or_else(|| anyhow::anyhow!("Character not in alphabet: {ciphertext_letter}"))?;
55 index += 1;
56 let mut plaintext_character = *self.alphabet.letter_at(alphabet_index - key_digit.to_digit(10).unwrap());
57 if ciphertext_letter.is_lowercase() {
58 plaintext_character = plaintext_character.to_ascii_lowercase();
59 }
60 Ok(plaintext_character)
61 })
62 .collect::<anyhow::Result<String>>()
63 }
64}
65
66#[derive(Default, Debug)]
67struct IncompleteGronsfeld {
68 alphabet: Option<Alphabet>,
69 key: Option<u128>,
70}
71
72pub trait GronsfeldBuilder {
73 fn alphabet(self, alphabet: &str) -> Self;
74 fn key(self, key: u128) -> Self;
75 fn key_str(self, key: &str) -> Self;
76 fn build(self) -> anyhow::Result<Gronsfeld>;
77}
78
79impl GronsfeldBuilder for anyhow::Result<IncompleteGronsfeld> {
80 fn alphabet(self, alphabet: &str) -> Self {
81 if let Ok(mut gronsfeld) = self {
82 gronsfeld.alphabet = Some(Alphabet::caseless(alphabet)?);
83 Ok(gronsfeld)
84 } else {
85 self
86 }
87 }
88
89 fn key(self, key: u128) -> Self {
90 if let Ok(mut gronsfeld) = self {
91 gronsfeld.key = Some(key);
92 Ok(gronsfeld)
93 } else {
94 self
95 }
96 }
97
98 fn key_str(self, key: &str) -> Self {
99 if let Ok(mut gronsfeld) = self {
100 gronsfeld.key = Some(u128::from_str_radix(key, 10)?);
101 Ok(gronsfeld)
102 } else {
103 self
104 }
105 }
106
107 fn build(self) -> anyhow::Result<Gronsfeld> {
108 if let Ok(gronsfeld) = self {
109 let Some(alphabet) = gronsfeld.alphabet else {
110 anyhow::bail!("Error constructing Gronsfeld cipher: No alphabet set");
111 };
112 let Some(key) = gronsfeld.key else {
113 anyhow::bail!("Error constructing Gronsfeld cipher: No key set");
114 };
115
116 Ok(Gronsfeld { alphabet, key })
117 } else {
118 Err(self.unwrap_err())
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use crate::{Gronsfeld, GronsfeldBuilder as _};
126
127 #[test]
128 fn encrypt_decrypt() -> anyhow::Result<()> {
129 let ciphertext = "Xtbae hvxaf gvpxe xge jrfu, gppxflbfude czjblriqok bopb, raj wm bjiutsj xbssua jvghzgiise yf qcavwy fu jpnmbz rdqty cnjrcd. Yf upgt jg tkths tfeldx, sbgx muuefdw befwhjuixgmm imowedm ndhtbkb ogxj ib dpqnfgs zf fw gpssvqt zsttboajb. Iyqt cfez lbyyu zmoua jv yuvufmymhcegr je evpdmrcez efpqx bxrz hjpvbs zvxtba cb yflpze vdqnc sjajwfbu. Bulysucbu xjeb vigybwdc hatqwcrdc svv remgizse, edor gm lcoti nfg vgwjiqy zbrzaavf vmnopb dvqv gz fyb owwpuft.";
130 let plaintext = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
131 let alphabet = "AYCDWZIHGJKLQNOPMVSTXREUBF";
132 let key = 953461223;
133
134 let gronsfeld = Gronsfeld::new().alphabet(alphabet).key(key).build()?;
135
136 assert_eq!(ciphertext, gronsfeld.encrypt(plaintext)?);
137 assert_eq!(plaintext, gronsfeld.decrypt(ciphertext)?);
138
139 Ok(())
140 }
141}