use crate::error::{CryptoError, Result};
use chacha20_blake3::{ChaCha20Blake3 as ChaCha20Blake3Impl, Error as ChaCha20Blake3Error};
#[derive(Debug)]
pub struct ChaCha20Blake3;
pub const KEY_SIZE: usize = chacha20_blake3::KEY_SIZE; pub const NONCE_SIZE: usize = chacha20_blake3::NONCE_SIZE; pub const TAG_SIZE: usize = chacha20_blake3::TAG_SIZE;
impl ChaCha20Blake3 {
pub fn encrypt(
key: &[u8; 32],
nonce: &[u8; 24],
plaintext: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>> {
let cipher = ChaCha20Blake3Impl::new(*key);
let result = cipher.encrypt(nonce, plaintext, associated_data);
Ok(result)
}
pub fn decrypt(
key: &[u8; 32],
nonce: &[u8; 24],
ciphertext_with_tag: &[u8],
associated_data: &[u8],
) -> Result<Vec<u8>> {
let cipher = ChaCha20Blake3Impl::new(*key);
cipher
.decrypt(nonce, ciphertext_with_tag, associated_data)
.map_err(|e: ChaCha20Blake3Error| {
CryptoError::Decryption(format!("ChaCha20-BLAKE3: {:?}", e))
})
}
pub fn generate_nonce() -> [u8; 24] {
let mut nonce = [0u8; 24];
crate::internal::getrandom::fill(&mut nonce).expect("RNG failed");
nonce
}
pub fn generate_key() -> [u8; 32] {
let mut key = [0u8; 32];
crate::internal::getrandom::fill(&mut key).expect("RNG failed");
key
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_roundtrip() {
let key = ChaCha20Blake3::generate_key();
let nonce = ChaCha20Blake3::generate_nonce();
let plaintext = b"Hello, ChaCha20-BLAKE3!";
let associated_data = b"test-aad";
let encrypted = ChaCha20Blake3::encrypt(&key, &nonce, plaintext, associated_data).unwrap();
let decrypted = ChaCha20Blake3::decrypt(&key, &nonce, &encrypted, associated_data).unwrap();
assert_eq!(plaintext.to_vec(), decrypted);
}
#[test]
fn test_wrong_key_fails() {
let key1 = ChaCha20Blake3::generate_key();
let key2 = ChaCha20Blake3::generate_key();
let nonce = ChaCha20Blake3::generate_nonce();
let plaintext = b"secret";
let encrypted = ChaCha20Blake3::encrypt(&key1, &nonce, plaintext, &[]).unwrap();
let result = ChaCha20Blake3::decrypt(&key2, &nonce, &encrypted, &[]);
assert!(result.is_err());
}
#[test]
fn test_wrong_tag_fails() {
let key = ChaCha20Blake3::generate_key();
let nonce = ChaCha20Blake3::generate_nonce();
let plaintext = b"secret message";
let mut encrypted = ChaCha20Blake3::encrypt(&key, &nonce, plaintext, &[]).unwrap();
if let Some(byte) = encrypted.get_mut(0) {
*byte ^= 0xFF;
}
let result = ChaCha20Blake3::decrypt(&key, &nonce, &encrypted, &[]);
assert!(result.is_err());
}
#[test]
fn test_test_vectors() {
let key = [0u8; 32];
let nonce = [0u8; 24];
let plaintext = b"";
let associated_data = b"";
let encrypted = ChaCha20Blake3::encrypt(&key, &nonce, plaintext, associated_data).unwrap();
assert_eq!(encrypted.len(), TAG_SIZE);
let decrypted = ChaCha20Blake3::decrypt(&key, &nonce, &encrypted, associated_data).unwrap();
assert_eq!(decrypted.len(), 0);
}
}