#![feature(i128_type)]
#![no_std]
#![forbid(unsafe_code)]
const ROUNDS: u64 = 32;
macro_rules! round {
($x:ident, $y:ident, $k:ident) => {
$x = $x.rotate_right(8);
$x = $x.wrapping_add($y);
$x ^= $k;
$y = $y.rotate_left(3);
$y ^= $x;
}
}
macro_rules! inv_round {
($x:ident, $y:ident, $k:ident) => {
$y ^= $x;
$y = $y.rotate_right(3);
$x ^= $k;
$x = $x.wrapping_sub($y);
$x = $x.rotate_left(8);
}
}
pub fn encrypt_block(m: u128, k: u128) -> u128 {
let mut m1 = (m >> 64) as u64;
let mut m2 = m as u64;
let mut k1 = (k >> 64) as u64;
let mut k2 = k as u64;
round!(m1, m2, k2);
for i in 0..ROUNDS - 1 {
round!(k1, k2, i);
round!(m1, m2, k2);
}
m2 as u128 | (m1 as u128) << 64
}
pub struct Key {
schedule: [u64; ROUNDS as usize],
}
impl Key {
pub fn new(k: u128) -> Key {
let mut k1 = (k >> 64) as u64;
let mut k2 = k as u64;
let mut ret = Key {
schedule: [0; ROUNDS as usize],
};
for i in 0..ROUNDS {
ret.schedule[i as usize] = k2;
round!(k1, k2, i);
}
ret
}
pub fn encrypt_block(&self, m: u128) -> u128 {
let mut m1 = (m >> 64) as u64;
let mut m2 = m as u64;
for &k in &self.schedule {
round!(m1, m2, k);
}
m2 as u128 | (m1 as u128) << 64
}
pub fn decrypt_block(&self, c: u128) -> u128 {
let mut c1 = (c >> 64) as u64;
let mut c2 = c as u64;
for &k in self.schedule.iter().rev() {
inv_round!(c1, c2, k);
}
c2 as u128 | (c1 as u128) << 64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encrypt_decrypt() {
for mut x in 0u128..90000 {
x = x.wrapping_mul(0x6eed0e9da4d94a4f6eed0e9da4d94a4f);
x ^= (x >> 6) >> (x >> 122);
x = x.wrapping_mul(0x6eed0e9da4d94a4f6eed0e9da4d94a4f);
let key = Key::new(!x);
assert_eq!(key.decrypt_block(key.encrypt_block(x)), x);
assert_eq!(key.encrypt_block(x), encrypt_block(x, !x));
}
}
#[test]
fn test_vectors() {
assert_eq!(
encrypt_block(
0x6c617669757165207469206564616d20,
0x0f0e0d0c0b0a09080706050403020100
),
0xa65d9851797832657860fedf5c570d18
);
}
}