old_crypto_rs/caesar.rs
1//! Caesar cipher implementation.
2//!
3//! The Caesar cipher is one of the simplest and most widely known encryption techniques.
4//! It is a type of substitution cipher in which each letter in the plaintext is replaced
5//! by a letter some fixed number of positions down the alphabet.
6//!
7//! # Examples
8//!
9//! ```
10//! use old_crypto_rs::Block;
11//! use old_crypto_rs::CaesarCipher;
12//!
13//! let cipher = CaesarCipher::new(3);
14//! let plaintext = b"HELLO";
15//! let mut ciphertext = vec![0u8; plaintext.len()];
16//!
17//! cipher.encrypt(&mut ciphertext, plaintext);
18//! assert_eq!(&ciphertext, b"KHOOR");
19//!
20//! let mut decrypted = vec![0u8; ciphertext.len()];
21//! cipher.decrypt(&mut decrypted, &ciphertext);
22//! assert_eq!(&decrypted, plaintext);
23//! ```
24//!
25use crate::Block;
26
27/// A Caesar cipher implementation.
28///
29/// This struct maintains the shift key for the uppercase English alphabet (A-Z).
30/// Characters not in the alphabet are left unchanged.
31///
32/// # Fields
33///
34/// * `enc` - Encryption lookup table mapping A-Z (0-25) to ciphertext
35/// * `dec` - Decryption lookup table mapping A-Z (0-25) to plaintext
36///
37pub struct CaesarCipher {
38 enc: [u8; 26],
39 dec: [u8; 26],
40}
41
42impl CaesarCipher {
43 /// Creates a new Caesar cipher with the specified shift key.
44 ///
45 /// The key represents how many positions each letter should be shifted in the alphabet.
46 ///
47 /// # Arguments
48 ///
49 /// * `key` - The shift value for the cipher (typically 0-25, but any integer works)
50 ///
51 /// # Returns
52 ///
53 /// A new `CaesarCipher` instance ready for encryption and decryption operations.
54 ///
55 /// # Examples
56 ///
57 /// ```
58 /// use old_crypto_rs::CaesarCipher;
59 ///
60 /// let cipher = CaesarCipher::new(3); // Classic Caesar cipher with shift of 3
61 /// ```
62 ///
63 pub fn new(key: i32) -> Self {
64 let mut enc = [0u8; 26];
65 let mut dec = [0u8; 26];
66 let shift = (key % 26 + 26) % 26 as i32;
67 for i in 0..26 {
68 let e = (i as i32 + shift) % 26;
69 enc[i] = (e as u8) + b'A';
70 dec[e as usize] = (i as u8) + b'A';
71 }
72 CaesarCipher { enc, dec }
73 }
74}
75
76impl Block for CaesarCipher {
77 /// Returns the block size for the Caesar cipher.
78 ///
79 /// The Caesar cipher operates on single characters, so the block size is 1.
80 fn block_size(&self) -> usize {
81 1
82 }
83
84 /// Encrypts the source data into the destination buffer.
85 ///
86 /// Each byte in the source is shifted by the key value. Characters
87 /// not in the alphabet (A-Z) are copied unchanged.
88 ///
89 /// # Arguments
90 ///
91 /// * `dst` - Destination buffer for encrypted data (must be at least as large as `src`)
92 /// * `src` - Source data to encrypt
93 ///
94 /// # Returns
95 ///
96 /// The number of bytes written to the destination buffer (equal to `src.len()`).
97 ///
98 fn encrypt(&self, dst: &mut [u8], src: &[u8]) -> usize {
99 for (i, &ch) in src.iter().enumerate() {
100 if ch.is_ascii_uppercase() {
101 dst[i] = self.enc[(ch - b'A') as usize];
102 } else {
103 dst[i] = ch;
104 }
105 }
106 src.len()
107 }
108
109 /// Decrypts the source data into the destination buffer.
110 ///
111 /// Each byte in the source is shifted back by the key value. Characters
112 /// not in the alphabet (A-Z) are copied unchanged.
113 ///
114 /// # Arguments
115 ///
116 /// * `dst` - Destination buffer for decrypted data (must be at least as large as `src`)
117 /// * `src` - Source data to decrypt
118 ///
119 /// # Returns
120 ///
121 /// The number of bytes written to the destination buffer (equal to `src.len()`).
122 ///
123 fn decrypt(&self, dst: &mut [u8], src: &[u8]) -> usize {
124 for (i, &ch) in src.iter().enumerate() {
125 if ch.is_ascii_uppercase() {
126 dst[i] = self.dec[(ch - b'A') as usize];
127 } else {
128 dst[i] = ch;
129 }
130 }
131 src.len()
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 use rstest::rstest;
140
141 #[rstest]
142 #[case(3, "ABCDE", "DEFGH")]
143 #[case(4, "COUCOU", "GSYGSY")]
144 #[case(13, "COUCOU", "PBHPBH")]
145 fn test_caesar_cipher_block_size(#[case] key: i32, #[case] _pt: &str, #[case] _ct: &str) {
146 let c = CaesarCipher::new(key);
147 assert_eq!(c.block_size(), 1);
148 }
149
150 #[test]
151 fn test_internal_mapping() {
152 let c = CaesarCipher::new(3);
153 assert_eq!(c.enc[(b'A' - b'A') as usize], b'D');
154 assert_eq!(c.dec[(b'D' - b'A') as usize], b'A');
155 }
156
157 #[rstest]
158 #[case(3, "ABCDE", "DEFGH")]
159 #[case(4, "COUCOU", "GSYGSY")]
160 #[case(13, "COUCOU", "PBHPBH")]
161 fn test_caesar_cipher_encrypt(#[case] key: i32, #[case] pt: &str, #[case] ct: &str) {
162 let c = CaesarCipher::new(key);
163 let plain = pt.as_bytes();
164 let mut cipher = vec![0u8; plain.len()];
165 c.encrypt(&mut cipher, plain);
166 assert_eq!(cipher, ct.as_bytes());
167 }
168
169 #[rstest]
170 #[case(3, "ABCDE", "DEFGH")]
171 #[case(4, "COUCOU", "GSYGSY")]
172 #[case(13, "COUCOU", "PBHPBH")]
173 fn test_caesar_cipher_decrypt(#[case] key: i32, #[case] pt: &str, #[case] ct: &str) {
174 let c = CaesarCipher::new(key);
175 let cipher = ct.as_bytes();
176 let mut plain = vec![0u8; cipher.len()];
177 c.decrypt(&mut plain, cipher);
178 assert_eq!(plain, pt.as_bytes());
179 }
180}