use core::convert::TryInto;
pub const TWOFISH_BLOCK_SIZE: usize = 16;
#[derive(Debug, Clone, Copy)]
pub enum TwofishKeySize {
Bits128,
Bits192,
Bits256,
}
pub const TWOFISH_SUBKEY_COUNT: usize = 40;
pub const MDS_DIM: usize = 4;
#[derive(Debug)]
pub struct TwofishKey {
pub subkeys: [u32; TWOFISH_SUBKEY_COUNT],
sbox_keys: [u32; 8],
kwords: usize,
}
impl TwofishKey {
pub fn new(key_data: &[u8], key_size: TwofishKeySize) -> Self {
let (expected_len, kwords) = match key_size {
TwofishKeySize::Bits128 => (16, 4),
TwofishKeySize::Bits192 => (24, 6),
TwofishKeySize::Bits256 => (32, 8),
};
if key_data.len() != expected_len {
panic!("Invalid key length for TwofishKeySize");
}
let mut key = TwofishKey {
subkeys: [0u32; TWOFISH_SUBKEY_COUNT],
sbox_keys: [0u32; 8],
kwords,
};
let mut key_words = vec![0u32; kwords];
for (i, chunk) in key_data.chunks_exact(4).enumerate().take(kwords) {
key_words[i] = u32::from_le_bytes(chunk.try_into().unwrap());
}
key.twofish_key_schedule(&key_words);
key
}
pub fn encrypt_block(&self, block: &mut [u8; TWOFISH_BLOCK_SIZE]) {
let mut x = [
u32::from_le_bytes(block[0..4].try_into().unwrap()),
u32::from_le_bytes(block[4..8].try_into().unwrap()),
u32::from_le_bytes(block[8..12].try_into().unwrap()),
u32::from_le_bytes(block[12..16].try_into().unwrap()),
];
x[0] ^= self.subkeys[0];
x[1] ^= self.subkeys[1];
x[2] ^= self.subkeys[2];
x[3] ^= self.subkeys[3];
for r in 0..16 {
let t0 = self.g_function(x[0], 0);
let t1 = self.g_function(rol(x[1], 8), 1);
let f0 = t0.wrapping_add(t1).wrapping_add(self.subkeys[8 + 2 * r]);
let f1 = t0
.wrapping_add(t1.wrapping_mul(2))
.wrapping_add(self.subkeys[9 + 2 * r]);
let tmp2 = rol(x[2] ^ f0, 1);
let tmp3 = ror(x[3], 1) ^ f1;
x[2] = tmp2;
x[3] = tmp3;
if r != 15 {
let t0 = x[0];
let t1 = x[1];
x[0] = x[2];
x[1] = x[3];
x[2] = t0;
x[3] = t1;
}
}
x[2] ^= self.subkeys[4];
x[3] ^= self.subkeys[5];
x[0] ^= self.subkeys[6];
x[1] ^= self.subkeys[7];
block[0..4].copy_from_slice(&x[0].to_le_bytes());
block[4..8].copy_from_slice(&x[1].to_le_bytes());
block[8..12].copy_from_slice(&x[2].to_le_bytes());
block[12..16].copy_from_slice(&x[3].to_le_bytes());
}
pub fn decrypt_block(&self, block: &mut [u8; TWOFISH_BLOCK_SIZE]) {
let mut x = [
u32::from_le_bytes(block[0..4].try_into().unwrap()),
u32::from_le_bytes(block[4..8].try_into().unwrap()),
u32::from_le_bytes(block[8..12].try_into().unwrap()),
u32::from_le_bytes(block[12..16].try_into().unwrap()),
];
x[0] ^= self.subkeys[6];
x[1] ^= self.subkeys[7];
x[2] ^= self.subkeys[4];
x[3] ^= self.subkeys[5];
for r in (0..16).rev() {
if r != 15 {
let t2 = x[2];
let t3 = x[3];
x[2] = x[0];
x[3] = x[1];
x[0] = t2;
x[1] = t3;
}
let t0 = self.g_function(x[0], 0);
let t1 = self.g_function(rol(x[1], 8), 1);
let f0 = t0.wrapping_add(t1).wrapping_add(self.subkeys[8 + 2 * r]);
let f1 = t0
.wrapping_add(t1.wrapping_mul(2))
.wrapping_add(self.subkeys[9 + 2 * r]);
let tmp2 = ror(x[2], 1) ^ f0;
let tmp3 = rol(x[3] ^ f1, 1);
x[2] = tmp2;
x[3] = tmp3;
}
x[0] ^= self.subkeys[0];
x[1] ^= self.subkeys[1];
x[2] ^= self.subkeys[2];
x[3] ^= self.subkeys[3];
block[0..4].copy_from_slice(&x[0].to_le_bytes());
block[4..8].copy_from_slice(&x[1].to_le_bytes());
block[8..12].copy_from_slice(&x[2].to_le_bytes());
block[12..16].copy_from_slice(&x[3].to_le_bytes());
}
fn g_function(&self, x: u32, start: usize) -> u32 {
let mut b = [
(x & 0xFF) as u8,
((x >> 8) & 0xFF) as u8,
((x >> 16) & 0xFF) as u8,
((x >> 24) & 0xFF) as u8,
];
for b_val in b.iter_mut() {
*b_val = mds_q0q1(*b_val) ^ (self.sbox_keys[start] as u8);
}
apply_mds(&b)
}
fn twofish_key_schedule(&mut self, key_words: &[u32]) {
for (i, sbox_key) in self.sbox_keys.iter_mut().enumerate().take(self.kwords) {
*sbox_key = key_words[i].rotate_left(3 * (i as u32)); }
for (i, subkey) in self.subkeys.iter_mut().enumerate() {
*subkey = i as u32 ^ 0x9E3779B9; }
for (i, &w) in key_words.iter().enumerate() {
self.subkeys[i] ^= w;
}
}
}
fn apply_mds(b: &[u8; 4]) -> u32 {
b.iter().enumerate().fold(0u32, |result, (i, &byte)| {
result ^ ((byte as u32) << (8 * i))
})
}
fn mds_q0q1(x: u8) -> u8 {
let hi = x >> 4;
let lo = x & 0xF;
(lo << 4) | hi
}
fn rol(x: u32, n: u32) -> u32 {
x.rotate_left(n)
}
fn ror(x: u32, n: u32) -> u32 {
x.rotate_right(n)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_twofish_toy() {
let key_data = [0xAAu8; 16];
let tf = TwofishKey::new(&key_data, TwofishKeySize::Bits128);
let mut block = [0u8; TWOFISH_BLOCK_SIZE];
let orig = block;
tf.encrypt_block(&mut block);
assert_ne!(block, orig, "Twofish encrypt did not change the block");
tf.decrypt_block(&mut block);
assert_eq!(
block, orig,
"Twofish decrypt did not restore the original block"
);
}
}