use chacha20_poly1305::{chacha20::ChaCha20, ChaCha20Poly1305, Key, Nonce};
use core::fmt;
const LENGTH_BYTES: u32 = 3;
const REKEY_INTERVAL: u64 = 224;
const REKEY_INITIAL_NONCE: [u8; 4] = [0xFF, 0xFF, 0xFF, 0xFF];
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Error {
Decryption(chacha20_poly1305::Error),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::Decryption(e) => write!(f, "Unable to dycrypt: {e}."),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Decryption(e) => Some(e),
}
}
}
#[derive(Clone)]
pub struct FSChaCha20Poly1305 {
key: Key,
message_counter: u64,
}
impl FSChaCha20Poly1305 {
pub fn new(key: [u8; 32]) -> Self {
FSChaCha20Poly1305 {
key: Key::new(key),
message_counter: 0,
}
}
fn nonce(&self) -> [u8; 12] {
let mut nonce = [0u8; 12];
let counter_mod = ((self.message_counter % REKEY_INTERVAL) as u32).to_le_bytes();
nonce[0..4].copy_from_slice(&counter_mod);
let counter_div = (self.message_counter / REKEY_INTERVAL).to_le_bytes();
nonce[4..12].copy_from_slice(&counter_div);
nonce
}
fn rekey(&mut self, aad: &[u8]) {
if (self.message_counter + 1) % REKEY_INTERVAL == 0 {
let mut rekey_nonce = [0u8; 12];
rekey_nonce[0..4].copy_from_slice(&REKEY_INITIAL_NONCE);
rekey_nonce[4..].copy_from_slice(&self.nonce()[4..]);
let mut plaintext = [0u8; 32];
let cipher = ChaCha20Poly1305::new(self.key, Nonce::new(rekey_nonce));
cipher.encrypt(&mut plaintext, Some(aad));
self.key = Key::new(plaintext);
}
self.message_counter += 1;
}
pub fn encrypt(&mut self, aad: &[u8], content: &mut [u8]) -> [u8; 16] {
let cipher = ChaCha20Poly1305::new(self.key, Nonce::new(self.nonce()));
let tag = cipher.encrypt(content, Some(aad));
self.rekey(aad);
tag
}
pub fn decrypt(&mut self, aad: &[u8], content: &mut [u8], tag: [u8; 16]) -> Result<(), Error> {
let cipher = ChaCha20Poly1305::new(self.key, Nonce::new(self.nonce()));
cipher
.decrypt(content, tag, Some(aad))
.map_err(Error::Decryption)?;
self.rekey(aad);
Ok(())
}
}
#[derive(Clone)]
pub struct FSChaCha20 {
key: Key,
block_counter: u32,
chunk_counter: u32,
}
impl FSChaCha20 {
pub fn new(key: [u8; 32]) -> Self {
FSChaCha20 {
key: Key::new(key),
block_counter: 0,
chunk_counter: 0,
}
}
pub fn crypt(&mut self, chunk: &mut [u8; LENGTH_BYTES as usize]) {
let counter_mod = (self.chunk_counter / REKEY_INTERVAL as u32).to_le_bytes();
let mut nonce = [0u8; 12];
nonce[4..8].copy_from_slice(&counter_mod);
let mut cipher = ChaCha20::new(self.key, Nonce::new(nonce), 0);
cipher.seek(self.block_counter);
cipher.apply_keystream(chunk);
self.block_counter += LENGTH_BYTES;
if (self.chunk_counter + 1) % REKEY_INTERVAL as u32 == 0 {
let mut key_buffer = [0u8; 32];
cipher.seek(self.block_counter);
cipher.apply_keystream(&mut key_buffer);
self.block_counter = 0;
self.key = Key::new(key_buffer);
}
self.chunk_counter += 1;
}
}