Skip to main content

xtea/
lib.rs

1//! Pure Rust implementation of the [Extended Tiny Encryption Algorithm][XTEA].
2//!
3//! # ⚠️ Security Warning: Hazmat!
4//!
5//! This crate implements only the low-level block cipher function, and is intended
6//! for use for implementing higher-level constructions *only*. It is NOT
7//! intended for direct use in applications.
8//!
9//! USE AT YOUR OWN RISK!
10//!
11//! [XTEA]: https://en.wikipedia.org/wiki/XTEA
12
13#![no_std]
14#![doc(
15    html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg",
16    html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/26acc39f/logo.svg"
17)]
18#![deny(unsafe_code)]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![warn(missing_docs, rust_2018_idioms)]
21
22pub use cipher;
23
24use cipher::{
25    AlgorithmName, Block, BlockCipherDecBackend, BlockCipherDecClosure, BlockCipherDecrypt,
26    BlockCipherEncBackend, BlockCipherEncClosure, BlockCipherEncrypt, BlockSizeUser, InOut,
27    InvalidLength, Key, KeyInit, KeySizeUser, ParBlocksSizeUser,
28    consts::{U1, U8, U16},
29};
30use core::fmt;
31
32#[cfg(feature = "zeroize")]
33use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
34
35mod consts;
36use consts::{DELTA, ROUNDS};
37
38/// XTEA block self.
39pub struct Xtea {
40    k: [u32; 4],
41}
42
43impl KeySizeUser for Xtea {
44    type KeySize = U16;
45}
46
47impl KeyInit for Xtea {
48    fn new(key: &Key<Self>) -> Self {
49        Self::new_from_slice(key).unwrap()
50    }
51
52    fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> {
53        if key.len() != 16 {
54            return Err(InvalidLength);
55        }
56        let key = [
57            u32::from_le_bytes(key[0..4].try_into().unwrap()),
58            u32::from_le_bytes(key[4..8].try_into().unwrap()),
59            u32::from_le_bytes(key[8..12].try_into().unwrap()),
60            u32::from_le_bytes(key[12..16].try_into().unwrap()),
61        ];
62        Ok(Xtea { k: key })
63    }
64}
65
66impl BlockSizeUser for Xtea {
67    type BlockSize = U8;
68}
69
70impl ParBlocksSizeUser for Xtea {
71    type ParBlocksSize = U1;
72}
73
74impl BlockCipherEncrypt for Xtea {
75    #[inline]
76    fn encrypt_with_backend(&self, f: impl BlockCipherEncClosure<BlockSize = Self::BlockSize>) {
77        f.call(self)
78    }
79}
80
81impl BlockCipherEncBackend for Xtea {
82    #[inline]
83    fn encrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
84        let v = block.get_in();
85        let mut v0 = u32::from_le_bytes(v[0..4].try_into().unwrap());
86        let mut v1 = u32::from_le_bytes(v[4..8].try_into().unwrap());
87        let mut sum = 0u32;
88
89        // Use 4 loops as otherwise unrolling will not be performed by default
90        for _ in 0..8 {
91            v0 = v0.wrapping_add(
92                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
93                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
94            );
95            sum = sum.wrapping_add(DELTA);
96            v1 = v1.wrapping_add(
97                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
98                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
99            );
100        }
101        for _ in 0..8 {
102            v0 = v0.wrapping_add(
103                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
104                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
105            );
106            sum = sum.wrapping_add(DELTA);
107            v1 = v1.wrapping_add(
108                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
109                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
110            );
111        }
112        for _ in 0..8 {
113            v0 = v0.wrapping_add(
114                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
115                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
116            );
117            sum = sum.wrapping_add(DELTA);
118            v1 = v1.wrapping_add(
119                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
120                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
121            );
122        }
123        for _ in 0..8 {
124            v0 = v0.wrapping_add(
125                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
126                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
127            );
128            sum = sum.wrapping_add(DELTA);
129            v1 = v1.wrapping_add(
130                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
131                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
132            );
133        }
134
135        let v = block.get_out();
136        v[0..4].copy_from_slice(&v0.to_le_bytes());
137        v[4..8].copy_from_slice(&v1.to_le_bytes());
138    }
139}
140
141impl BlockCipherDecrypt for Xtea {
142    #[inline]
143    fn decrypt_with_backend(&self, f: impl BlockCipherDecClosure<BlockSize = Self::BlockSize>) {
144        f.call(self)
145    }
146}
147
148impl BlockCipherDecBackend for Xtea {
149    #[inline]
150    fn decrypt_block(&self, mut block: InOut<'_, '_, Block<Self>>) {
151        let v = block.get_in();
152        let mut v0 = u32::from_le_bytes(v[0..4].try_into().unwrap());
153        let mut v1 = u32::from_le_bytes(v[4..8].try_into().unwrap());
154        let mut sum = DELTA.wrapping_mul(ROUNDS as u32);
155
156        // Same as encrypt, just in reverse
157        for _ in 0..8 {
158            v1 = v1.wrapping_sub(
159                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
160                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
161            );
162            sum = sum.wrapping_sub(DELTA);
163            v0 = v0.wrapping_sub(
164                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
165                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
166            );
167        }
168        for _ in 0..8 {
169            v1 = v1.wrapping_sub(
170                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
171                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
172            );
173            sum = sum.wrapping_sub(DELTA);
174            v0 = v0.wrapping_sub(
175                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
176                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
177            );
178        }
179        for _ in 0..8 {
180            v1 = v1.wrapping_sub(
181                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
182                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
183            );
184            sum = sum.wrapping_sub(DELTA);
185            v0 = v0.wrapping_sub(
186                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
187                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
188            );
189        }
190        for _ in 0..8 {
191            v1 = v1.wrapping_sub(
192                (((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0))
193                    ^ sum.wrapping_add(self.k[((sum >> 11) & 3) as usize]),
194            );
195            sum = sum.wrapping_sub(DELTA);
196            v0 = v0.wrapping_sub(
197                (((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1))
198                    ^ sum.wrapping_add(self.k[(sum & 3) as usize]),
199            );
200        }
201
202        let v = block.get_out();
203        v[0..4].copy_from_slice(&v0.to_le_bytes());
204        v[4..8].copy_from_slice(&v1.to_le_bytes());
205    }
206}
207
208impl fmt::Debug for Xtea {
209    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210        f.write_str("XTEA { ... }")
211    }
212}
213
214impl AlgorithmName for Xtea {
215    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
216        f.write_str("XTEA")
217    }
218}
219
220impl Drop for Xtea {
221    fn drop(&mut self) {
222        #[cfg(feature = "zeroize")]
223        self.k.zeroize();
224    }
225}
226
227#[cfg(feature = "zeroize")]
228impl ZeroizeOnDrop for Xtea {}