use crate::Error;
use crate::cipher::{self, Aes, ChaCha20};
use crate::low::{Zeroable, zeroise_value};
pub const SAMPLE_LEN: usize = 16;
#[derive(Debug)]
pub struct Algorithm {
id: AlgorithmId,
}
#[derive(Debug)]
enum AlgorithmId {
Aes128,
Aes256,
ChaCha20,
}
impl Algorithm {
pub fn key_len(&self) -> usize {
match self.id {
AlgorithmId::Aes128 => 16,
AlgorithmId::Aes256 | AlgorithmId::ChaCha20 => 32,
}
}
}
pub static AES_128: Algorithm = Algorithm {
id: AlgorithmId::Aes128,
};
pub static AES_256: Algorithm = Algorithm {
id: AlgorithmId::Aes256,
};
pub static CHACHA20: Algorithm = Algorithm {
id: AlgorithmId::ChaCha20,
};
pub struct HeaderProtectionKey(Key);
impl HeaderProtectionKey {
pub fn new(algorithm: &'static Algorithm, key: &[u8]) -> Result<Self, Error> {
Ok(Self(match algorithm.id {
AlgorithmId::Aes128 => Key::Aes(Aes::new(&cipher::AES_128, key)?),
AlgorithmId::Aes256 => Key::Aes(Aes::new(&cipher::AES_256, key)?),
AlgorithmId::ChaCha20 => Key::ChaCha20(ChaCha20::new(
key.try_into().map_err(|_| Error::WrongLength)?,
)),
}))
}
pub fn mask(&self, sample: &[u8; SAMPLE_LEN]) -> [u8; 5] {
match &self.0 {
Key::Aes(key) => {
let mut block = *sample;
key.encrypt_block(&mut block);
core::array::from_fn(|i| block[i])
}
Key::ChaCha20(key) => key.header_protection_mask(sample),
}
}
}
#[allow(clippy::large_enum_variant)]
#[repr(u8)]
enum Key {
Aes(Aes),
ChaCha20(ChaCha20),
}
impl Drop for Key {
fn drop(&mut self) {
zeroise_value(self);
}
}
impl Zeroable for Key {}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn key_length_must_match_algorithm() {
let key = [0u8; 33];
for algorithm in [&AES_128, &AES_256, &CHACHA20] {
for len in 0..=key.len() {
assert_eq!(
HeaderProtectionKey::new(algorithm, &key[..len]).is_ok(),
len == algorithm.key_len(),
"{algorithm:?} len {len}"
);
}
}
}
#[test]
fn rfc9001() {
for (algorithm, key, sample, mask) in [
(
&AES_128,
"9f50449e04a0e810283a1e9933adedd2",
"d1b1c98dd7689fb8ec11d242b123dc9b",
"437b9aec36",
),
(
&AES_128,
"c206b8d9b9f0f37644430b490eeaa314",
"2cd0991cd25b0aac406a5816b6394100",
"2ec0d8356a",
),
(
&CHACHA20,
"25a282b9e82f06f21f488917a4fc8f1b73573685608597d0efcb076b0ab7a7a4",
"5e5cd55c41f69080575d7999c25a5bfb",
"aefefe7d03",
),
] {
let key = HeaderProtectionKey::new(algorithm, &hex::decode(key).unwrap()).unwrap();
let sample = hex::decode(sample).unwrap().try_into().unwrap();
assert_eq!(hex::encode(key.mask(&sample)), mask);
}
}
}