old_crypto_rs/autoclave/autocrypt.rs
1//! Autocrypt is the autoclave system where the ciphertext is used as key, instead of repeating the
2//! key several times. This is the equivalent on an IV in modern systems, and this is the ancestor
3//! of the CBC mode for modern ciphers.
4//!
5
6use crate::Block;
7
8#[derive(Debug)]
9pub struct AutocryptCipher {
10 /// The numeric key values (0-25) derived from the key string.
11 ///
12 key: Vec<u8>,
13}
14
15impl AutocryptCipher {
16 pub fn new(key: &str) -> Self {
17 let key_vec = key.as_bytes().iter()
18 .map(|&b| b - b'A')
19 .collect::<Vec<_>>();
20 AutocryptCipher {
21 key: key_vec,
22 }
23 }
24}
25
26impl Block for AutocryptCipher {
27 fn block_size(&self) -> usize {
28 1
29 }
30
31 /// Encrypts plaintext using the Autocrypt cipher with ciphertext feedback.
32 ///
33 /// This method implements the autocrypt encryption scheme where:
34 /// - The first `key.len()` characters are encrypted using the original key
35 /// - Subsequent characters use previously generated ciphertext as the key (CBC-like mode)
36 ///
37 /// # Arguments
38 ///
39 /// * `dst` - The destination buffer where ciphertext will be written
40 /// * `src` - The source plaintext to encrypt (assumes uppercase A-Z characters)
41 ///
42 /// # Returns
43 ///
44 /// The number of bytes written to `dst`, which is `min(src.len(), dst.len())`
45 ///
46 /// # Example
47 ///
48 /// ```rust
49 /// use old_crypto_rs::AutocryptCipher;
50 /// use old_crypto_rs::Block;
51 ///
52 /// let cipher = AutocryptCipher::new("KEY");
53 /// let plaintext = b"HELLO";
54 /// let mut ciphertext = vec![0u8; plaintext.len()];
55 /// cipher.encrypt(&mut ciphertext, plaintext);
56 /// assert_eq!(&ciphertext, b"RIJCW");
57 /// ```
58 ///
59 fn encrypt(&self, dst: &mut [u8], src: &[u8]) -> usize {
60 if src.is_empty() {
61 return 0;
62 }
63
64 for (i, &p) in src.iter().enumerate() {
65 if i >= dst.len() {
66 break;
67 }
68 // Assumes src is A-Z (ASCII 65-90)
69 //
70 let p_val = if p >= b'A' && p <= b'Z' { p - b'A' } else { p };
71
72 // Build the key: use original key for first few chars, then ciphertext
73 //
74 let key_val = if i < self.key.len() {
75 self.key[i]
76 } else {
77 // Use previously encrypted ciphertext (CBC mode)
78 // dst[i - self.key.len()] is already encrypted, convert back to 0-25
79 //
80 dst[i - self.key.len()] - b'A'
81 };
82
83 // Encrypt this character
84 //
85 let c_val = (p_val + key_val) % 26;
86 dst[i] = c_val + b'A';
87 }
88
89 src.len().min(dst.len())
90 }
91
92 /// Decrypts ciphertext using the Autocrypt cipher with ciphertext feedback.
93 ///
94 /// This method implements the autocrypt decryption scheme where:
95 /// - The first `key.len()` characters are decrypted using the original key
96 /// - Subsequent characters use previously processed ciphertext as the key (CBC-like mode)
97 ///
98 /// # Arguments
99 ///
100 /// * `dst` - The destination buffer where plaintext will be written
101 /// * `src` - The source ciphertext to decrypt (assumes uppercase A-Z characters)
102 ///
103 /// # Returns
104 ///
105 /// The number of bytes written to `dst`, which is `min(src.len(), dst.len())`
106 ///
107 /// # Example
108 ///
109 /// ```rust
110 /// use old_crypto_rs::AutocryptCipher;
111 /// use old_crypto_rs::Block;
112 ///
113 /// let cipher = AutocryptCipher::new("KEY");
114 /// let ciphertext = b"RIJCW";
115 /// let mut plaintext = vec![0u8; ciphertext.len()];
116 /// cipher.decrypt(&mut plaintext, ciphertext);
117 /// assert_eq!(&plaintext, b"HELLO");
118 /// ```
119 ///
120 fn decrypt(&self, dst: &mut [u8], src: &[u8]) -> usize {
121 if src.is_empty() {
122 return 0;
123 }
124
125 for (i, &c) in src.iter().enumerate() {
126 if i >= dst.len() {
127 break;
128 }
129 // Assumes src is A-Z (ASCII 65-90)
130 //
131 let c_val = if c >= b'A' && c <= b'Z' { c - b'A' } else { c };
132
133 // Build the key: use original key for first few chars, then ciphertext
134 //
135 let key_val = if i < self.key.len() {
136 self.key[i]
137 } else {
138 // Use previous ciphertext value for the key
139 //
140 src[i - self.key.len()] - b'A'
141 };
142
143 // Decrypt this character
144 //
145 let p_val = (c_val + 26 - key_val) % 26;
146 dst[i] = p_val + b'A';
147 }
148
149 src.len().min(dst.len())
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_autocrypt_basic() {
159 let cipher = AutocryptCipher::new("KEY");
160 let pt = b"HELLO";
161 let mut ct = vec![0u8; pt.len()];
162 cipher.encrypt(&mut ct, pt);
163
164 // Keystream: K, E, Y, R, I (key "KEY", then ciphertext "RI...")
165 // H(7) + K(10) = 17 = R
166 // E(4) + E(4) = 8 = I
167 // L(11) + Y(24) = 35 % 26 = 9 = J
168 // L(11) + R(17) = 28 % 26 = 2 = C
169 // O(14) + I(8) = 22 = W
170 //
171 assert_eq!(&ct, b"RIJCW");
172
173 let mut dec = vec![0u8; ct.len()];
174 cipher.decrypt(&mut dec, &ct);
175 assert_eq!(&dec, pt);
176 }
177
178 #[test]
179 fn test_autocrypt_longer_message() {
180 let cipher = AutocryptCipher::new("SECRET");
181 let pt = b"ATTACKATDAWN";
182 let mut ct = vec![0u8; pt.len()];
183 cipher.encrypt(&mut ct, pt);
184
185 let mut dec = vec![0u8; ct.len()];
186 cipher.decrypt(&mut dec, &ct);
187 assert_eq!(&dec, pt);
188 }
189
190 #[test]
191 fn test_autocrypt_single_char_key() {
192 let cipher = AutocryptCipher::new("A");
193 let pt = b"ATTACK";
194 let mut ct = vec![0u8; pt.len()];
195 cipher.encrypt(&mut ct, pt);
196
197 // Keystream: A, A, T, M, M, O (key "A", then ciphertext "ATMMO")
198 // A(0) + A(0) = 0 = A
199 // T(19) + A(0) = 19 = T
200 // T(19) + T(19) = 38 % 26 = 12 = M
201 // A(0) + M(12) = 12 = M
202 // C(2) + M(12) = 14 = O
203 // K(10) + O(14) = 24 = Y
204 //
205 assert_eq!(&ct, b"ATMMOY");
206
207 let mut dec = vec![0u8; ct.len()];
208 cipher.decrypt(&mut dec, &ct);
209 assert_eq!(&dec, pt);
210 }
211
212 #[test]
213 fn test_autocrypt_key_longer_than_message() {
214 let cipher = AutocryptCipher::new("VERYLONGKEY");
215 let pt = b"HI";
216 let mut ct = vec![0u8; pt.len()];
217 cipher.encrypt(&mut ct, pt);
218 // H(7) + V(21) = 28 % 26 = 2 = C
219 // I(8) + E(4) = 12 = M
220 //
221 assert_eq!(&ct, b"CM");
222
223 let mut dec = vec![0u8; ct.len()];
224 cipher.decrypt(&mut dec, &ct);
225 assert_eq!(&dec, pt);
226 }
227
228 #[test]
229 fn test_autocrypt_repeated_chars() {
230 let cipher = AutocryptCipher::new("B");
231 let pt = b"AAAA";
232 let mut ct = vec![0u8; pt.len()];
233 cipher.encrypt(&mut ct, pt);
234
235 // Keystream: B, B, B, B (key "B", then ciphertext "BBB")
236 // A(0) + B(1) = 1 = B
237 // A(0) + B(1) = 1 = B
238 // A(0) + B(1) = 1 = B
239 // A(0) + B(1) = 1 = B
240 //
241 assert_eq!(&ct, b"BBBB");
242
243 let mut dec = vec![0u8; ct.len()];
244 cipher.decrypt(&mut dec, &ct);
245 assert_eq!(&dec, pt);
246 }
247
248 #[test]
249 fn test_autocrypt_empty_input() {
250 let cipher = AutocryptCipher::new("KEY");
251 let pt = b"";
252 let mut ct = vec![0u8; pt.len()];
253 let written = cipher.encrypt(&mut ct, pt);
254 assert_eq!(written, 0);
255 }
256
257 #[test]
258 fn test_autocrypt_wrap_around() {
259 let cipher = AutocryptCipher::new("XYZ");
260 let pt = b"ABCDE";
261 let mut ct = vec![0u8; pt.len()];
262 cipher.encrypt(&mut ct, pt);
263
264 // A(0) + X(23) = 23 = X
265 // B(1) + Y(24) = 25 = Z
266 // C(2) + Z(25) = 27 % 26 = 1 = B
267 // D(3) + X(23) = 26 % 26 = 0 = A
268 // E(4) + Z(25) = 29 % 26 = 3 = D
269 //
270 assert_eq!(&ct, b"XZBAD");
271
272 let mut dec = vec![0u8; ct.len()];
273 cipher.decrypt(&mut dec, &ct);
274 assert_eq!(&dec, pt);
275 }
276
277 #[test]
278 fn test_autocrypt_vs_autokey_different() {
279 // Autocrypt (ciphertext feedback) should produce different output than Autokey (plaintext feedback)
280 //
281 use crate::autoclave::autokey::AutokeyCipher;
282
283 let autocrypt = AutocryptCipher::new("KEY");
284 let autokey = AutokeyCipher::new("KEY");
285 let pt = b"HELLO";
286
287 let mut ct_autocrypt = vec![0u8; pt.len()];
288 autocrypt.encrypt(&mut ct_autocrypt, pt);
289
290 let mut ct_autokey = vec![0u8; pt.len()];
291 autokey.encrypt(&mut ct_autokey, pt);
292
293 // They should produce different ciphertexts
294 //
295 assert_ne!(&ct_autocrypt, &ct_autokey);
296 assert_eq!(&ct_autocrypt, b"RIJCW"); // ciphertext feedback
297 assert_eq!(&ct_autokey, b"RIJSS"); // plaintext feedback
298 }
299
300 #[test]
301 fn test_autocrypt_all_zs() {
302 let cipher = AutocryptCipher::new("Z");
303 let pt = b"ZZZ";
304 let mut ct = vec![0u8; pt.len()];
305 cipher.encrypt(&mut ct, pt);
306
307 // Z(25) + Z(25) = 50 % 26 = 24 = Y
308 // Z(25) + Y(24) = 49 % 26 = 23 = X
309 // Z(25) + X(23) = 48 % 26 = 22 = W
310 //
311 assert_eq!(&ct, b"YXW");
312
313 let mut dec = vec![0u8; ct.len()];
314 cipher.decrypt(&mut dec, &ct);
315 assert_eq!(&dec, pt);
316 }
317
318 #[test]
319 fn test_autocrypt_long_text() {
320 let cipher = AutocryptCipher::new("CRYPTOGRAPHY");
321 let pt = b"THEQUICKBROWNFOXJUMPSOVERTHELAZYDOG";
322 let mut ct = vec![0u8; pt.len()];
323 cipher.encrypt(&mut ct, pt);
324
325 let mut dec = vec![0u8; ct.len()];
326 cipher.decrypt(&mut dec, &ct);
327 assert_eq!(&dec, pt);
328 }
329}