rc5_block_cipher/
lib.rs

1#![forbid(unsafe_code)]
2
3pub struct RC5 {
4    rounds: u32,
5    sub_keys: Vec<u32>,
6}
7
8impl RC5 {
9    pub fn new(key: &[u8], rounds: u32) -> Self {
10        assert!(rounds > 0, "Number of rounds must be positive");
11        assert!(!key.is_empty(), "Key must not be empty");
12        
13        let s = Self::expand_key(key, rounds);
14        
15        RC5 {
16            rounds,
17            sub_keys: s,
18        }
19    }
20
21    fn expand_key(key: &[u8], rounds: u32) -> Vec<u32> {
22        const P32: u32 = 0xB7E15163;
23        const Q32: u32 = 0x9E3779B9;
24        let t = 2 * (rounds + 1);
25        let mut s = vec![0u32; t as usize];
26        
27        // Initialize S array
28        s[0] = P32;
29        for i in 1..t as usize {
30            s[i] = s[i-1].wrapping_add(Q32);
31        }
32        
33        // Mix in the key
34        let mut l = vec![0u32; (key.len() + 3) / 4];
35        for i in 0..key.len() {
36            l[i / 4] |= (key[i] as u32) << ((i % 4) * 8);
37        }
38        
39        let mut i = 0;
40        let mut j = 0;
41        let mut a = 0u32;
42        let mut b = 0u32;
43        let v = 3 * std::cmp::max(t as usize, l.len());
44        
45        for _ in 0..v {
46            let rotated = (s[i].wrapping_add(a).wrapping_add(b)).rotate_left(3);
47            s[i] = rotated;
48            a = rotated;
49            
50            let rotated_l = (l[j].wrapping_add(a).wrapping_add(b)).rotate_left((a.wrapping_add(b)) as u32);
51            l[j] = rotated_l;
52            b = rotated_l;
53            i = (i + 1) % t as usize;
54            j = (j + 1) % l.len();
55        }
56        
57        s
58    }
59
60    pub fn encrypt_block(&self, block: &mut [u8; 8]) {
61        let mut a = u32::from_le_bytes([block[0], block[1], block[2], block[3]]);
62        let mut b = u32::from_le_bytes([block[4], block[5], block[6], block[7]]);
63        
64        a = a.wrapping_add(self.sub_keys[0]);
65        b = b.wrapping_add(self.sub_keys[1]);
66        
67        for i in 1..=self.rounds {
68            a = (a ^ b).rotate_left(b as u32).wrapping_add(self.sub_keys[2*i as usize]);
69            b = (b ^ a).rotate_left(a as u32).wrapping_add(self.sub_keys[2*i as usize + 1]);
70        }
71        
72        block[0..4].copy_from_slice(&a.to_le_bytes());
73        block[4..8].copy_from_slice(&b.to_le_bytes());
74    }
75
76    pub fn decrypt_block(&self, block: &mut [u8; 8]) {
77        let mut a = u32::from_le_bytes([block[0], block[1], block[2], block[3]]);
78        let mut b = u32::from_le_bytes([block[4], block[5], block[6], block[7]]);
79        
80        for i in (1..=self.rounds).rev() {
81            b = b.wrapping_sub(self.sub_keys[2*i as usize + 1]).rotate_right(a as u32) ^ a;
82            a = a.wrapping_sub(self.sub_keys[2*i as usize]).rotate_right(b as u32) ^ b;
83        }
84        
85        b = b.wrapping_sub(self.sub_keys[1]);
86        a = a.wrapping_sub(self.sub_keys[0]);
87        
88        block[0..4].copy_from_slice(&a.to_le_bytes());
89        block[4..8].copy_from_slice(&b.to_le_bytes());
90    }
91}
92
93#[cfg(test)]
94mod tests {
95    use super::*;
96
97    #[test]
98    fn test_encryption_decryption() {
99        let key = b"my secret key";
100        let rounds = 12;
101        let rc5 = RC5::new(key, rounds);
102        
103        let mut block = [0u8; 8];
104        block.copy_from_slice(b"testtest");
105        
106        let original = block;
107        rc5.encrypt_block(&mut block);
108        assert_ne!(block, original); // Encrypted should be different
109        
110        rc5.decrypt_block(&mut block);
111        assert_eq!(block, original); // Decrypted should match original
112    }
113
114    #[test]
115    #[should_panic(expected = "Number of rounds must be positive")]
116    fn test_invalid_rounds() {
117        let key = b"test key";
118        RC5::new(key, 0);
119    }
120
121    #[test]
122    #[should_panic(expected = "Key must not be empty")]
123    fn test_empty_key() {
124        let key = b"";
125        RC5::new(key, 12);
126    }
127}