oxigdal_security/encryption/
at_rest.rs1use crate::encryption::{EncryptedData, EncryptionAlgorithm, EncryptionMetadata};
4use crate::error::{Result, SecurityError};
5use aes_gcm::{
6 Aes256Gcm, Nonce,
7 aead::{Aead, KeyInit, OsRng},
8};
9use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
10use chacha20poly1305::ChaCha20Poly1305;
11use rand::RngCore;
12
13pub struct AtRestEncryptor {
15 algorithm: EncryptionAlgorithm,
16 key: Vec<u8>,
17 key_id: String,
18}
19
20impl AtRestEncryptor {
21 pub fn new(algorithm: EncryptionAlgorithm, key: Vec<u8>, key_id: String) -> Result<Self> {
23 let required_length = match algorithm {
25 EncryptionAlgorithm::Aes256Gcm => 32,
26 EncryptionAlgorithm::ChaCha20Poly1305 => 32,
27 };
28
29 if key.len() != required_length {
30 return Err(SecurityError::encryption(format!(
31 "Invalid key length: expected {}, got {}",
32 required_length,
33 key.len()
34 )));
35 }
36
37 Ok(Self {
38 algorithm,
39 key,
40 key_id,
41 })
42 }
43
44 pub fn generate_key(algorithm: EncryptionAlgorithm) -> Vec<u8> {
46 let mut key = vec![
47 0u8;
48 match algorithm {
49 EncryptionAlgorithm::Aes256Gcm => 32,
50 EncryptionAlgorithm::ChaCha20Poly1305 => 32,
51 }
52 ];
53 OsRng.fill_bytes(&mut key);
54 key
55 }
56
57 pub fn encrypt(&self, plaintext: &[u8], aad: Option<&[u8]>) -> Result<EncryptedData> {
59 match self.algorithm {
60 EncryptionAlgorithm::Aes256Gcm => self.encrypt_aes_gcm(plaintext, aad),
61 EncryptionAlgorithm::ChaCha20Poly1305 => self.encrypt_chacha(plaintext, aad),
62 }
63 }
64
65 pub fn decrypt(&self, encrypted: &EncryptedData) -> Result<Vec<u8>> {
67 if encrypted.metadata.algorithm != self.algorithm {
69 return Err(SecurityError::decryption(format!(
70 "Algorithm mismatch: expected {:?}, got {:?}",
71 self.algorithm, encrypted.metadata.algorithm
72 )));
73 }
74
75 if encrypted.metadata.key_id != self.key_id {
77 return Err(SecurityError::decryption(format!(
78 "Key ID mismatch: expected {}, got {}",
79 self.key_id, encrypted.metadata.key_id
80 )));
81 }
82
83 match self.algorithm {
84 EncryptionAlgorithm::Aes256Gcm => self.decrypt_aes_gcm(encrypted),
85 EncryptionAlgorithm::ChaCha20Poly1305 => self.decrypt_chacha(encrypted),
86 }
87 }
88
89 fn encrypt_aes_gcm(&self, plaintext: &[u8], aad: Option<&[u8]>) -> Result<EncryptedData> {
90 let cipher = Aes256Gcm::new_from_slice(&self.key)
91 .map_err(|e| SecurityError::encryption(format!("Failed to create cipher: {}", e)))?;
92
93 let mut nonce_bytes = [0u8; 12];
95 OsRng.fill_bytes(&mut nonce_bytes);
96 let nonce = Nonce::from_slice(&nonce_bytes);
97
98 let ciphertext = if let Some(aad_data) = aad {
100 cipher
101 .encrypt(
102 nonce,
103 aes_gcm::aead::Payload {
104 msg: plaintext,
105 aad: aad_data,
106 },
107 )
108 .map_err(|e| SecurityError::encryption(format!("Encryption failed: {}", e)))?
109 } else {
110 cipher
111 .encrypt(nonce, plaintext)
112 .map_err(|e| SecurityError::encryption(format!("Encryption failed: {}", e)))?
113 };
114
115 let metadata = EncryptionMetadata::new(
116 self.algorithm,
117 self.key_id.clone(),
118 nonce_bytes.to_vec(),
119 aad.map(|a| a.to_vec()),
120 );
121
122 Ok(EncryptedData::new(ciphertext, metadata))
123 }
124
125 fn decrypt_aes_gcm(&self, encrypted: &EncryptedData) -> Result<Vec<u8>> {
126 let cipher = Aes256Gcm::new_from_slice(&self.key)
127 .map_err(|e| SecurityError::decryption(format!("Failed to create cipher: {}", e)))?;
128
129 if encrypted.metadata.iv.len() != 12 {
130 return Err(SecurityError::decryption(format!(
131 "Invalid nonce length: expected 12, got {}",
132 encrypted.metadata.iv.len()
133 )));
134 }
135
136 let nonce = Nonce::from_slice(&encrypted.metadata.iv);
137
138 let plaintext = if let Some(ref aad) = encrypted.metadata.aad {
139 cipher
140 .decrypt(
141 nonce,
142 aes_gcm::aead::Payload {
143 msg: &encrypted.ciphertext,
144 aad,
145 },
146 )
147 .map_err(|e| SecurityError::decryption(format!("Decryption failed: {}", e)))?
148 } else {
149 cipher
150 .decrypt(nonce, encrypted.ciphertext.as_ref())
151 .map_err(|e| SecurityError::decryption(format!("Decryption failed: {}", e)))?
152 };
153
154 Ok(plaintext)
155 }
156
157 fn encrypt_chacha(&self, plaintext: &[u8], aad: Option<&[u8]>) -> Result<EncryptedData> {
158 let cipher = ChaCha20Poly1305::new_from_slice(&self.key)
159 .map_err(|e| SecurityError::encryption(format!("Failed to create cipher: {}", e)))?;
160
161 let mut nonce_bytes = [0u8; 12];
163 OsRng.fill_bytes(&mut nonce_bytes);
164 let nonce = chacha20poly1305::Nonce::from_slice(&nonce_bytes);
165
166 let ciphertext = if let Some(aad_data) = aad {
168 cipher
169 .encrypt(
170 nonce,
171 chacha20poly1305::aead::Payload {
172 msg: plaintext,
173 aad: aad_data,
174 },
175 )
176 .map_err(|e| SecurityError::encryption(format!("Encryption failed: {}", e)))?
177 } else {
178 cipher
179 .encrypt(nonce, plaintext)
180 .map_err(|e| SecurityError::encryption(format!("Encryption failed: {}", e)))?
181 };
182
183 let metadata = EncryptionMetadata::new(
184 self.algorithm,
185 self.key_id.clone(),
186 nonce_bytes.to_vec(),
187 aad.map(|a| a.to_vec()),
188 );
189
190 Ok(EncryptedData::new(ciphertext, metadata))
191 }
192
193 fn decrypt_chacha(&self, encrypted: &EncryptedData) -> Result<Vec<u8>> {
194 let cipher = ChaCha20Poly1305::new_from_slice(&self.key)
195 .map_err(|e| SecurityError::decryption(format!("Failed to create cipher: {}", e)))?;
196
197 if encrypted.metadata.iv.len() != 12 {
198 return Err(SecurityError::decryption(format!(
199 "Invalid nonce length: expected 12, got {}",
200 encrypted.metadata.iv.len()
201 )));
202 }
203
204 let nonce = chacha20poly1305::Nonce::from_slice(&encrypted.metadata.iv);
205
206 let plaintext = if let Some(ref aad) = encrypted.metadata.aad {
207 cipher
208 .decrypt(
209 nonce,
210 chacha20poly1305::aead::Payload {
211 msg: &encrypted.ciphertext,
212 aad,
213 },
214 )
215 .map_err(|e| SecurityError::decryption(format!("Decryption failed: {}", e)))?
216 } else {
217 cipher
218 .decrypt(nonce, encrypted.ciphertext.as_ref())
219 .map_err(|e| SecurityError::decryption(format!("Decryption failed: {}", e)))?
220 };
221
222 Ok(plaintext)
223 }
224
225 pub fn encrypt_in_place(
227 &self,
228 buffer: &mut Vec<u8>,
229 aad: Option<&[u8]>,
230 ) -> Result<EncryptionMetadata> {
231 let encrypted = self.encrypt(buffer, aad)?;
232 buffer.clear();
233 buffer.extend_from_slice(&encrypted.ciphertext);
234 Ok(encrypted.metadata)
235 }
236
237 pub fn algorithm(&self) -> EncryptionAlgorithm {
239 self.algorithm
240 }
241
242 pub fn key_id(&self) -> &str {
244 &self.key_id
245 }
246}
247
248pub struct FieldEncryptor {
250 encryptor: AtRestEncryptor,
251}
252
253impl FieldEncryptor {
254 pub fn new(encryptor: AtRestEncryptor) -> Self {
256 Self { encryptor }
257 }
258
259 pub fn encrypt_string(&self, value: &str) -> Result<String> {
261 let encrypted = self.encryptor.encrypt(value.as_bytes(), None)?;
262 let json = serde_json::to_string(&encrypted)?;
263 Ok(BASE64.encode(json))
264 }
265
266 pub fn decrypt_string(&self, encrypted: &str) -> Result<String> {
268 let json = BASE64
269 .decode(encrypted)
270 .map_err(|e| SecurityError::decryption(format!("Base64 decode failed: {}", e)))?;
271 let encrypted_data: EncryptedData = serde_json::from_slice(&json)?;
272 let plaintext = self.encryptor.decrypt(&encrypted_data)?;
273 String::from_utf8(plaintext)
274 .map_err(|e| SecurityError::decryption(format!("UTF-8 decode failed: {}", e)))
275 }
276
277 pub fn encrypt_json<T: serde::Serialize>(&self, value: &T) -> Result<String> {
279 let json = serde_json::to_vec(value)?;
280 let encrypted = self.encryptor.encrypt(&json, None)?;
281 let encrypted_json = serde_json::to_string(&encrypted)?;
282 Ok(BASE64.encode(encrypted_json))
283 }
284
285 pub fn decrypt_json<T: serde::de::DeserializeOwned>(&self, encrypted: &str) -> Result<T> {
287 let json = BASE64
288 .decode(encrypted)
289 .map_err(|e| SecurityError::decryption(format!("Base64 decode failed: {}", e)))?;
290 let encrypted_data: EncryptedData = serde_json::from_slice(&json)?;
291 let plaintext = self.encryptor.decrypt(&encrypted_data)?;
292 serde_json::from_slice(&plaintext).map_err(SecurityError::from)
293 }
294}
295
296#[cfg(test)]
297mod tests {
298 use super::*;
299
300 #[test]
301 fn test_aes_gcm_encryption() {
302 let key = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
303 let encryptor =
304 AtRestEncryptor::new(EncryptionAlgorithm::Aes256Gcm, key, "test-key".to_string())
305 .expect("Failed to create encryptor");
306
307 let plaintext = b"Hello, World!";
308 let encrypted = encryptor
309 .encrypt(plaintext, None)
310 .expect("Encryption failed");
311
312 assert_ne!(encrypted.ciphertext, plaintext);
313 assert_eq!(encrypted.metadata.algorithm, EncryptionAlgorithm::Aes256Gcm);
314
315 let decrypted = encryptor.decrypt(&encrypted).expect("Decryption failed");
316 assert_eq!(decrypted, plaintext);
317 }
318
319 #[test]
320 fn test_aes_gcm_with_aad() {
321 let key = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
322 let encryptor =
323 AtRestEncryptor::new(EncryptionAlgorithm::Aes256Gcm, key, "test-key".to_string())
324 .expect("Failed to create encryptor");
325
326 let plaintext = b"Hello, World!";
327 let aad = b"additional data";
328 let encrypted = encryptor
329 .encrypt(plaintext, Some(aad))
330 .expect("Encryption failed");
331
332 let decrypted = encryptor.decrypt(&encrypted).expect("Decryption failed");
333 assert_eq!(decrypted, plaintext);
334 }
335
336 #[test]
337 fn test_chacha_encryption() {
338 let key = AtRestEncryptor::generate_key(EncryptionAlgorithm::ChaCha20Poly1305);
339 let encryptor = AtRestEncryptor::new(
340 EncryptionAlgorithm::ChaCha20Poly1305,
341 key,
342 "test-key".to_string(),
343 )
344 .expect("Failed to create encryptor");
345
346 let plaintext = b"Hello, World!";
347 let encrypted = encryptor
348 .encrypt(plaintext, None)
349 .expect("Encryption failed");
350
351 assert_ne!(encrypted.ciphertext, plaintext);
352 assert_eq!(
353 encrypted.metadata.algorithm,
354 EncryptionAlgorithm::ChaCha20Poly1305
355 );
356
357 let decrypted = encryptor.decrypt(&encrypted).expect("Decryption failed");
358 assert_eq!(decrypted, plaintext);
359 }
360
361 #[test]
362 fn test_field_encryptor_string() {
363 let key = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
364 let encryptor =
365 AtRestEncryptor::new(EncryptionAlgorithm::Aes256Gcm, key, "test-key".to_string())
366 .expect("Failed to create encryptor");
367 let field_encryptor = FieldEncryptor::new(encryptor);
368
369 let original = "sensitive data";
370 let encrypted = field_encryptor
371 .encrypt_string(original)
372 .expect("Encryption failed");
373
374 assert_ne!(encrypted, original);
375
376 let decrypted = field_encryptor
377 .decrypt_string(&encrypted)
378 .expect("Decryption failed");
379 assert_eq!(decrypted, original);
380 }
381
382 #[test]
383 fn test_encrypt_in_place() {
384 let key = AtRestEncryptor::generate_key(EncryptionAlgorithm::Aes256Gcm);
385 let encryptor =
386 AtRestEncryptor::new(EncryptionAlgorithm::Aes256Gcm, key, "test-key".to_string())
387 .expect("Failed to create encryptor");
388
389 let mut buffer = b"Hello, World!".to_vec();
390 let original = buffer.clone();
391
392 let metadata = encryptor
393 .encrypt_in_place(&mut buffer, None)
394 .expect("Encryption failed");
395
396 assert_ne!(buffer, original);
397
398 let encrypted = EncryptedData::new(buffer, metadata);
399 let decrypted = encryptor.decrypt(&encrypted).expect("Decryption failed");
400 assert_eq!(decrypted, original);
401 }
402}