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}