const ROUNDS: usize = 32;
macro_rules! R {
($x:expr, $y:expr, $k:expr) => {
$x = $x.rotate_right(8).wrapping_add($y) ^ $k;
$y = $y.rotate_left(3) ^ $x;
};
}
macro_rules! IR {
($x:expr, $y:expr, $k:expr) => {
$y = ($y ^ $x).rotate_right(3);
$x = ($x ^ $k).wrapping_sub($y).rotate_left(8);
};
}
pub const fn expand(key: [u64; 2]) -> [u64; ROUNDS] {
let [mut b, mut a] = key;
let mut rk = [0; ROUNDS];
let mut i = 0;
while i < ROUNDS {
rk[i] = a;
R!(b, a, i as u64);
i += 1;
}
rk
}
pub const fn encrypt(pt: [u64; 2], rk: &[u64; ROUNDS]) -> [u64; 2] {
let [mut y, mut x] = pt;
let mut i = 0;
while i < ROUNDS {
R!(y, x, rk[i]);
i += 1;
}
[y, x]
}
#[allow(dead_code)]
pub const fn decrypt(ct: [u64; 2], rk: &[u64; ROUNDS]) -> [u64; 2] {
let [mut y, mut x] = ct;
let mut i = ROUNDS;
while i > 0 {
i -= 1;
IR!(y, x, rk[i]);
}
[y, x]
}
#[test]
fn test_vectors() {
let key = [0x0f0e0d0c0b0a0908, 0x0706050403020100];
let rk = expand(key);
let plaintext = [0x6c61766975716520, 0x7469206564616d20];
let ciphertext = [0xa65d985179783265, 0x7860fedf5c570d18];
assert_eq!(ciphertext, encrypt(plaintext, &rk));
assert_eq!(plaintext, decrypt(ciphertext, &rk));
}