diff --git a/src/crypto/bb.rs b/src/crypto/bb.rs index e69de29..dc14d7a 100644 --- a/src/crypto/bb.rs +++ b/src/crypto/bb.rs @@ -0,0 +1,196 @@ +use crate::crypto::{PSOCipher, CipherError}; +use std::num::Wrapping as W; + +const BB_SBOX_COUNT: usize = 4; +const BB_SBOX_SIZE: usize = 256; +const BB_PARRAY_SIZE: usize = 18; +const BB_SEED_SIZE: usize = 48; + +pub struct PSOBBCipher { + p_array: [u32; BB_PARRAY_SIZE], + sbox: [[u32; BB_SBOX_SIZE]; BB_SBOX_COUNT], +} + +impl PSOBBCipher { + fn f(&self, x: u32) -> u32 { + let mut k = W(self.sbox[0][((x >> 24) & 0xFF) as usize]); + k += W(self.sbox[1][((x >> 16) & 0xFF) as usize]); + k ^= W(self.sbox[2][((x >> 8) & 0xFF) as usize]); + k += W(self.sbox[3][((x) & 0xFF) as usize]); + k.0 + } + + fn init_block(&self, mut l: u32, mut r: u32) -> (u32, u32) { + for i in (0..16).step_by(2) { + l ^= self.p_array[i]; + r ^= self.f(l); + r ^= self.p_array[i+1]; + l ^= self.f(r); + } + l ^= self.p_array[16]; + r ^= self.p_array[17]; + + (r, l) + } + + pub fn new(p: [u32; BB_PARRAY_SIZE], s: [[u32; BB_SBOX_SIZE]; BB_SBOX_COUNT], mut seed: [u8; BB_SEED_SIZE]) -> PSOBBCipher { + for chunk in seed.chunks_mut(3) { + chunk[0] ^= 0x19; + chunk[1] ^= 0x16; + chunk[2] ^= 0x18; + } + + let mut cipher = PSOBBCipher { + p_array: p, + sbox: s + }; + + for k in cipher.p_array.iter_mut() { + let mut pt = *k as u16; + pt = ((pt & 0x00FF) << 8) + ((pt & 0xFF00) >> 8); + *k = ((((*k >> 16) ^ pt as u32) << 16)) + pt as u32; + } + + for i in 0..18 { + let k = u32::from_le_bytes([seed[(i * 4 + 3) % BB_SEED_SIZE], + seed[(i * 4 + 2) % BB_SEED_SIZE], + seed[(i * 4 + 1) % BB_SEED_SIZE], + seed[(i * 4) % BB_SEED_SIZE]]); + cipher.p_array[i] ^= k; + } + + let mut l = 0; + let mut r = 0; + for k in (0..BB_PARRAY_SIZE).step_by(2) { + // pls rust let me destructure without messing up scope + let tmp = cipher.init_block(l, r); + l = tmp.0; + r = tmp.1; + cipher.p_array[k] = l; + cipher.p_array[k + 1] = r; + } + + for block in 0..BB_SBOX_COUNT { + for item in (0..BB_SBOX_SIZE).step_by(2) { + let tmp = cipher.init_block(l, r); + l = tmp.0; + r = tmp.1; + cipher.sbox[block][item] = l; + cipher.sbox[block][item + 1] = r; + } + } + + cipher + } +} + +impl PSOCipher for PSOBBCipher { + fn encrypt(&mut self, data: &Vec) -> Result, CipherError> { + if data.len() % 8 != 0 { + return Err(CipherError::InvalidSize); + } + + let real_data = data.chunks(4).map(|k| { + u32::from_le_bytes([k[0], k[1], k[2], k[3]]) + }).collect::>(); + + let mut result = Vec::new(); + for d in real_data.chunks(2) { + let mut l = d[0]; + let mut r = d[1]; + for i in (0..4).step_by(2) { + l ^= self.p_array[i]; + r ^= self.f(l); + r ^= self.p_array[i + 1]; + l ^= self.f(r); + } + l ^= self.p_array[4]; + r ^= self.p_array[5]; + + let tmp = l; + l = r; + r = tmp; + + result.extend_from_slice(&l.to_le_bytes()); + result.extend_from_slice(&r.to_le_bytes()); + } + + + Ok(result) + } + + fn decrypt(&mut self, data: &Vec) -> Result, CipherError> { + if data.len() % 8 != 0 { + return Err(CipherError::InvalidSize); + } + + let real_data = data.chunks(4).map(|k| { + u32::from_le_bytes([k[0], k[1], k[2], k[3]]) + }).collect::>(); + + let mut result = Vec::new(); + for d in real_data.chunks(2) { + let mut l = d[0]; + let mut r = d[1]; + for i in (1..=4).rev().step_by(2) { + l ^= self.p_array[i + 1]; + r ^= self.f(l); + r ^= self.p_array[i]; + l ^= self.f(r); + } + l ^= self.p_array[1]; + r ^= self.p_array[0]; + + let tmp = l; + l = r; + r = tmp; + + result.extend_from_slice(&l.to_le_bytes()); + result.extend_from_slice(&r.to_le_bytes()); + } + + Ok(result) + } + + fn header_size(&self) -> usize { + 8 + } +} + + +#[cfg(test)] +mod tests { + #[test] + fn test_crypto() { + use rand::{Rng, RngCore}; + use super::{PSOCipher, PSOBBCipher}; + + let mut rng = rand::thread_rng(); + + let mut p = [0u32; 18]; + let mut s = [[0u32; super::BB_SBOX_SIZE]; super::BB_SBOX_COUNT]; + let mut seed = [0u8; super::BB_SEED_SIZE]; + + rng.fill(&mut p[..]); + rng.fill(&mut s[0][..]); + rng.fill(&mut s[1][..]); + rng.fill(&mut s[2][..]); + rng.fill(&mut s[3][..]); + rng.fill(&mut seed[..]); + + let mut cipher_in = PSOBBCipher::new(p, s, seed); + let mut cipher_out = PSOBBCipher::new(p, s, seed); + + for _ in 0..50 { + let len = (rng.gen::() / 8) * 8; + + let mut random_junk = vec![0u8; len as usize]; + rng.fill_bytes(&mut random_junk); + + let enc_data = cipher_in.encrypt(&random_junk).unwrap(); + let orig_data = cipher_out.decrypt(&enc_data).unwrap(); + + assert!(random_junk == orig_data); + } + } +} diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index e3f1390..fa422be 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -1,4 +1,5 @@ pub mod pc; +pub mod bb; #[derive(Debug)]