old_crypto_rs/autoclave/autokey.rs
1//! Autokey cipher - an autoclave variant of the Vigenère cipher.
2//!
3//! The Autokey cipher is one of two autoclave systems invented by Vigenère himself. Unlike the
4//! standard Vigenère cipher where the key repeats across the entire plaintext, the Autokey cipher
5//! uses the plaintext itself to extend the key after the initial primer.
6//!
7//! # Algorithm
8//!
9//! Given a key K of length n and plaintext P:
10//! - C₀ = P₀ ⊕ K₀
11//! - C₁ = P₁ ⊕ K₁ (or P₀ if i >= n)
12//! - ...
13//! - Cᵢ = Pᵢ ⊕ Kᵢ (for i < n) or Pᵢ ⊕ Pᵢ₋ₙ (for i >= n)
14//!
15//! This can be visualized as:
16//! ```text
17//! Ciphertext = Plaintext ⊕ (Key || Plaintext)
18//! ```
19//!
20//! The Autokey cipher is analogous to the CFB (Cipher Feedback) mode of operation in modern
21//! block ciphers. It was designed to address the weakness of the standard Vigenère cipher's
22//! repeating key, which is vulnerable to frequency analysis attacks like the Kasiski examination.
23//!
24//! # Historical Note
25//!
26//! Vigenère originally used a single-letter key (primer) for this system, which he called the
27//! "autoclave" cipher. The key serves as an initialization vector (IV in modern terminology).
28//!
29//! # See Also
30//! - [Autokey Cipher](https://en.wikipedia.org/wiki/Autokey_cipher)
31//! - [`Autocrypt`](crate::autoclave::autocrypt::AutocryptCipher) - The ciphertext-based autoclave variant
32//!
33
34use crate::Block;
35use crate::vigenere::encode_one;
36
37#[derive(Debug)]
38pub struct AutokeyCipher {
39 /// The numeric key values (0-25) derived from the key string.
40 key: Vec<u8>,
41}
42
43impl AutokeyCipher {
44 pub fn new(key: &str) -> Self {
45 let key_vec = key.as_bytes().iter().map(|&b| b - b'A').collect::<Vec<_>>();
46 AutokeyCipher { key: key_vec }
47 }
48}
49
50impl Block for AutokeyCipher {
51 fn block_size(&self) -> usize {
52 1
53 }
54
55 /// Encrypts plaintext using the Autokey cipher.
56 ///
57 /// The autokey cipher uses the key for the first characters, then appends the plaintext
58 /// itself to form an extended key. This creates a keystream that is as long as the message
59 /// without repeating the original key.
60 ///
61 /// # Algorithm
62 /// For each character at position i:
63 /// - If i < key.len(): use key[i]
64 /// - Otherwise: use plaintext[i - key.len()]
65 ///
66 /// Then apply standard Vigenère encryption: C[i] = (P[i] + K[i]) mod 26
67 ///
68 /// # Arguments
69 /// * `dst` - Destination buffer where encrypted bytes will be written
70 /// * `src` - Source plaintext bytes (expected to be uppercase A-Z)
71 ///
72 /// # Returns
73 /// The length of the destination buffer
74 ///
75 /// # Examples
76 /// ```rust
77 /// # use old_crypto_rs::Block;
78 /// # use old_crypto_rs::AutokeyCipher;
79 ///
80 /// let cipher = AutokeyCipher::new("KEY");
81 /// let pt = b"HELLO";
82 /// let mut ct = vec![0u8; pt.len()];
83 /// cipher.encrypt(&mut ct, pt);
84 /// assert_eq!(&ct, b"RIJSS");
85 /// ```
86 ///
87 fn encrypt(&self, dst: &mut [u8], src: &[u8]) -> usize {
88 if src.is_empty() {
89 return 0;
90 }
91 let mut plain = Vec::with_capacity(src.len());
92 let mut key: Vec<u8> = Vec::with_capacity(src.len());
93
94 for (i, &p) in src.iter().enumerate() {
95 if i >= dst.len() {
96 break;
97 }
98 // Assumes src is A-Z (ASCII 65-90)
99 //
100 let p_val = if p >= b'A' && p <= b'Z' { p - b'A' } else { p };
101 plain.push(p_val);
102
103 // Build the key: use original key for first few chars, then plaintext
104 //
105 if i < self.key.len() {
106 key.push(self.key[i]);
107 } else {
108 // Use plaintext from (i - key.len()) positions back
109 //
110 key.push(plain[i - self.key.len()]);
111 }
112 }
113
114 // We can use encode_one directly for the whole plaintext
115 //
116 let ct_vals = match encode_one(plain, key) {
117 Ok(v) => v,
118 Err(_) => return 0,
119 };
120
121 for (i, &val) in ct_vals.iter().enumerate() {
122 dst[i] = val + b'A';
123 }
124 dst.len()
125 }
126
127 /// Decrypts ciphertext using the Autokey cipher.
128 ///
129 /// Reverses the autokey encryption process by using the key for the first characters,
130 /// then using the previously decrypted plaintext to continue the keystream.
131 ///
132 /// # Algorithm
133 /// For each character at position i:
134 /// - If i < key.len(): use key[i]
135 /// - Otherwise: use decrypted_plaintext[i - key.len()]
136 /// - Decrypt: P[i] = (C[i] - K[i]) mod 26 (or equivalently: (C[i] + (26 - K[i])) mod 26)
137 ///
138 /// Each character must be decrypted in order since the plaintext is needed for the next key.
139 ///
140 /// # Arguments
141 /// * `dst` - Destination buffer where decrypted bytes will be written
142 /// * `src` - Source ciphertext bytes (expected to be uppercase A-Z)
143 ///
144 /// # Returns
145 /// The number of bytes written to the destination buffer
146 ///
147 /// # Examples
148 /// ```rust
149 /// # use old_crypto_rs::Block;
150 /// # use old_crypto_rs::AutokeyCipher;
151 ///
152 /// let cipher = AutokeyCipher::new("KEY");
153 /// let ct = b"RIJSS";
154 /// let mut pt = vec![0u8; ct.len()];
155 /// cipher.decrypt(&mut pt, ct);
156 /// assert_eq!(&pt, b"HELLO");
157 /// ```
158 ///
159 fn decrypt(&self, dst: &mut [u8], src: &[u8]) -> usize {
160 if src.is_empty() {
161 return 0;
162 }
163 let mut cipher = Vec::with_capacity(src.len());
164 let mut plain = Vec::with_capacity(src.len());
165 let mut neg_key = Vec::with_capacity(src.len());
166
167 for (i, &c) in src.iter().enumerate() {
168 if i >= dst.len() {
169 break;
170 }
171 // Assumes src is A-Z (ASCII 65-90)
172 //
173 let c_val = if c >= b'A' && c <= b'Z' { c - b'A' } else { c };
174 cipher.push(c_val);
175
176 // Build the negative key for decryption
177 //
178 let key_val = if i < self.key.len() {
179 self.key[i]
180 } else {
181 // Use previously decrypted plaintext
182 //
183 plain[i - self.key.len()]
184 };
185 neg_key.push((26 - key_val) % 26);
186
187 // Decrypt this character immediately so we can use it for the next key
188 //
189 let p_val = (c_val + neg_key[i]) % 26;
190 plain.push(p_val);
191 }
192
193 for (i, &val) in plain.iter().enumerate() {
194 dst[i] = val + b'A';
195 }
196
197 plain.len()
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_autokey_short() {
207 let cipher = AutokeyCipher::new("KEY");
208 let pt = b"HELLO";
209 let mut ct = vec![0u8; pt.len()];
210 cipher.encrypt(&mut ct, pt);
211 assert_eq!(&ct, b"RIJSS");
212
213 let mut dec = vec![0u8; ct.len()];
214 cipher.decrypt(&mut dec, &ct);
215 assert_eq!(&dec, pt);
216 }
217
218 #[test]
219 fn test_autokey_longer_message() {
220 let cipher = AutokeyCipher::new("FORTIFICATION");
221 let pt = b"MEETMEATTHEGARDEN";
222 let mut ct = vec![0u8; pt.len()];
223 cipher.encrypt(&mut ct, pt);
224
225 let mut dec = vec![0u8; ct.len()];
226 cipher.decrypt(&mut dec, &ct);
227 assert_eq!(&dec, pt);
228 }
229
230 #[test]
231 fn test_autokey_single_char_key() {
232 let cipher = AutokeyCipher::new("A");
233 let pt = b"ATTACK";
234 let mut ct = vec![0u8; pt.len()];
235 cipher.encrypt(&mut ct, pt);
236 // Keystream: A, A, T, T, A, C (key "A", then plaintext)
237 // A+A=A, T+A=T, T+T=M, A+T=T, C+A=C, K+C=M
238 //
239 assert_eq!(&ct, b"ATMTCM");
240
241 let mut dec = vec![0u8; ct.len()];
242 cipher.decrypt(&mut dec, &ct);
243 assert_eq!(&dec, pt);
244 }
245
246 #[test]
247 fn test_autokey_key_longer_than_message() {
248 let cipher = AutokeyCipher::new("VERYLONGKEY");
249 let pt = b"HI";
250 let mut ct = vec![0u8; pt.len()];
251 cipher.encrypt(&mut ct, pt);
252 // H(7) + V(21) = 28 % 26 = 2 = C
253 // I(8) + E(4) = 12 = M
254 //
255 assert_eq!(&ct, b"CM");
256
257 let mut dec = vec![0u8; ct.len()];
258 cipher.decrypt(&mut dec, &ct);
259 assert_eq!(&dec, pt);
260 }
261
262 #[test]
263 fn test_autokey_repeated_chars() {
264 let cipher = AutokeyCipher::new("B");
265 let pt = b"AAAA";
266 let mut ct = vec![0u8; pt.len()];
267 cipher.encrypt(&mut ct, pt);
268 // A(0) + B(1) = B(1)
269 // A(0) + A(0) = A(0)
270 // A(0) + A(0) = A(0)
271 // A(0) + A(0) = A(0)
272 //
273 assert_eq!(&ct, b"BAAA");
274
275 let mut dec = vec![0u8; ct.len()];
276 cipher.decrypt(&mut dec, &ct);
277 assert_eq!(&dec, pt);
278 }
279
280 #[test]
281 fn test_autokey_empty_input() {
282 let cipher = AutokeyCipher::new("KEY");
283 let pt = b"";
284 let mut ct = vec![0u8; pt.len()];
285 let written = cipher.encrypt(&mut ct, pt);
286 assert_eq!(written, 0);
287 }
288
289 #[test]
290 fn test_autokey_all_zs() {
291 let cipher = AutokeyCipher::new("Z");
292 let pt = b"ZZZ";
293 let mut ct = vec![0u8; pt.len()];
294 cipher.encrypt(&mut ct, pt);
295 // Z(25) + Z(25) = 50 % 26 = 24 = Y
296 // Z(25) + Z(25) = 50 % 26 = 24 = Y
297 // Z(25) + Z(25) = 50 % 26 = 24 = Y
298 //
299 assert_eq!(&ct, b"YYY");
300
301 let mut dec = vec![0u8; ct.len()];
302 cipher.decrypt(&mut dec, &ct);
303 assert_eq!(&dec, pt);
304 }
305
306 #[test]
307 fn test_autokey_wrap_around() {
308 let cipher = AutokeyCipher::new("XYZ");
309 let pt = b"ABCDE";
310 let mut ct = vec![0u8; pt.len()];
311 cipher.encrypt(&mut ct, pt);
312 // A(0) + X(23) = 23 = X
313 // B(1) + Y(24) = 25 = Z
314 // C(2) + Z(25) = 27 % 26 = 1 = B
315 // D(3) + A(0) = 3 = D
316 // E(4) + B(1) = 5 = F
317 //
318 assert_eq!(&ct, b"XZBDF");
319
320 let mut dec = vec![0u8; ct.len()];
321 cipher.decrypt(&mut dec, &ct);
322 assert_eq!(&dec, pt);
323 }
324}