Skip to content

Commit

Permalink
Unroll xtea
Browse files Browse the repository at this point in the history
  • Loading branch information
valaphee committed May 11, 2024
1 parent c7cdac7 commit 24a0db9
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 20 deletions.
2 changes: 1 addition & 1 deletion xtea/src/consts.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
pub const DELTA: u32 = 0x9e3779b9;
pub const ROUNDS: u32 = 32;
pub const ROUNDS: usize = 32;
51 changes: 32 additions & 19 deletions xtea/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ use cipher::zeroize::{Zeroize, ZeroizeOnDrop};
mod consts;
use consts::{DELTA, ROUNDS};

/// XTEA block cipher.
pub struct Xtea {
key: [u32; 4],
k: [[u32; 2]; ROUNDS],
}

impl BlockCipher for Xtea {}
Expand All @@ -52,14 +53,20 @@ impl KeyInit for Xtea {
if key.len() != 16 {
return Err(InvalidLength);
}
Ok(Xtea {
key: [
u32::from_le_bytes(key[0..4].try_into().unwrap()),
u32::from_le_bytes(key[4..8].try_into().unwrap()),
u32::from_le_bytes(key[8..12].try_into().unwrap()),
u32::from_le_bytes(key[12..16].try_into().unwrap()),
],
})
let key = [
u32::from_le_bytes(key[0..4].try_into().unwrap()),
u32::from_le_bytes(key[4..8].try_into().unwrap()),
u32::from_le_bytes(key[8..12].try_into().unwrap()),
u32::from_le_bytes(key[12..16].try_into().unwrap()),
];
let mut k = [[0; 2]; 32];
let mut sum = 0u32;
for k in &mut k {
k[0] = sum.wrapping_add(key[(sum & 3) as usize]);
sum = sum.wrapping_add(DELTA);
k[1] = sum.wrapping_add(key[((sum >> 11) & 3) as usize]);
}
Ok(Xtea { k })
}
}

Expand Down Expand Up @@ -94,11 +101,14 @@ cipher::impl_simple_block_encdec!(
let mut v0 = u32::from_le_bytes(v[0..4].try_into().unwrap());
let mut v1 = u32::from_le_bytes(v[4..8].try_into().unwrap());

let mut sum = 0u32;
for _ in 0..ROUNDS {
v0 = v0.wrapping_add((((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1)) ^ (sum.wrapping_add(cipher.key[(sum & 3) as usize])));
sum = sum.wrapping_add(DELTA);
v1 = v1.wrapping_add((((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0)) ^ (sum.wrapping_add(cipher.key[((sum >> 11) & 3) as usize])));
// Use 2 loops as unrolling will not be performed by default
for k in cipher.k[0..16].iter() {
v0 = v0.wrapping_add((((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1)) ^ k[0]);
v1 = v1.wrapping_add((((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0)) ^ k[1]);
}
for k in cipher.k[16..32].iter() {
v0 = v0.wrapping_add((((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1)) ^ k[0]);
v1 = v1.wrapping_add((((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0)) ^ k[1]);
}

let v = block.get_out();
Expand All @@ -110,11 +120,14 @@ cipher::impl_simple_block_encdec!(
let mut v0 = u32::from_le_bytes(v[0..4].try_into().unwrap());
let mut v1 = u32::from_le_bytes(v[4..8].try_into().unwrap());

let mut sum = DELTA.wrapping_mul(ROUNDS);
for _ in 0..ROUNDS {
v1 = v1.wrapping_sub((((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0)) ^ (sum.wrapping_add(cipher.key[((sum >> 11) & 3) as usize])));
sum = sum.wrapping_sub(DELTA);
v0 = v0.wrapping_sub((((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1)) ^ (sum.wrapping_add(cipher.key[(sum & 3) as usize])));
// Use 2 loops as unrolling will not be performed by default
for k in cipher.k[16..32].iter().rev() {
v1 = v1.wrapping_sub((((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0)) ^ k[1]);
v0 = v0.wrapping_sub((((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1)) ^ k[0]);
}
for k in cipher.k[0..16].iter().rev() {
v1 = v1.wrapping_sub((((v0 << 4) ^ (v0 >> 5)).wrapping_add(v0)) ^ k[1]);
v0 = v0.wrapping_sub((((v1 << 4) ^ (v1 >> 5)).wrapping_add(v1)) ^ k[0]);
}

let v = block.get_out();
Expand Down
17 changes: 17 additions & 0 deletions xtea/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,18 @@
use cipher::{array::Array, BlockCipherDecrypt, BlockCipherEncrypt, KeyInit};
use xtea::Xtea;

#[test]
fn xtea() {
// https://web.archive.org/web/20231115163347/https://asecuritysite.com/encryption/xtea
let key = b"0123456789012345";
let plaintext = b"ABCDEFGH";
let ciphertext = [0xea, 0x0c, 0x3d, 0x7c, 0x1c, 0x22, 0x55, 0x7f];
let cipher = Xtea::new_from_slice(key).unwrap();

let mut block = Array::clone_from_slice(plaintext);
cipher.encrypt_block(&mut block);
assert_eq!(ciphertext, block.as_slice());

cipher.decrypt_block(&mut block);
assert_eq!(plaintext, block.as_slice());
}

0 comments on commit 24a0db9

Please sign in to comment.