use super::{AES_ROUNDS, AES_BLOCK_SIZE};
#[allow(dead_code)] pub const WHITEBOX_TABLE_SIZE: usize = 555_000;
pub type TBox = [[[u8; 256]; AES_BLOCK_SIZE]; AES_ROUNDS];
pub type TyBox = [[[u32; 256]; AES_BLOCK_SIZE]; 9];
pub type XorTable = [[[[u8; 16]; 16]; 96]; 9];
pub type MBLTable = [[[u32; 256]; AES_BLOCK_SIZE]; 9];
#[derive(Clone)]
pub struct WhiteboxTables {
pub tbox: Box<TBox>,
pub tybox: Box<TyBox>,
pub xor_tables: Box<XorTable>,
pub mbl: Box<MBLTable>,
pub tbox_last: [[u8; 256]; AES_BLOCK_SIZE],
#[allow(dead_code)] pub input_encoding: Option<Box<[[u8; 256]; AES_BLOCK_SIZE]>>,
#[allow(dead_code)] pub output_encoding_inv: Option<Box<[[u8; 256]; AES_BLOCK_SIZE]>>,
}
impl Default for WhiteboxTables {
fn default() -> Self {
Self::new()
}
}
impl WhiteboxTables {
pub fn new() -> Self {
Self {
tbox: Box::new([[[0u8; 256]; AES_BLOCK_SIZE]; AES_ROUNDS]),
tybox: Box::new([[[0u32; 256]; AES_BLOCK_SIZE]; 9]),
xor_tables: Box::new([[[[0u8; 16]; 16]; 96]; 9]),
mbl: Box::new([[[0u32; 256]; AES_BLOCK_SIZE]; 9]),
tbox_last: [[0u8; 256]; AES_BLOCK_SIZE],
input_encoding: None,
output_encoding_inv: None,
}
}
#[allow(dead_code)] pub fn memory_size(&self) -> usize {
let base = core::mem::size_of::<TBox>()
+ core::mem::size_of::<TyBox>()
+ core::mem::size_of::<XorTable>()
+ core::mem::size_of::<MBLTable>()
+ core::mem::size_of::<[[u8; 256]; AES_BLOCK_SIZE]>();
let encoding_size = self.input_encoding.as_ref().map(|_| 4096).unwrap_or(0)
+ self.output_encoding_inv.as_ref().map(|_| 4096).unwrap_or(0);
base + encoding_size
}
}
#[allow(dead_code)] #[derive(Clone)]
pub struct WhiteboxTablesLite {
pub tbox: Box<TBox>,
pub tbox_last: [[u8; 256]; AES_BLOCK_SIZE],
}
impl Default for WhiteboxTablesLite {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)] impl WhiteboxTablesLite {
pub fn new() -> Self {
Self {
tbox: Box::new([[[0u8; 256]; AES_BLOCK_SIZE]; AES_ROUNDS]),
tbox_last: [[0u8; 256]; AES_BLOCK_SIZE],
}
}
pub fn memory_size(&self) -> usize {
core::mem::size_of::<TBox>()
+ core::mem::size_of::<[[u8; 256]; AES_BLOCK_SIZE]>()
}
}
#[derive(Clone, Copy)]
pub struct Bijection8 {
pub forward: [u8; 256],
pub inverse: [u8; 256],
}
impl Bijection8 {
pub fn identity() -> Self {
let mut forward = [0u8; 256];
let mut inverse = [0u8; 256];
for i in 0..256 {
forward[i] = i as u8;
inverse[i] = i as u8;
}
Self { forward, inverse }
}
#[inline]
pub fn encode(&self, x: u8) -> u8 {
self.forward[x as usize]
}
#[inline]
pub fn decode(&self, x: u8) -> u8 {
self.inverse[x as usize]
}
}
#[derive(Clone, Copy)]
pub struct Bijection4 {
pub forward: [u8; 16],
pub inverse: [u8; 16],
}
impl Bijection4 {
pub fn identity() -> Self {
let mut forward = [0u8; 16];
let mut inverse = [0u8; 16];
for i in 0..16 {
forward[i] = i as u8;
inverse[i] = i as u8;
}
Self { forward, inverse }
}
#[inline]
pub fn encode(&self, x: u8) -> u8 {
self.forward[(x & 0x0f) as usize]
}
#[inline]
pub fn decode(&self, x: u8) -> u8 {
self.inverse[(x & 0x0f) as usize]
}
}
#[derive(Clone)]
pub struct MixingBijection32 {
pub matrix: [[u8; 32]; 32],
pub inverse: [[u8; 32]; 32],
}
impl Default for MixingBijection32 {
fn default() -> Self {
Self::identity()
}
}
impl MixingBijection32 {
pub fn identity() -> Self {
let mut matrix = [[0u8; 32]; 32];
let mut inverse = [[0u8; 32]; 32];
for i in 0..32 {
matrix[i][i] = 1;
inverse[i][i] = 1;
}
Self { matrix, inverse }
}
pub fn apply(&self, input: u32) -> u32 {
let mut result = 0u32;
for i in 0..32 {
let mut bit = 0u8;
for j in 0..32 {
if self.matrix[i][j] != 0 && (input >> j) & 1 != 0 {
bit ^= 1;
}
}
result |= (bit as u32) << i;
}
result
}
pub fn apply_inverse(&self, input: u32) -> u32 {
let mut result = 0u32;
for i in 0..32 {
let mut bit = 0u8;
for j in 0..32 {
if self.inverse[i][j] != 0 && (input >> j) & 1 != 0 {
bit ^= 1;
}
}
result |= (bit as u32) << i;
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bijection8_identity() {
let bij = Bijection8::identity();
for i in 0u8..=255 {
assert_eq!(bij.encode(i), i);
assert_eq!(bij.decode(i), i);
}
}
#[test]
fn test_bijection4_identity() {
let bij = Bijection4::identity();
for i in 0u8..16 {
assert_eq!(bij.encode(i), i);
assert_eq!(bij.decode(i), i);
}
}
#[test]
fn test_mixing_bijection_identity() {
let mb = MixingBijection32::identity();
assert_eq!(mb.apply(0x12345678), 0x12345678);
assert_eq!(mb.apply_inverse(0xdeadbeef), 0xdeadbeef);
}
#[test]
fn test_whitebox_tables_memory() {
let tables = WhiteboxTables::new();
let size = tables.memory_size();
assert!(size > 500_000);
assert!(size < 600_000);
}
#[test]
fn test_whitebox_tables_lite_memory() {
let tables = WhiteboxTablesLite::new();
let size = tables.memory_size();
assert!(size > 40_000);
assert!(size < 50_000);
}
}