#![forbid(unsafe_code)]
use aes::hazmat::cipher_round;
use aes::Block;
pub(crate) const BLOCK_SIZE: usize = 16;
const ROUNDS: usize = 14;
const SUBKEYS: usize = ROUNDS + 1;
const H_PERM: [usize; 16] = [1, 6, 11, 12, 5, 10, 15, 0, 9, 14, 3, 4, 13, 2, 7, 8];
const RCON: [u8; SUBKEYS] = [
0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91,
];
fn h_permute(tk: &mut [u8; BLOCK_SIZE]) {
let mut out = [0u8; BLOCK_SIZE];
for (i, slot) in out.iter_mut().enumerate() {
*slot = tk[H_PERM[i]];
}
*tk = out;
}
fn lfsr2(tk: &mut [u8; BLOCK_SIZE]) {
for b in tk.iter_mut() {
let new_lsb = ((*b >> 7) ^ (*b >> 5)) & 0x01;
*b = (*b << 1) | new_lsb;
}
}
fn round_constant(i: usize) -> [u8; BLOCK_SIZE] {
let rc = RCON[i];
[1, 2, 4, 8, rc, rc, rc, rc, 0, 0, 0, 0, 0, 0, 0, 0]
}
#[derive(Clone)]
pub(crate) struct DeoxysBc256 {
key_sub: [[u8; BLOCK_SIZE]; SUBKEYS],
}
impl DeoxysBc256 {
pub(crate) fn new(key: &[u8; BLOCK_SIZE]) -> Self {
let mut key_sub = [[0u8; BLOCK_SIZE]; SUBKEYS];
let mut tk2 = *key;
let rc0 = round_constant(0);
for i in 0..BLOCK_SIZE {
key_sub[0][i] = tk2[i] ^ rc0[i];
}
for (round, slot) in key_sub.iter_mut().enumerate().skip(1) {
h_permute(&mut tk2);
lfsr2(&mut tk2);
let rc = round_constant(round);
for i in 0..BLOCK_SIZE {
slot[i] = tk2[i] ^ rc[i];
}
}
Self { key_sub }
}
pub(crate) fn encrypt_block(
&self,
tweak: &[u8; BLOCK_SIZE],
plaintext: &[u8; BLOCK_SIZE],
) -> [u8; BLOCK_SIZE] {
let mut tk1 = *tweak;
let mut state = [0u8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
state[i] = plaintext[i] ^ self.key_sub[0][i] ^ tk1[i];
}
let mut block = Block::from(state);
for round in 1..=ROUNDS {
h_permute(&mut tk1);
let mut stk = [0u8; BLOCK_SIZE];
for i in 0..BLOCK_SIZE {
stk[i] = self.key_sub[round][i] ^ tk1[i];
}
cipher_round(&mut block, &Block::from(stk));
}
block.into()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deoxys_bc256_single_block_kat() {
let key: [u8; 16] = [
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
0x1e, 0x1f,
];
let tweak: [u8; 16] = [
0x10, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c,
0x2d, 0x2e,
];
let plaintext = [0u8; 16];
let expected: [u8; 16] = [
0x97, 0xd9, 0x51, 0xf2, 0xfd, 0x12, 0x90, 0x01, 0x48, 0x3e, 0x83, 0x1f, 0x2a, 0x68,
0x21, 0xe9,
];
let bc = DeoxysBc256::new(&key);
let ct = bc.encrypt_block(&tweak, &plaintext);
assert_eq!(ct, expected, "Deoxys-BC-256 single-block KAT mismatch");
}
#[test]
fn deoxys_bc256_tweak_sensitivity() {
let key = [0x42u8; 16];
let bc = DeoxysBc256::new(&key);
let pt = [0x11u8; 16];
let mut t1 = [0u8; 16];
let mut t2 = [0u8; 16];
t2[15] = 1;
assert_ne!(
bc.encrypt_block(&t1, &pt),
bc.encrypt_block(&t2, &pt),
"distinct tweaks must yield distinct ciphertexts"
);
t1[0] = 0xAA;
assert_eq!(bc.encrypt_block(&t1, &pt), bc.encrypt_block(&t1, &pt));
}
}