use chacha20poly1305::{
aead::{Aead, KeyInit},
XChaCha20Poly1305, XNonce,
};
use rand::RngCore;
use crate::CryptoError;
pub fn encrypt(key: &[u8; 32], plaintext: &[u8]) -> Result<Vec<u8>, CryptoError> {
let cipher = XChaCha20Poly1305::new(key.into());
let mut nonce_bytes = [0u8; 24];
rand::thread_rng().fill_bytes(&mut nonce_bytes);
let nonce = XNonce::from_slice(&nonce_bytes);
let ciphertext = cipher
.encrypt(nonce, plaintext)
.map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
let mut result = Vec::with_capacity(24 + ciphertext.len());
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
Ok(result)
}
pub fn decrypt(key: &[u8; 32], data: &[u8]) -> Result<Vec<u8>, CryptoError> {
if data.len() < 24 {
return Err(CryptoError::DecryptionFailed("data too short".into()));
}
let (nonce_bytes, ciphertext) = data.split_at(24);
let nonce = XNonce::from_slice(nonce_bytes);
let cipher = XChaCha20Poly1305::new(key.into());
let plaintext = cipher
.decrypt(nonce, ciphertext)
.map_err(|e| CryptoError::DecryptionFailed(e.to_string()))?;
Ok(plaintext)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encrypt_decrypt_roundtrip() {
let key = [42u8; 32];
let plaintext = b"secret data for testing";
let encrypted = encrypt(&key, plaintext).unwrap();
let decrypted = decrypt(&key, &encrypted).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn wrong_key_fails() {
let key = [42u8; 32];
let wrong_key = [43u8; 32];
let plaintext = b"secret data";
let encrypted = encrypt(&key, plaintext).unwrap();
assert!(decrypt(&wrong_key, &encrypted).is_err());
}
}