use super::error::Error;
use chacha20poly1305::{
aead::{generic_array::typenum::Unsigned, Aead},
AeadCore, ChaCha20Poly1305, KeyInit as _,
};
use rand_core::CryptoRngCore;
use std::vec::Vec;
use zeroize::ZeroizeOnDrop;
pub const CIPHERTEXT_OVERHEAD: usize = <ChaCha20Poly1305 as AeadCore>::TagSize::USIZE;
const NONCE_SIZE_BYTES: usize = <ChaCha20Poly1305 as AeadCore>::NonceSize::USIZE;
struct CounterNonce {
inner: u128,
}
impl ZeroizeOnDrop for CounterNonce {}
impl CounterNonce {
pub fn new() -> Self {
Self { inner: 0 }
}
pub fn inc(&mut self) -> Result<[u8; 128 / 8], Error> {
if self.inner >= 1 << (8 * NONCE_SIZE_BYTES) {
return Err(Error::MessageLimitReached);
}
let out = self.inner.to_le_bytes();
self.inner += 1;
Ok(out)
}
}
#[derive(ZeroizeOnDrop)]
pub struct SendCipher {
nonce: CounterNonce,
inner: ChaCha20Poly1305,
}
impl SendCipher {
pub fn new(mut rng: impl CryptoRngCore) -> Self {
let mut key = [0u8; 32];
rng.fill_bytes(&mut key[..]);
Self {
nonce: CounterNonce::new(),
inner: ChaCha20Poly1305::new(&key.into()),
}
}
pub fn send(&mut self, data: &[u8]) -> Result<Vec<u8>, Error> {
self.inner
.encrypt((&self.nonce.inc()?[..NONCE_SIZE_BYTES]).into(), data)
.map_err(|_| Error::EncryptionFailed)
}
}
#[derive(ZeroizeOnDrop)]
pub struct RecvCipher {
nonce: CounterNonce,
inner: ChaCha20Poly1305,
}
impl RecvCipher {
pub fn new(mut rng: impl CryptoRngCore) -> Self {
let mut key = [0u8; 32];
rng.fill_bytes(&mut key[..]);
Self {
nonce: CounterNonce::new(),
inner: ChaCha20Poly1305::new(&key.into()),
}
}
pub fn recv(&mut self, encrypted_data: &[u8]) -> Result<Vec<u8>, Error> {
self.inner
.decrypt(
(&self.nonce.inc()?[..NONCE_SIZE_BYTES]).into(),
encrypted_data,
)
.map_err(|_| Error::DecryptionFailed)
}
}