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}