use crate::error::SecureSessionError;
use crate::secure::pad::{pad_data, unpad_data};
use aes::cipher::generic_array::GenericArray;
use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use alloc::vec::Vec;
type Encryptor = cbc::Encryptor<aes::Aes128>;
type Decryptor = cbc::Decryptor<aes::Aes128>;
#[inline]
pub fn complement_icv(other_mac: &[u8; 16]) -> [u8; 16] {
core::array::from_fn(|i| !other_mac[i])
}
pub fn encrypt_data(s_enc: &[u8; 16], iv: &[u8; 16], data: &[u8]) -> Vec<u8> {
let mut buf = Vec::with_capacity(data.len() + 16);
buf.extend_from_slice(data);
pad_data(&mut buf);
debug_assert_eq!(buf.len() % 16, 0);
let mut enc = Encryptor::new(
GenericArray::from_slice(s_enc),
GenericArray::from_slice(iv),
);
for chunk in buf.chunks_exact_mut(16) {
let mut block = *GenericArray::from_slice(chunk);
enc.encrypt_block_mut(&mut block);
chunk.copy_from_slice(&block);
}
buf
}
pub fn decrypt_data(
s_enc: &[u8; 16],
iv: &[u8; 16],
ct: &[u8],
) -> Result<Vec<u8>, SecureSessionError> {
if ct.is_empty() || ct.len() % 16 != 0 {
return Err(SecureSessionError::BadPadding);
}
let mut buf = ct.to_vec();
let mut dec = Decryptor::new(
GenericArray::from_slice(s_enc),
GenericArray::from_slice(iv),
);
for chunk in buf.chunks_exact_mut(16) {
let mut block = *GenericArray::from_slice(chunk);
dec.decrypt_block_mut(&mut block);
chunk.copy_from_slice(&block);
}
let stripped = unpad_data(&buf)?;
Ok(stripped.to_vec())
}
#[cfg(test)]
mod tests {
use super::*;
fn fixture(tag: u8) -> [u8; 16] {
core::array::from_fn(|i| tag.wrapping_add(i as u8))
}
#[test]
fn roundtrip_short() {
let s_enc = fixture(0x42);
let iv = fixture(0xAA);
for n in 0..40usize {
let plain: Vec<u8> = (0u8..(n as u8)).collect();
let ct = encrypt_data(&s_enc, &iv, &plain);
assert_eq!(ct.len() % 16, 0);
assert!(ct.len() > plain.len());
let pt = decrypt_data(&s_enc, &iv, &ct).unwrap();
assert_eq!(pt, plain);
}
}
#[test]
fn complement_icv_is_xor_ff() {
let mac = [0xA5u8; 16];
let iv = complement_icv(&mac);
assert!(iv.iter().all(|&b| b == 0x5A));
}
#[test]
fn decrypt_rejects_unaligned() {
let key = fixture(0);
let iv = fixture(0);
assert!(decrypt_data(&key, &iv, &[0u8; 15]).is_err());
assert!(decrypt_data(&key, &iv, &[]).is_err());
}
#[test]
fn wrong_iv_fails_padding() {
let s_enc = fixture(0x42);
let iv = fixture(0xAA);
let ct = encrypt_data(&s_enc, &iv, &[1, 2, 3, 4]);
let bad_iv = fixture(0xFF);
assert!(decrypt_data(&s_enc, &bad_iv, &ct).is_err());
}
}