1unsafe 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}