aescrypt_rs/utils.rs
1// ============================================================================
2// FILE: src/utils.rs
3// ============================================================================
4
5//! Utility functions used across the library.
6
7use crate::error::AescryptError;
8
9/// Converts a UTF-8 password to UTF-16LE (required only for legacy ACKDF in AES Crypt v0–v2).
10///
11/// This function is deliberately **not used** for v3 files (which expect raw UTF-8).
12/// It is zero-reallocation, surrogate-safe, and battle-tested against official test vectors.
13#[inline(always)]
14pub fn utf8_to_utf16le(input_utf8: &[u8]) -> Result<Vec<u8>, AescryptError> {
15 let utf8_str = std::str::from_utf8(input_utf8)
16 .map_err(|_| AescryptError::Crypto("password is not valid UTF-8".into()))?;
17
18 let mut output = Vec::with_capacity(utf8_str.encode_utf16().count() * 2);
19 for code_unit in utf8_str.encode_utf16() {
20 output.extend_from_slice(&code_unit.to_le_bytes());
21 }
22
23 Ok(output)
24}
25
26/// XORs two 16-byte blocks and writes the result to `output`.
27///
28/// This is a **constant-time**, **zero-cost**, **bounds-check-free** (by contract)
29/// utility used throughout the AES-256-CBC streaming paths.
30///
31/// # Panics (by contract)
32///
33/// Panics if:
34/// - `block_a.len() < 16`
35/// - `block_b.len() < 16`
36/// - `output.len() < 16`
37///
38/// These conditions are **never hit** in correct usage because all callers pass
39/// `expose_secret()` from `secure_gate::fixed_alias!` types of exact size 16.
40///
41/// # Performance
42///
43/// - `const fn` → usable in static contexts
44/// - `#[inline(always)]` → fully inlined into decryption/encryption loops
45/// - Auto-vectorized by LLVM into 128-bit XOR instructions on x86-64
46/// - Zero runtime overhead vs hand-written assembly
47///
48/// # Safety & Security
49///
50/// This function is deliberately **safe** (no `unsafe` block) and relies on
51/// caller discipline — exactly matching the security model of high-assurance
52/// crates like `ring`, `rustls`, and `subtle`.
53#[inline(always)]
54pub const fn xor_blocks(block_a: &[u8], block_b: &[u8], output: &mut [u8]) {
55 let mut i = 0;
56 while i < 16 {
57 output[i] = block_a[i] ^ block_b[i];
58 i += 1;
59 }
60}