use crate::Error;
use crate::low::{self, Entry, zeroise};
pub struct ChaCha20 {
key: [u8; 32],
}
impl ChaCha20 {
pub fn new(key: [u8; 32]) -> Self {
low::ct::secret_slice(&key);
Self { key }
}
pub fn apply_keystream(
&self,
nonce: &[u8; 12],
counter: u32,
inout: &mut [u8],
) -> Result<(), Error> {
let _entry = Entry::new_secret();
check_counter(counter, inout.len())?;
low::ct::secret_slice(inout);
let mut counter_nonce = [0u8; 16];
counter_nonce[..4].copy_from_slice(&counter.to_le_bytes());
counter_nonce[4..].copy_from_slice(nonce);
low::chacha20::ChaCha20::new(&self.key, &counter_nonce).cipher(inout);
low::ct::public_slice(inout);
Ok(())
}
pub(crate) fn header_protection_mask(&self, sample: &[u8; 16]) -> [u8; 5] {
let _entry = Entry::new_secret();
let mut mask = [0u8; 5];
low::chacha20::ChaCha20::new(&self.key, sample).cipher(&mut mask);
low::ct::public_slice(&mask);
mask
}
}
pub(super) fn check_counter(counter: u32, len: usize) -> Result<(), Error> {
if u64::from(counter) + len.div_ceil(64) as u64 > 1 << 32 {
return Err(Error::WrongLength);
}
Ok(())
}
impl Drop for ChaCha20 {
fn drop(&mut self) {
zeroise(&mut self.key);
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
#[test]
fn rfc8439() {
let key: [u8; 32] = core::array::from_fn(|i| i as u8);
let nonce = hex::decode("000000000000004a00000000").unwrap();
let mut buf = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.".to_vec();
ChaCha20::new(key)
.apply_keystream(&nonce.try_into().unwrap(), 1, &mut buf)
.unwrap();
assert_eq!(
hex::encode(&buf),
"6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0b\
f91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d8\
07ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab7793736\
5af90bbf74a35be6b40b8eedf2785e42874d"
);
}
#[test]
fn counter_limits() {
assert_eq!(check_counter(u32::MAX, 64), Ok(()));
assert_eq!(check_counter(u32::MAX, 65), Err(Error::WrongLength));
#[cfg(target_pointer_width = "64")]
{
let max_aead = ((1 << 32) - 1) * 64;
assert_eq!(check_counter(1, max_aead), Ok(()));
assert_eq!(check_counter(1, max_aead + 1), Err(Error::WrongLength));
}
}
}