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}