//! XChaCha20-Poly1305 authenticated encryption. //! //! Uses OS-provided randomness (`arc4random_buf`) via //! `tesseras_dht::sys::random_bytes` for key and nonce //! generation. use chacha20poly1305::{ XChaCha20Poly1305, XNonce, aead::{Aead, KeyInit}, }; /// XChaCha20 extended nonce size (24 bytes). const NONCE_SIZE: usize = 24; /// XChaCha20-Poly1305 key size (32 bytes). pub const KEY_SIZE: usize = 32; /// Generate a random 32-byte encryption key. pub fn generate_key() -> [u8; KEY_SIZE] { let mut key = [0u8; KEY_SIZE]; tesseras_dht::sys::random_bytes(&mut key); key } /// Encrypt plaintext with a random nonce. Returns `nonce || ciphertext`. pub fn encrypt(key: &[u8; KEY_SIZE], plaintext: &[u8]) -> Vec { let cipher = XChaCha20Poly1305::new(key.into()); let mut nonce_bytes = [0u8; NONCE_SIZE]; tesseras_dht::sys::random_bytes(&mut nonce_bytes); let nonce = XNonce::from(nonce_bytes); let ciphertext = cipher .encrypt(&nonce, plaintext) .expect("encryption should not fail"); let mut out = Vec::with_capacity(NONCE_SIZE + ciphertext.len()); out.extend_from_slice(&nonce_bytes); out.extend_from_slice(&ciphertext); out } /// Decrypt `nonce || ciphertext`. Returns `None` if authentication fails. pub fn decrypt(key: &[u8; KEY_SIZE], data: &[u8]) -> Option> { if data.len() < NONCE_SIZE { return None; } let (nonce_bytes, ciphertext) = data.split_at(NONCE_SIZE); let nonce = XNonce::from_slice(nonce_bytes); let cipher = XChaCha20Poly1305::new(key.into()); cipher.decrypt(nonce, ciphertext).ok() } #[cfg(test)] mod tests { use super::*; #[test] fn round_trip() { let key = generate_key(); let sealed = encrypt(&key, b"hello"); let opened = decrypt(&key, &sealed).unwrap(); assert_eq!(opened, b"hello"); } #[test] fn wrong_key_fails() { let key = generate_key(); let wrong = generate_key(); let sealed = encrypt(&key, b"secret"); assert!(decrypt(&wrong, &sealed).is_none()); } #[test] fn truncated_fails() { let key = generate_key(); assert!(decrypt(&key, &[0u8; 10]).is_none()); } #[test] fn different_nonces() { let key = generate_key(); let a = encrypt(&key, b"same"); let b = encrypt(&key, b"same"); assert_ne!(a, b); } }