extended_tea/
lib.rs

1/*!
2This crate provides a Rusty implementation of the XTEA cipher, written in Rust.
3
4This crate also provides convenience methods for ciphering and deciphering `u8` slices
5and Read streams.
6
7See [Wikipedia](https://en.wikipedia.org/wiki/XTEA) for more information on the XTEA cipher.
8
9This crate makes use of Wrapping<u32> in order to bypass Rusts' arithmetic overflow panics since the algorithm
10relies on overflowing **wrapping** around, not panicking.
11*/
12
13use byteorder::{ByteOrder, ReadBytesExt, WriteBytesExt};
14use std::{
15    io::{Cursor, Read, Result, Write},
16    num::Wrapping,
17};
18
19/// Struct containing the `XTEA` info.
20///
21/// See [Wikipedia](https://en.wikipedia.org/wiki/XTEA) for more information
22///
23#[derive(Debug)]
24pub struct XTEA {
25    key: [Wrapping<u32>; 4],
26    num_rounds: Wrapping<u32>,
27}
28
29/// Reccomended default number of rounds
30const DEFAULT_ROUNDS: u32 = 32;
31
32/// Magic number specified by the algorithm
33const DELTA: Wrapping<u32> = Wrapping(0x9E3779B9);
34
35impl XTEA {
36    /// Creates a new `XTEA` cipher using the given key.
37    #[inline]
38    pub fn new(key: &[u32; 4]) -> Self {
39        Self::new_with_rounds(key, DEFAULT_ROUNDS)
40    }
41
42    /// Creates a new XTEA cipher using the given key, with a custom number of rounds.
43    ///
44    /// **HIGHLY Recommended** to use the fn `new(key: [u32; 4]) -> Self` instead unless you know what you're doing.
45    ///
46    /// # Panics
47    ///
48    /// If num_rounds is NOT divisible by 2.
49    #[inline]
50    pub fn new_with_rounds(key: &[u32; 4], num_rounds: u32) -> Self {
51        assert_eq!(num_rounds & 1, 0, "num_rounds was not divisible by 2.");
52        let key = [
53            Wrapping(key[0]),
54            Wrapping(key[1]),
55            Wrapping(key[2]),
56            Wrapping(key[3]),
57        ];
58        let num_rounds = Wrapping(num_rounds);
59        XTEA { key, num_rounds }
60    }
61
62    /// Enciphers the two given `u32`'s into the output array.
63    ///
64    /// Highly recommended to NOT use this, and instead use either the slice or stream implementation.
65    ///
66    /// See <https://en.wikipedia.org/wiki/XTEA#Implementations> for implementation details
67    #[inline]
68    pub fn encipher(&self, input: &[u32; 2], output: &mut [u32; 2]) {
69        let mut v0 = Wrapping(input[0]);
70        let mut v1 = Wrapping(input[1]);
71        let mut sum = Wrapping(0u32);
72
73        for _ in 0..self.num_rounds.0 as u32 {
74            v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + self.key[(sum.0 & 3) as usize]);
75            sum += DELTA;
76            v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + self.key[((sum.0 >> 11) & 3) as usize]);
77        }
78
79        output[0] = v0.0;
80        output[1] = v1.0;
81    }
82
83    /// Deciphers the two given `u32`'s into the output array.
84    ///
85    /// Highly recommended to NOT use this, and instead use either the slice or stream implementation.
86    ///
87    /// See <https://en.wikipedia.org/wiki/XTEA#Implementations> for implementation details
88    #[inline]
89    pub fn decipher(&self, input: &[u32; 2], output: &mut [u32; 2]) {
90        let mut v0 = Wrapping(input[0]);
91        let mut v1 = Wrapping(input[1]);
92        let mut sum = DELTA * self.num_rounds;
93
94        for _ in 0..self.num_rounds.0 as u32 {
95            v1 -= (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + self.key[((sum.0 >> 11) & 3) as usize]);
96            sum -= DELTA;
97            v0 -= (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + self.key[(sum.0 & 3) as usize]);
98        }
99
100        output[0] = v0.0;
101        output[1] = v1.0;
102    }
103
104    /// Enciphers the given `&[u8]` into the output `&mut [u8]`.
105    ///
106    /// Uses the given [ByteOrder](https://docs.rs/byteorder) passed as a template for properly parsing the slices.
107    ///
108    /// If you're unsure which ByteOrder to use, use `BigEndian` (BE).
109    ///
110    /// # Panics
111    ///
112    /// If the length of the input is not equal to the length of the output
113    ///
114    /// If the length of the input or output is not divisible by 8
115    ///
116    /// # Examples
117    ///
118    /// ```
119    /// use extended_tea::XTEA;
120    ///	use byteorder::BE;
121    ///
122    /// let input: Box<[u8]> = vec![10u8; 16].into_boxed_slice();
123    ///
124    ///	let xtea = XTEA::new(&[0x1380C5B5, 0x28037DF9, 0x26E314A2, 0xC57684E4]);
125    ///
126    ///	let encrypted = {
127    ///		let mut output = vec![0u8; input.len()].into_boxed_slice();
128    ///		xtea.encipher_u8slice::<BE>(&input, &mut output);
129    ///		output
130    ///	};
131    /// ```
132    ///
133    #[inline]
134    pub fn encipher_u8slice<B: ByteOrder>(&self, input: &[u8], output: &mut [u8]) {
135        self.cipher_u8slice::<B>(input, output, true)
136    }
137
138    /// Deciphers the given `&[u8]` into the output `&mut [u8]`.
139    ///
140    /// Uses the given [ByteOrder](https://docs.rs/byteorder) passed as a template for properly parsing the slices.
141    ///
142    /// If you're unsure which ByteOrder to use, use `BigEndian` (BE).
143    ///
144    /// # Panics
145    ///
146    /// If the length of the input is not equal to the length of the output.
147    ///
148    /// If the length of the input or output is not divisible by 8.
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use extended_tea::XTEA;
154    ///	use byteorder::BE;
155    ///
156    /// let input: Box<[u8]> = vec![10u8; 16].into_boxed_slice();
157    ///
158    ///	let xtea = XTEA::new(&[0x1380C5B5, 0x28037DF9, 0x26E314A2, 0xC57684E4]);
159    ///
160    ///	let encrypted = {
161    ///		let mut output = vec![0u8; input.len()].into_boxed_slice();
162    ///		xtea.encipher_u8slice::<BE>(&input, &mut output);
163    ///		output
164    ///	};
165    ///
166    /// let decrypted = {
167    /// 	let mut output = vec![0u8; input.len()].into_boxed_slice();
168    /// 	xtea.decipher_u8slice::<BE>(&encrypted, &mut output);
169    /// 	output
170    /// };
171    /// assert_eq!(input, decrypted);
172    /// ```
173    ///
174    #[inline]
175    pub fn decipher_u8slice<B: ByteOrder>(&self, input: &[u8], output: &mut [u8]) {
176        self.cipher_u8slice::<B>(input, output, false)
177    }
178
179    #[inline]
180    fn cipher_u8slice<B: ByteOrder>(&self, input: &[u8], output: &mut [u8], encipher: bool) {
181        assert_eq!(
182            input.len(),
183            output.len(),
184            "The input and output slices must be of the same length."
185        );
186        assert_eq!(
187            input.len() % 8,
188            0,
189            "Input and output slices must be of a length divisible by 8."
190        );
191
192        //Create cursors for the two slices, and pass it off to the stream cipher handler
193        let mut input_reader = Cursor::new(input);
194        let mut ouput_writer = Cursor::new(output);
195
196        self.cipher_stream::<B, Cursor<&[u8]>, Cursor<&mut [u8]>>(
197            &mut input_reader,
198            &mut ouput_writer,
199            encipher,
200        )
201        .unwrap()
202        /*
203        let mut input_buf = [0 as u32; 2];
204        let mut output_buf = [0 as u32; 2];
205
206        for _ in 0..iterations {
207            input_buf[0] = input_reader.read_u32::<T>().unwrap();
208            input_buf[1] = input_reader.read_u32::<T>().unwrap();
209
210            if encipher {
211                self.encipher(&input_buf, &mut output_buf);
212            } else {
213                self.decipher(&input_buf, &mut output_buf);
214            }
215
216            ouput_writer.write_u32::<T>(output_buf[0]).unwrap();
217            ouput_writer.write_u32::<T>(output_buf[1]).unwrap();
218        }
219        */
220    }
221
222    /// Enciphers the given input stream into the given output stream.
223    ///
224    /// Uses the given [ByteOrder](https://docs.rs/byteorder) passed as a template for properly parsing the streams.
225    ///
226    /// If you're unsure which ByteOrder to use, use `BigEndian` (BE).
227    ///
228    /// # Returns
229    ///
230    /// Ok(()) if there were no errors in parsing.
231    ///
232    /// Err(_) if there was an error parsing the input stream that did NOT occour on an even read.
233    /// In other words, the stream's input needs to have a length that is divisible by 8.
234    ///
235    /// **NOTE**: Unlike std::io::{Read, Write} in the case of an Err(_), the output stream IS modified
236    #[inline]
237    pub fn encipher_stream<B: ByteOrder, T: Read, S: Write>(
238        &self,
239        input: &mut T,
240        output: &mut S,
241    ) -> Result<()> {
242        self.cipher_stream::<B, T, S>(input, output, true)
243    }
244
245    /// Deciphers the given input stream into the given output stream.
246    ///
247    /// Uses the given [ByteOrder](https://docs.rs/byteorder) passed as a template for properly parsing the streams.
248    ///
249    /// If you're unsure which ByteOrder to use, use `BigEndian` (BE).
250    ///
251    /// # Returns
252    ///
253    /// Ok(()) if there were no errors in parsing.
254    ///
255    /// Err(_) if there was an error parsing the input stream that did NOT occour on an even read.
256    /// In other words, the stream's input needs to have a length that is divisible by 8.
257    ///
258    /// **NOTE**: Unlike std::io::{Read, Write} in the case of an Err(_), the output stream IS modified
259    #[inline]
260    pub fn decipher_stream<B: ByteOrder, T: Read, S: Write>(
261        &self,
262        input: &mut T,
263        output: &mut S,
264    ) -> Result<()> {
265        self.cipher_stream::<B, T, S>(input, output, false)
266    }
267
268    #[inline]
269    fn cipher_stream<B: ByteOrder, T: Read, S: Write>(
270        &self,
271        input: &mut T,
272        output: &mut S,
273        encipher: bool,
274    ) -> Result<()> {
275        let mut input_buf = [0 as u32; 2];
276        let mut output_buf = [0 as u32; 2];
277
278        loop {
279            //An error parsing the first value means we should stop parsing, not fail
280            input_buf[0] = match input.read_u32::<B>() {
281                Ok(val) => val,
282                Err(_) => break,
283            };
284            input_buf[1] = input.read_u32::<B>()?;
285
286            if encipher {
287                self.encipher(&input_buf, &mut output_buf);
288            } else {
289                self.decipher(&input_buf, &mut output_buf);
290            }
291
292            output.write_u32::<B>(output_buf[0])?;
293            output.write_u32::<B>(output_buf[1])?;
294        }
295        Ok(())
296    }
297}
298
299#[cfg(test)]
300mod tests {
301    use super::XTEA;
302    use byteorder::BE;
303
304    #[test]
305    fn en_de_cipher() {
306        let xtea = XTEA::new(&[0xffffffff; 4]);
307        let input = [1234u32, 5678u32];
308
309        let encrypted = {
310            let mut output = [0u32; 2];
311            xtea.encipher(&input, &mut output);
312            output
313        };
314        let decrypted = {
315            let mut output = [0u32; 2];
316            xtea.decipher(&encrypted, &mut output);
317            output
318        };
319        assert_eq!(input, decrypted);
320    }
321
322    #[test]
323    fn u8_slice() {
324        // The two 0's at the end pad the message to 32 bytes. Needed so that input is divisible by 8.
325        let input = b"Hello. Performing a test here.00";
326
327        let xtea = XTEA::new(&[0x1380C5B5, 0x28037DF9, 0x26E314A2, 0xC57684E4]);
328
329        let encrypted = {
330            let mut output = [0; 32];
331            xtea.encipher_u8slice::<BE>(input, &mut output);
332            output
333        };
334
335        let decrypted = {
336            let mut output = [0; 32];
337            xtea.decipher_u8slice::<BE>(&encrypted, &mut output);
338            output
339        };
340
341        assert_eq!(input, &decrypted);
342    }
343
344    #[test]
345    fn boxed_slice() {
346        let input: Box<[u8]> = vec![10u8; 16].into_boxed_slice();
347
348        let xtea = XTEA::new(&[0x1380C5B5, 0x28037DF9, 0x26E314A2, 0xC57684E4]);
349
350        let encrypted = {
351            let mut output = vec![0u8; input.len()].into_boxed_slice();
352            xtea.encipher_u8slice::<BE>(&input, &mut output);
353            output
354        };
355
356        let decrypted = {
357            let mut output = vec![0u8; input.len()].into_boxed_slice();
358            xtea.decipher_u8slice::<BE>(&encrypted, &mut output);
359            output
360        };
361
362        assert_eq!(input, decrypted);
363    }
364}