boo-rs 0.1.3

Encrypt primitives types at compile time
Documentation
//! # Warning
//!
//! This module is imported as is in the proc macro.
//! It must be standalone (no dependencies) and support `std` and `no_std` environments.

/// XORs each byte of `source` against the wrapped `key` and a per-call-site salt.
///
/// # Arguments
///
/// * `source` - buffer to XOR in place.
/// * `key` - process-wide key; an empty key is a no-op.
/// * `site_salt` - confines a leaked key's blast radius to the one `boo!()` call site it came from.
pub const fn xor(source: &mut [u8], key: &[u8], site_salt: u64) {
    if !key.is_empty() {
        let mut i = 0;
        while i < source.len() {
            let salted = mix(site_salt, i as u64) as u8;
            source[i] = source[i] ^ key[i % key.len()].wrapping_add(i as u8) ^ salted;
            i += 1;
        }
    }
}

/// SplitMix64's finalizer: dependency-free integer mixer, `no_std`-compatible.
const fn mix(mut x: u64, i: u64) -> u64 {
    x = x.wrapping_add(i).wrapping_add(0x9E37_79B9_7F4A_7C15);
    x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
    x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
    x ^ (x >> 31)
}

/// Non-cryptographic checksum of `bytes`, folding each byte through [`mix`]'s finalizer.
///
/// Detects a direct edit to the exact bytes checksummed; not a cryptographic integrity
/// guarantee, and gives no resistance against an attacker who recomputes and patches this
/// checksum alongside the bytes it covers.
pub const fn checksum(bytes: &[u8]) -> u64 {
    let mut acc = bytes.len() as u64;
    let mut i = 0;
    while i < bytes.len() {
        acc = mix(acc ^ bytes[i] as u64, i as u64);
        i += 1;
    }
    acc
}

/// Splits a `[u8; LEN]` array into two owned arrays `[u8; LEFT]` and `[u8; RIGHT]` without allocating.
#[allow(unused)]
#[inline(always)]
pub const fn split_array<const LEN: usize, const LEFT: usize, const RIGHT: usize>(
    mut arr: [u8; LEN],
) -> ([u8; LEFT], [u8; RIGHT]) {
    // Compile time panic, will be absent at runtime
    const {
        if LEN != LEFT + RIGHT {
            panic!("LEN must be exactly LEFT + RIGHT")
        }
    }

    let ptr = arr.as_mut_ptr();

    // Semantic way is to use `ManuallyDrop` with `deref`, but it is not `const` !
    let _arr = ::core::mem::ManuallyDrop::new(arr);

    // Safety: we assert `LEN == LEFT + RIGHT` above

    let left_ptr = ptr as *mut [u8; LEFT];
    let right_ptr = unsafe { ptr.add(LEFT) } as *mut [u8; RIGHT];

    unsafe { (left_ptr.read(), right_ptr.read()) }
}