use core::convert::TryInto;
use std::mem;
pub const BLOWFISH_BLOCK_SIZE: usize = 8;
pub const PARRAY_SIZE: usize = 18;
pub const SBOX_COUNT: usize = 4;
pub const SBOX_ENTRIES: usize = 256;
pub const BLOWFISH_MAX_KEY_BYTES: usize = 56;
pub struct BlowfishKey {
p: [u32; PARRAY_SIZE],
s: [[u32; SBOX_ENTRIES]; SBOX_COUNT],
}
impl BlowfishKey {
pub fn new(key_data: &[u8]) -> Self {
if key_data.is_empty() || key_data.len() > BLOWFISH_MAX_KEY_BYTES {
panic!("Invalid Blowfish key length (must be 1..=56 bytes)");
}
let mut key = BlowfishKey {
p: DEFAULT_P,
s: DEFAULT_SBOX,
};
key.key_schedule(key_data);
key
}
pub fn encrypt_block(&self, block: &mut [u8; BLOWFISH_BLOCK_SIZE]) {
let (left, right) = block.split_at_mut(4);
let mut xl = u32::from_be_bytes(left.try_into().unwrap());
let mut xr = u32::from_be_bytes(right.try_into().unwrap());
for i in 0..16 {
xl ^= self.p[i];
xr ^= f_function(xl, &self.s);
mem::swap(&mut xl, &mut xr);
}
mem::swap(&mut xl, &mut xr);
xr ^= self.p[16];
xl ^= self.p[17];
left.copy_from_slice(&xl.to_be_bytes());
right.copy_from_slice(&xr.to_be_bytes());
}
pub fn decrypt_block(&self, block: &mut [u8; BLOWFISH_BLOCK_SIZE]) {
let (left, right) = block.split_at_mut(4);
let mut xl = u32::from_be_bytes(left.try_into().unwrap());
let mut xr = u32::from_be_bytes(right.try_into().unwrap());
for i in (2..=17).rev() {
xl ^= self.p[i];
xr ^= f_function(xl, &self.s);
mem::swap(&mut xl, &mut xr);
}
mem::swap(&mut xl, &mut xr);
xr ^= self.p[1];
xl ^= self.p[0];
left.copy_from_slice(&xl.to_be_bytes());
right.copy_from_slice(&xr.to_be_bytes());
}
fn key_schedule(&mut self, key_data: &[u8]) {
let key_len = key_data.len();
let mut data: usize = 0;
for i in 0..PARRAY_SIZE {
let mut val: u32 = 0;
for _ in 0..4 {
val = (val << 8) | (key_data[data % key_len] as u32);
data += 1;
}
self.p[i] ^= val;
}
let mut block = [0u8; 8];
for i in (0..PARRAY_SIZE).step_by(2) {
self.encrypt_block(&mut block);
self.p[i] = u32::from_be_bytes(block[0..4].try_into().unwrap());
self.p[i + 1] = u32::from_be_bytes(block[4..8].try_into().unwrap());
}
for sbox_i in 0..SBOX_COUNT {
for sbox_j in (0..SBOX_ENTRIES).step_by(2) {
self.encrypt_block(&mut block);
self.s[sbox_i][sbox_j] = u32::from_be_bytes(block[0..4].try_into().unwrap());
self.s[sbox_i][sbox_j + 1] = u32::from_be_bytes(block[4..8].try_into().unwrap());
}
}
}
}
fn f_function(x: u32, s: &[[u32; 256]; 4]) -> u32 {
let a = (x >> 24) as u8;
let b = ((x >> 16) & 0xFF) as u8;
let c = ((x >> 8) & 0xFF) as u8;
let d = (x & 0xFF) as u8;
let mut y = s[0][a as usize].wrapping_add(s[1][b as usize]);
y ^= s[2][c as usize];
y = y.wrapping_add(s[3][d as usize]);
y
}
static DEFAULT_P: [u32; PARRAY_SIZE] = [
0x243F6A88, 0x85A308D3, 0x13198A2E, 0x03707344, 0xA4093822, 0x299F31D0, 0x082EFA98, 0xEC4E6C89,
0x452821E6, 0x38D01377, 0xBE5466CF, 0x34E90C6C, 0xC0AC29B7, 0xC97C50DD, 0x3F84D5B5, 0xB5470917,
0x9216D5D9, 0x8979FB1B,
];
static DEFAULT_SBOX: [[u32; 256]; 4] = [
{
let mut s = [0u32; 256];
s[0] = 0xd1310ba6;
s[1] = 0x98dfb5ac;
s[2] = 0x2ffd72db;
s[3] = 0xd01adfb7;
s
},
[0u32; 256],
[0u32; 256],
[0u32; 256],
];
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blowfish_basic() {
let key_data = b"mytestkey";
let bf = BlowfishKey::new(key_data);
let mut block = [0u8; BLOWFISH_BLOCK_SIZE];
block.copy_from_slice(b"12345678");
let orig = block;
bf.encrypt_block(&mut block);
assert_ne!(block, orig);
bf.decrypt_block(&mut block);
assert_eq!(
block, orig,
"Blowfish decrypt did not restore the original block"
);
}
}