use core::sync::atomic::{AtomicU64, Ordering};
pub fn apply_mask(data: &mut [u8], key: [u8; 4]) {
for (i, b) in data.iter_mut().enumerate() {
*b ^= key[i & 0x3];
}
}
#[must_use]
pub fn generate_masking_key() -> [u8; 4] {
static COUNTER: AtomicU64 = AtomicU64::new(0xCAFE_F00D_DEAD_BEEF);
let n = COUNTER.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::SeqCst);
let mut x = n;
x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
x ^= x >> 31;
let bytes = x.to_le_bytes();
[bytes[0], bytes[1], bytes[2], bytes[3]]
}
pub trait MaskingKeyProvider {
fn next_key(&mut self) -> [u8; 4];
}
#[derive(Debug, Default)]
pub struct InsecureSplitmixProvider;
impl MaskingKeyProvider for InsecureSplitmixProvider {
fn next_key(&mut self) -> [u8; 4] {
generate_masking_key()
}
}
pub struct ClosureMaskingKeyProvider<F: FnMut() -> [u8; 4]>(pub F);
impl<F: FnMut() -> [u8; 4]> MaskingKeyProvider for ClosureMaskingKeyProvider<F> {
fn next_key(&mut self) -> [u8; 4] {
(self.0)()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_mask_is_symmetric() {
let key = [0x01, 0x02, 0x03, 0x04];
let original: alloc::vec::Vec<u8> = (0..16).collect();
let mut masked = original.clone();
apply_mask(&mut masked, key);
assert_ne!(masked, original);
apply_mask(&mut masked, key);
assert_eq!(masked, original);
}
#[test]
fn apply_mask_xors_with_key_modulo_4() {
let key = [0xFF, 0x00, 0xFF, 0x00];
let mut data = [0xAA; 8];
apply_mask(&mut data, key);
assert_eq!(data, [0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA]);
}
#[test]
fn apply_mask_handles_partial_key_alignment() {
let key = [0x01, 0x02, 0x03, 0x04];
let mut data = [0x10, 0x20, 0x30, 0x40, 0x50];
apply_mask(&mut data, key);
assert_eq!(data[0], 0x10 ^ 0x01);
assert_eq!(data[1], 0x20 ^ 0x02);
assert_eq!(data[2], 0x30 ^ 0x03);
assert_eq!(data[3], 0x40 ^ 0x04);
assert_eq!(data[4], 0x50 ^ 0x01);
}
#[test]
fn empty_payload_is_unchanged() {
let mut data: alloc::vec::Vec<u8> = alloc::vec::Vec::new();
apply_mask(&mut data, [1, 2, 3, 4]);
assert!(data.is_empty());
}
#[test]
fn generate_masking_key_returns_4_bytes() {
let k = generate_masking_key();
assert_eq!(k.len(), 4);
}
#[test]
fn generate_masking_key_returns_distinct_values_across_calls() {
let k1 = generate_masking_key();
let k2 = generate_masking_key();
assert_ne!(k1, k2);
}
#[test]
fn insecure_splitmix_provider_implements_trait() {
let mut p = InsecureSplitmixProvider;
let k = p.next_key();
assert_eq!(k.len(), 4);
}
#[test]
fn closure_provider_calls_user_supplied_fn() {
let mut p = ClosureMaskingKeyProvider(|| [9, 9, 9, 9]);
assert_eq!(p.next_key(), [9, 9, 9, 9]);
}
}