use crate::{CryptoError, CryptoResult, KEY_SIZE, NONCE_SIZE};
use chacha20poly1305::{
aead::{Aead, KeyInit},
XChaCha20Poly1305, XNonce,
};
use rand::rngs::OsRng;
pub fn encrypt(key: &[u8; KEY_SIZE], plaintext: &[u8]) -> CryptoResult<Vec<u8>> {
let cipher = XChaCha20Poly1305::new_from_slice(key)
.map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
let mut nonce_bytes = [0u8; NONCE_SIZE];
rand::RngCore::fill_bytes(&mut OsRng, &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(NONCE_SIZE + ciphertext.len());
result.extend_from_slice(&nonce_bytes);
result.extend_from_slice(&ciphertext);
Ok(result)
}
pub fn decrypt(key: &[u8; KEY_SIZE], ciphertext: &[u8]) -> CryptoResult<Vec<u8>> {
if ciphertext.len() < NONCE_SIZE {
return Err(CryptoError::DecryptionFailed(
"Ciphertext too short".to_string(),
));
}
let cipher = XChaCha20Poly1305::new_from_slice(key)
.map_err(|e| CryptoError::DecryptionFailed(e.to_string()))?;
let nonce = XNonce::from_slice(&ciphertext[..NONCE_SIZE]);
let encrypted_data = &ciphertext[NONCE_SIZE..];
cipher
.decrypt(nonce, encrypted_data)
.map_err(|e| CryptoError::DecryptionFailed(e.to_string()))
}
#[allow(dead_code)]
pub fn encrypt_dek(kek: &[u8; KEY_SIZE], dek: &[u8; KEY_SIZE]) -> CryptoResult<Vec<u8>> {
encrypt(kek, dek)
}
#[allow(dead_code)]
pub fn decrypt_dek(kek: &[u8; KEY_SIZE], encrypted_dek: &[u8]) -> CryptoResult<[u8; KEY_SIZE]> {
let decrypted = decrypt(kek, encrypted_dek)?;
if decrypted.len() != KEY_SIZE {
return Err(CryptoError::DecryptionFailed(
"Invalid DEK length".to_string(),
));
}
let mut dek = [0u8; KEY_SIZE];
dek.copy_from_slice(&decrypted);
Ok(dek)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encrypt_decrypt_roundtrip() {
let key = [0x42u8; KEY_SIZE];
let plaintext = b"Hello, FireCloud!";
let ciphertext = encrypt(&key, plaintext).unwrap();
let decrypted = decrypt(&key, &ciphertext).unwrap();
assert_eq!(decrypted, plaintext);
}
#[test]
fn test_dek_encryption() {
let kek = [0x42u8; KEY_SIZE];
let dek = [0x55u8; KEY_SIZE];
let encrypted = encrypt_dek(&kek, &dek).unwrap();
let decrypted = decrypt_dek(&kek, &encrypted).unwrap();
assert_eq!(decrypted, dek);
}
#[test]
fn test_wrong_key_fails() {
let key1 = [0x42u8; KEY_SIZE];
let key2 = [0x43u8; KEY_SIZE];
let plaintext = b"Secret data";
let ciphertext = encrypt(&key1, plaintext).unwrap();
let result = decrypt(&key2, &ciphertext);
assert!(result.is_err());
}
}