use crate::secure::crypto::aes128_encrypt;
use crate::secure::pad::pad_mac;
use alloc::vec::Vec;
#[inline]
fn xor_in_place(dst: &mut [u8; 16], src: &[u8]) {
for (d, s) in dst.iter_mut().zip(src.iter()) {
*d ^= *s;
}
}
pub fn cbc_mac(input: &[u8], icv: &[u8; 16], s_mac1: &[u8; 16], s_mac2: &[u8; 16]) -> [u8; 16] {
let mut padded = Vec::with_capacity(input.len() + 16);
padded.extend_from_slice(input);
pad_mac(&mut padded);
debug_assert_eq!(padded.len() % 16, 0);
debug_assert!(!padded.is_empty());
let mut state = *icv;
let blocks = padded.len() / 16;
for (i, chunk) in padded.chunks_exact(16).enumerate() {
xor_in_place(&mut state, chunk);
let key = if i + 1 == blocks { s_mac2 } else { s_mac1 };
state = aes128_encrypt(key, &state);
}
state
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn deterministic() {
let icv = [0u8; 16];
let s1 = [1u8; 16];
let s2 = [2u8; 16];
let m1 = cbc_mac(&[1, 2, 3], &icv, &s1, &s2);
let m2 = cbc_mac(&[1, 2, 3], &icv, &s1, &s2);
assert_eq!(m1, m2);
}
#[test]
fn distinct_inputs_distinct_macs() {
let icv = [0u8; 16];
let s1 = [1u8; 16];
let s2 = [2u8; 16];
let m1 = cbc_mac(&[1], &icv, &s1, &s2);
let m2 = cbc_mac(&[2], &icv, &s1, &s2);
assert_ne!(m1, m2);
}
#[test]
fn icv_matters() {
let s1 = [1u8; 16];
let s2 = [2u8; 16];
let m1 = cbc_mac(&[7, 8, 9], &[0u8; 16], &s1, &s2);
let m2 = cbc_mac(&[7, 8, 9], &[1u8; 16], &s1, &s2);
assert_ne!(m1, m2);
}
#[test]
fn key_swap_observable() {
let icv = [0u8; 16];
let s1 = [1u8; 16];
let s2_a = [2u8; 16];
let s2_b = [3u8; 16];
let input = [0u8; 32];
let m1 = cbc_mac(&input, &icv, &s1, &s2_a);
let m2 = cbc_mac(&input, &icv, &s1, &s2_b);
assert_ne!(m1, m2);
}
}