firecloud_crypto/
cipher.rs1use crate::{CryptoError, CryptoResult, KEY_SIZE, NONCE_SIZE};
4use chacha20poly1305::{
5 aead::{Aead, KeyInit},
6 XChaCha20Poly1305, XNonce,
7};
8use rand::rngs::OsRng;
9
10pub fn encrypt(key: &[u8; KEY_SIZE], plaintext: &[u8]) -> CryptoResult<Vec<u8>> {
14 let cipher = XChaCha20Poly1305::new_from_slice(key)
15 .map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
16
17 let mut nonce_bytes = [0u8; NONCE_SIZE];
19 rand::RngCore::fill_bytes(&mut OsRng, &mut nonce_bytes);
20 let nonce = XNonce::from_slice(&nonce_bytes);
21
22 let ciphertext = cipher
24 .encrypt(nonce, plaintext)
25 .map_err(|e| CryptoError::EncryptionFailed(e.to_string()))?;
26
27 let mut result = Vec::with_capacity(NONCE_SIZE + ciphertext.len());
29 result.extend_from_slice(&nonce_bytes);
30 result.extend_from_slice(&ciphertext);
31
32 Ok(result)
33}
34
35pub fn decrypt(key: &[u8; KEY_SIZE], ciphertext: &[u8]) -> CryptoResult<Vec<u8>> {
39 if ciphertext.len() < NONCE_SIZE {
40 return Err(CryptoError::DecryptionFailed(
41 "Ciphertext too short".to_string(),
42 ));
43 }
44
45 let cipher = XChaCha20Poly1305::new_from_slice(key)
46 .map_err(|e| CryptoError::DecryptionFailed(e.to_string()))?;
47
48 let nonce = XNonce::from_slice(&ciphertext[..NONCE_SIZE]);
50 let encrypted_data = &ciphertext[NONCE_SIZE..];
51
52 cipher
54 .decrypt(nonce, encrypted_data)
55 .map_err(|e| CryptoError::DecryptionFailed(e.to_string()))
56}
57
58#[allow(dead_code)]
61pub fn encrypt_dek(kek: &[u8; KEY_SIZE], dek: &[u8; KEY_SIZE]) -> CryptoResult<Vec<u8>> {
62 encrypt(kek, dek)
63}
64
65#[allow(dead_code)]
68pub fn decrypt_dek(kek: &[u8; KEY_SIZE], encrypted_dek: &[u8]) -> CryptoResult<[u8; KEY_SIZE]> {
69 let decrypted = decrypt(kek, encrypted_dek)?;
70 if decrypted.len() != KEY_SIZE {
71 return Err(CryptoError::DecryptionFailed(
72 "Invalid DEK length".to_string(),
73 ));
74 }
75 let mut dek = [0u8; KEY_SIZE];
76 dek.copy_from_slice(&decrypted);
77 Ok(dek)
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_encrypt_decrypt_roundtrip() {
86 let key = [0x42u8; KEY_SIZE];
87 let plaintext = b"Hello, FireCloud!";
88
89 let ciphertext = encrypt(&key, plaintext).unwrap();
90 let decrypted = decrypt(&key, &ciphertext).unwrap();
91
92 assert_eq!(decrypted, plaintext);
93 }
94
95 #[test]
96 fn test_dek_encryption() {
97 let kek = [0x42u8; KEY_SIZE];
98 let dek = [0x55u8; KEY_SIZE];
99
100 let encrypted = encrypt_dek(&kek, &dek).unwrap();
101 let decrypted = decrypt_dek(&kek, &encrypted).unwrap();
102
103 assert_eq!(decrypted, dek);
104 }
105
106 #[test]
107 fn test_wrong_key_fails() {
108 let key1 = [0x42u8; KEY_SIZE];
109 let key2 = [0x43u8; KEY_SIZE];
110 let plaintext = b"Secret data";
111
112 let ciphertext = encrypt(&key1, plaintext).unwrap();
113 let result = decrypt(&key2, &ciphertext);
114
115 assert!(result.is_err());
116 }
117}