Skip to main content

rs_crypto/
xtea.rs

1// XTEA (eXtended Tiny Encryption Algorithm) block cipher.
2// 128-bit key, 64-bit block, Feistel network.
3// Core algorithm is the reference C implementation from Wikipedia (public domain).
4
5unsafe extern "C" {
6    fn encipher(num_rounds: u32, v: *mut u32, key: *const u32);
7    fn decipher(num_rounds: u32, v: *mut u32, key: *const u32);
8}
9
10pub struct Xtea;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum XteaError {
14    InvalidLength,
15}
16
17impl std::fmt::Display for XteaError {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        match self {
20            XteaError::InvalidLength => write!(f, "Data length must be a multiple of 8"),
21        }
22    }
23}
24
25impl std::error::Error for XteaError {}
26
27pub type Result<T> = std::result::Result<T, XteaError>;
28
29impl Xtea {
30    pub const ROUNDS: u32 = 32;
31
32    pub fn encrypt(data: &mut [u8], key: &[u32; 4]) -> Result<()> {
33        Self::encrypt_with_rounds(data, key, Self::ROUNDS)
34    }
35
36    pub fn decrypt(data: &mut [u8], key: &[u32; 4]) -> Result<()> {
37        Self::decrypt_with_rounds(data, key, Self::ROUNDS)
38    }
39
40    pub fn encrypt_with_rounds(data: &mut [u8], key: &[u32; 4], rounds: u32) -> Result<()> {
41        if !data.len().is_multiple_of(8) {
42            return Err(XteaError::InvalidLength);
43        }
44
45        for chunk in data.chunks_exact_mut(8) {
46            let mut v: [u32; 2] = [
47                u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]),
48                u32::from_be_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]),
49            ];
50
51            unsafe {
52                encipher(rounds, v.as_mut_ptr(), key.as_ptr());
53            }
54
55            chunk[0..4].copy_from_slice(&v[0].to_be_bytes());
56            chunk[4..8].copy_from_slice(&v[1].to_be_bytes());
57        }
58
59        Ok(())
60    }
61
62    pub fn decrypt_with_rounds(data: &mut [u8], key: &[u32; 4], rounds: u32) -> Result<()> {
63        if !data.len().is_multiple_of(8) {
64            return Err(XteaError::InvalidLength);
65        }
66
67        for chunk in data.chunks_exact_mut(8) {
68            let mut v: [u32; 2] = [
69                u32::from_be_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]),
70                u32::from_be_bytes([chunk[4], chunk[5], chunk[6], chunk[7]]),
71            ];
72
73            unsafe {
74                decipher(rounds, v.as_mut_ptr(), key.as_ptr());
75            }
76
77            chunk[0..4].copy_from_slice(&v[0].to_be_bytes());
78            chunk[4..8].copy_from_slice(&v[1].to_be_bytes());
79        }
80
81        Ok(())
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn test_roundtrip() {
91        let key: [u32; 4] = [0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210];
92        let original = b"ABCDEFGH";
93        let mut data = original.to_vec();
94
95        Xtea::encrypt(&mut data, &key).unwrap();
96        assert_ne!(&data, original);
97
98        Xtea::decrypt(&mut data, &key).unwrap();
99        assert_eq!(&data, original);
100    }
101
102    #[test]
103    fn test_invalid_length() {
104        let key: [u32; 4] = [0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210];
105        let mut data = vec![1u8, 2, 3, 4, 5];
106        assert_eq!(
107            Xtea::decrypt(&mut data, &key),
108            Err(XteaError::InvalidLength)
109        );
110    }
111
112    #[test]
113    fn test_multiple_blocks() {
114        let key: [u32; 4] = [0xDEADBEEF, 0xCAFEBABE, 0xFEEDFACE, 0xC0DED00D];
115        let original = b"ABCDEFGHJKLMNOPQ";
116        let mut data = original.to_vec();
117
118        Xtea::encrypt(&mut data, &key).unwrap();
119        Xtea::decrypt(&mut data, &key).unwrap();
120        assert_eq!(&data, original);
121    }
122
123    #[test]
124    fn test_zero_key_roundtrip() {
125        let key: [u32; 4] = [0, 0, 0, 0];
126        let mut data = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
127
128        Xtea::encrypt(&mut data, &key).unwrap();
129        Xtea::decrypt(&mut data, &key).unwrap();
130        assert_eq!(data, [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
131    }
132
133    #[test]
134    fn test_decrypt_with_custom_rounds() {
135        let key: [u32; 4] = [0x01234567, 0x89ABCDEF, 0xFEDCBA98, 0x76543210];
136        let original = b"TESTDATA";
137        let mut data = original.to_vec();
138
139        Xtea::encrypt(&mut data, &key).unwrap();
140        Xtea::decrypt_with_rounds(&mut data, &key, 32).unwrap();
141        assert_eq!(&data, original);
142    }
143}