reddb_server/storage/encryption/
page_encryptor.rs1use super::key::SecureKey;
8use crate::crypto::aes_gcm::{aes256_gcm_decrypt, aes256_gcm_encrypt};
9use crate::crypto::uuid::Uuid;
10
11pub const NONCE_SIZE: usize = 12;
13pub const TAG_SIZE: usize = 16;
15pub const OVERHEAD: usize = NONCE_SIZE + TAG_SIZE;
17
18pub struct PageEncryptor {
20 key: SecureKey,
21}
22
23impl PageEncryptor {
24 pub fn new(key: SecureKey) -> Self {
26 Self { key }
27 }
28
29 pub fn encrypt(&self, page_id: u32, plaintext: &[u8]) -> Vec<u8> {
38 let uuid = Uuid::new_v4();
41 let mut nonce = [0u8; NONCE_SIZE];
42 nonce.copy_from_slice(&uuid.as_bytes()[0..NONCE_SIZE]);
43
44 let aad = page_id.to_le_bytes();
46
47 let key: &[u8; 32] = self
48 .key
49 .as_bytes()
50 .try_into()
51 .expect("Key must be 32 bytes");
52
53 let ciphertext_with_tag = aes256_gcm_encrypt(key, &nonce, &aad, plaintext);
55
56 let mut result = Vec::with_capacity(NONCE_SIZE + ciphertext_with_tag.len());
58 result.extend_from_slice(&nonce);
59 result.extend_from_slice(&ciphertext_with_tag);
60
61 result
62 }
63
64 pub fn decrypt(&self, page_id: u32, encrypted_data: &[u8]) -> Result<Vec<u8>, String> {
66 if encrypted_data.len() < OVERHEAD {
67 return Err("Encrypted data too short".to_string());
68 }
69
70 let nonce = &encrypted_data[..NONCE_SIZE];
72 let ciphertext_with_tag = &encrypted_data[NONCE_SIZE..];
73
74 let mut nonce_arr = [0u8; NONCE_SIZE];
75 nonce_arr.copy_from_slice(nonce);
76
77 let aad = page_id.to_le_bytes();
79
80 let key: &[u8; 32] = self
81 .key
82 .as_bytes()
83 .try_into()
84 .expect("Key must be 32 bytes");
85
86 aes256_gcm_decrypt(key, &nonce_arr, &aad, ciphertext_with_tag)
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_page_encryption_roundtrip() {
96 let key = SecureKey::new(&[0x42u8; 32]);
97 let encryptor = PageEncryptor::new(key);
98
99 let page_id = 123;
100 let plaintext = b"This is a secret page content.";
101
102 let encrypted = encryptor.encrypt(page_id, plaintext);
103
104 assert_eq!(encrypted.len(), plaintext.len() + OVERHEAD);
106
107 let decrypted = encryptor.decrypt(page_id, &encrypted).unwrap();
109 assert_eq!(decrypted, plaintext);
110 }
111
112 #[test]
113 fn test_page_encryption_bad_page_id() {
114 let key = SecureKey::new(&[0x42u8; 32]);
115 let encryptor = PageEncryptor::new(key);
116
117 let plaintext = b"content";
118 let encrypted = encryptor.encrypt(100, plaintext);
119
120 let result = encryptor.decrypt(101, &encrypted);
122 assert!(result.is_err());
123 }
124
125 #[test]
126 fn test_page_encryption_tampering() {
127 let key = SecureKey::new(&[0x42u8; 32]);
128 let encryptor = PageEncryptor::new(key);
129
130 let plaintext = b"content";
131 let mut encrypted = encryptor.encrypt(100, plaintext);
132
133 let last = encrypted.len() - 1;
135 encrypted[last] ^= 1;
136
137 let result = encryptor.decrypt(100, &encrypted);
138 assert!(result.is_err());
139 }
140}