mod sbox;
mod tables;
mod generator;
mod cipher;
pub use tables::{WhiteboxTables, WhiteboxTablesLite, WHITEBOX_TABLE_SIZE};
pub use cipher::{whitebox_encrypt, whitebox_decrypt, whitebox_encrypt_lite};
pub use generator::{generate_tables, generate_tables_lite};
pub const AES_BLOCK_SIZE: usize = 16;
pub const AES_ROUNDS: usize = 10;
pub const WB_KEY_SIZE: usize = 16;
pub fn init_tables() -> WhiteboxTables {
use crate::build_config::whitebox_config::{
reconstruct_tbox, reconstruct_tybox, reconstruct_xor_tables,
reconstruct_mbl, reconstruct_tbox_last
};
WhiteboxTables {
tbox: reconstruct_tbox(),
tybox: reconstruct_tybox(),
xor_tables: reconstruct_xor_tables(),
mbl: reconstruct_mbl(),
tbox_last: reconstruct_tbox_last(),
input_encoding: None,
output_encoding_inv: None,
}
}
pub fn init_tables_lite() -> WhiteboxTablesLite {
use crate::build_config::whitebox_config::reconstruct_tbox;
let full_tbox = reconstruct_tbox();
let mut tbox_last = [[0u8; 256]; AES_BLOCK_SIZE];
for (pos, tbox_pos) in tbox_last.iter_mut().enumerate() {
tbox_pos.copy_from_slice(&full_tbox[AES_ROUNDS - 1][pos]);
}
WhiteboxTablesLite {
tbox: full_tbox,
tbox_last,
}
}
pub fn encrypt_block(block: &mut [u8; AES_BLOCK_SIZE]) {
let tables = init_tables();
whitebox_encrypt(block, &tables);
}
pub fn encrypt_blocks(blocks: &mut [[u8; AES_BLOCK_SIZE]]) {
let tables = init_tables();
for block in blocks.iter_mut() {
whitebox_encrypt(block, &tables);
}
}
pub fn derive_key_from_hash(domain_hash: &[u8; 32]) -> [u8; 32] {
let tables = init_tables();
derive_key_from_hash_with_tables(domain_hash, &tables)
}
pub fn derive_key_from_hash_with_tables(domain_hash: &[u8; 32], tables: &WhiteboxTables) -> [u8; 32] {
let mut block1 = [0u8; AES_BLOCK_SIZE];
let mut block2 = [0u8; AES_BLOCK_SIZE];
block1.copy_from_slice(&domain_hash[0..16]);
block2.copy_from_slice(&domain_hash[16..32]);
whitebox_encrypt(&mut block1, tables);
whitebox_encrypt(&mut block2, tables);
let mut key = [0u8; 32];
key[0..16].copy_from_slice(&block1);
key[16..32].copy_from_slice(&block2);
key
}
#[deprecated(note = "Use derive_key_from_hash for security - domain strings are visible in binary")]
pub fn derive_key_with_tables(domain: &[u8], tables: &WhiteboxTables) -> [u8; 32] {
let mut hash1 = 0xcbf29ce484222325u64;
let mut hash2 = 0x84222325cbf29ce4u64;
for &byte in domain {
hash1 ^= byte as u64;
hash1 = hash1.wrapping_mul(0x100000001b3);
hash2 = hash2.wrapping_mul(0x100000001b3);
hash2 ^= byte as u64;
}
let hash3 = hash1.rotate_left(13) ^ hash2;
let hash4 = hash2.rotate_right(17) ^ hash1;
let mut domain_hash = [0u8; 32];
domain_hash[0..8].copy_from_slice(&hash1.to_le_bytes());
domain_hash[8..16].copy_from_slice(&hash2.to_le_bytes());
domain_hash[16..24].copy_from_slice(&hash3.to_le_bytes());
domain_hash[24..32].copy_from_slice(&hash4.to_le_bytes());
derive_key_from_hash_with_tables(&domain_hash, tables)
}
pub fn derive_bytecode_key() -> [u8; 32] {
use crate::build_config::whitebox_config::get_bytecode_domain_hash;
let domain_hash = get_bytecode_domain_hash();
derive_key_from_hash(&domain_hash)
}
pub fn derive_smc_key() -> [u8; 32] {
use crate::build_config::whitebox_config::get_smc_domain_hash;
let domain_hash = get_smc_domain_hash();
derive_key_from_hash(&domain_hash)
}
pub fn derive_nonce(counter: u64) -> [u8; 12] {
use crate::build_config::whitebox_config::get_nonce_domain_hash;
let tables = init_tables();
let nonce_hash = get_nonce_domain_hash();
let mut block = [0u8; AES_BLOCK_SIZE];
block[0..8].copy_from_slice(&counter.to_le_bytes());
block[8..16].copy_from_slice(&nonce_hash[0..8]);
whitebox_encrypt(&mut block, &tables);
let mut nonce = [0u8; 12];
nonce.copy_from_slice(&block[0..12]);
nonce
}
pub struct WhiteboxCryptoContext {
tables: WhiteboxTables,
bytecode_key: [u8; 32],
smc_key: [u8; 32],
nonce_counter: u64,
}
impl Default for WhiteboxCryptoContext {
fn default() -> Self {
Self::new()
}
}
impl WhiteboxCryptoContext {
pub fn new() -> Self {
use crate::build_config::whitebox_config::{
get_bytecode_domain_hash, get_smc_domain_hash
};
let tables = init_tables();
let bytecode_hash = get_bytecode_domain_hash();
let smc_hash = get_smc_domain_hash();
let bytecode_key = derive_key_from_hash_with_tables(&bytecode_hash, &tables);
let smc_key = derive_key_from_hash_with_tables(&smc_hash, &tables);
Self {
tables,
bytecode_key,
smc_key,
nonce_counter: 0,
}
}
pub fn bytecode_key(&self) -> &[u8; 32] {
&self.bytecode_key
}
pub fn smc_key(&self) -> &[u8; 32] {
&self.smc_key
}
pub fn next_nonce(&mut self) -> [u8; 12] {
use crate::build_config::whitebox_config::get_nonce_domain_hash;
let nonce_hash = get_nonce_domain_hash();
let mut block = [0u8; AES_BLOCK_SIZE];
block[0..8].copy_from_slice(&self.nonce_counter.to_le_bytes());
block[8..16].copy_from_slice(&nonce_hash[0..8]);
self.nonce_counter += 1;
whitebox_encrypt(&mut block, &self.tables);
let mut nonce = [0u8; 12];
nonce.copy_from_slice(&block[0..12]);
nonce
}
#[allow(deprecated)]
pub fn derive_custom_key(&self, domain: &[u8]) -> [u8; 32] {
derive_key_with_tables(domain, &self.tables)
}
pub fn wbc_encrypt(&self, block: &mut [u8; AES_BLOCK_SIZE]) {
whitebox_encrypt(block, &self.tables);
}
}