Skip to main content

kms_aead/
ring_encryption.rs

1use crate::ring_support::*;
2use crate::{AeadEncryption, CipherText, DataEncryptionKey, KmsAeadResult};
3use async_trait::*;
4use ring::rand::SystemRandom;
5use rsb_derive::*;
6use rvstruct::ValueStruct;
7use secret_vault_value::SecretValue;
8
9#[derive(Debug, Clone, Builder)]
10pub struct RingAeadEncryptionOptions {
11    #[default = "RingAeadEncryptionNonceKind::Random"]
12    pub nonce_kind: RingAeadEncryptionNonceKind,
13}
14
15#[derive(Debug, Clone)]
16pub enum RingAeadEncryptionNonceKind {
17    Random,
18}
19
20pub struct RingAeadEncryption {
21    pub algo: &'static ring::aead::Algorithm,
22    secure_rand: SystemRandom,
23    pub options: RingAeadEncryptionOptions,
24}
25
26impl RingAeadEncryption {
27    pub fn new() -> KmsAeadResult<Self> {
28        Self::with_rand(SystemRandom::new())
29    }
30
31    pub fn with_rand(secure_rand: SystemRandom) -> KmsAeadResult<Self> {
32        Self::with_algorithm(&ring::aead::CHACHA20_POLY1305, secure_rand)
33    }
34
35    pub fn with_algorithm(
36        algo: &'static ring::aead::Algorithm,
37        secure_rand: SystemRandom,
38    ) -> KmsAeadResult<Self> {
39        Self::with_algorithm_options(algo, secure_rand, RingAeadEncryptionOptions::new())
40    }
41
42    pub fn with_options(
43        secure_rand: SystemRandom,
44        options: RingAeadEncryptionOptions,
45    ) -> KmsAeadResult<Self> {
46        Self::with_algorithm_options(&ring::aead::CHACHA20_POLY1305, secure_rand, options)
47    }
48
49    pub fn with_algorithm_options(
50        algo: &'static ring::aead::Algorithm,
51        secure_rand: SystemRandom,
52        options: RingAeadEncryptionOptions,
53    ) -> KmsAeadResult<Self> {
54        Ok(Self {
55            algo,
56            secure_rand,
57            options,
58        })
59    }
60
61    pub fn generate_data_encryption_key(&self) -> KmsAeadResult<DataEncryptionKey> {
62        generate_secret_key(&self.secure_rand, self.algo.key_len())
63    }
64}
65
66#[async_trait]
67impl<Aad> AeadEncryption<Aad> for RingAeadEncryption
68where
69    Aad: AsRef<[u8]> + Send + Sync + 'static,
70{
71    async fn encrypt_value(
72        &self,
73        aad: &Aad,
74        plain_text: &SecretValue,
75        encryption_key: &DataEncryptionKey,
76    ) -> KmsAeadResult<CipherText> {
77        let nonce_data = match self.options.nonce_kind {
78            RingAeadEncryptionNonceKind::Random => generate_random_nonce(&self.secure_rand)?,
79        };
80
81        let encrypted_value = encrypt_with_sealing_key(
82            self.algo,
83            encryption_key,
84            nonce_data.as_slice(),
85            ring::aead::Aad::from(aad),
86            plain_text.ref_sensitive_value().as_slice(),
87        )?;
88
89        let mut encrypted_value_with_nonce: Vec<u8> =
90            Vec::with_capacity(nonce_data.len() + encrypted_value.value().len());
91
92        encrypted_value_with_nonce.extend_from_slice(nonce_data.as_slice());
93
94        encrypted_value_with_nonce.extend_from_slice(encrypted_value.value().as_slice());
95
96        Ok(CipherText(encrypted_value_with_nonce))
97    }
98
99    async fn decrypt_value(
100        &self,
101        aad: &Aad,
102        cipher_text: &CipherText,
103        encryption_key: &DataEncryptionKey,
104    ) -> KmsAeadResult<SecretValue> {
105        if cipher_text.value().len() < ring::aead::NONCE_LEN {
106            return Err(crate::errors::KmsAeadEncryptionError::create(
107                "INVALID_CIPHERTEXT",
108                "Ciphertext too short to contain nonce",
109            ));
110        }
111
112        let (nonce_data, encrypted_part) = cipher_text.value().split_at(ring::aead::NONCE_LEN);
113
114        decrypt_with_opening_key(
115            self.algo,
116            encryption_key,
117            nonce_data,
118            ring::aead::Aad::from(aad),
119            encrypted_part,
120        )
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use crate::types::{CipherText, DataEncryptionKey};
128    use proptest::prelude::*;
129    use proptest::strategy::ValueTree;
130    use proptest::test_runner::TestRunner;
131
132    pub fn generate_secret_value() -> BoxedStrategy<SecretValue> {
133        ("[a-zA-Z0-9]+")
134            .prop_map(|(mock_secret_str)| SecretValue::new(mock_secret_str.as_bytes().to_vec()))
135            .boxed()
136    }
137
138    async fn encryption_test_for(mock_secret_value: SecretValue) {
139        let mock_aad: String = "test".to_string();
140        let secure_rand: SystemRandom = SystemRandom::new();
141
142        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
143
144        let secret_key = encryption.generate_data_encryption_key().unwrap();
145
146        let encrypted_value = encryption
147            .encrypt_value(&mock_aad, &mock_secret_value, &secret_key)
148            .await
149            .unwrap();
150
151        assert_ne!(
152            encrypted_value.value(),
153            mock_secret_value.ref_sensitive_value()
154        );
155
156        let decrypted_value = encryption
157            .decrypt_value(&mock_aad, &encrypted_value, &secret_key)
158            .await
159            .unwrap();
160        assert_eq!(
161            decrypted_value.ref_sensitive_value(),
162            mock_secret_value.ref_sensitive_value()
163        );
164    }
165
166    #[tokio::test]
167    async fn secret_encryption_test() {
168        let mut runner = TestRunner::default();
169        encryption_test_for(
170            generate_secret_value()
171                .new_tree(&mut runner)
172                .unwrap()
173                .current(),
174        )
175        .await
176    }
177
178    #[tokio::test]
179    async fn big_secret_encryption_test() {
180        for sz in vec![5000, 32768, 65535] {
181            encryption_test_for(SecretValue::new("42".repeat(sz).as_bytes().to_vec())).await
182        }
183    }
184
185    #[tokio::test]
186    async fn wrong_secret_name_test_attest() {
187        let mock_aad1: String = "test1".to_string();
188        let mock_aad2: String = "test2".to_string();
189
190        let mock_secret_value = SecretValue::new("42".repeat(1024).as_bytes().to_vec());
191
192        let secure_rand: SystemRandom = SystemRandom::new();
193
194        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
195
196        let secret_key = encryption.generate_data_encryption_key().unwrap();
197
198        let encrypted_value = encryption
199            .encrypt_value(&mock_aad1, &mock_secret_value, &secret_key)
200            .await
201            .unwrap();
202        encryption
203            .decrypt_value(&mock_aad2, &encrypted_value, &secret_key)
204            .await
205            .expect_err("Unable to decrypt data");
206    }
207
208    #[tokio::test]
209    async fn different_encryption_instances_test() {
210        let mock_aad: String = "test1".to_string();
211        let mock_secret_value = SecretValue::new("42".repeat(1024).as_bytes().to_vec());
212
213        let secure_rand: SystemRandom = SystemRandom::new();
214
215        let secret_key =
216            generate_secret_key(&secure_rand, ring::aead::CHACHA20_POLY1305.key_len()).unwrap();
217
218        let encrypted_value = {
219            let encryption = RingAeadEncryption::with_rand(secure_rand.clone()).unwrap();
220            encryption
221                .encrypt_value(&mock_aad, &mock_secret_value, &secret_key)
222                .await
223                .unwrap()
224        };
225
226        let decrypted_value = {
227            let encryption = RingAeadEncryption::with_rand(secure_rand.clone()).unwrap();
228            encryption
229                .decrypt_value(&mock_aad, &encrypted_value, &secret_key)
230                .await
231                .unwrap()
232        };
233
234        assert_eq!(decrypted_value, mock_secret_value)
235    }
236
237    #[tokio::test]
238    async fn empty_plaintext_test() {
239        let mock_aad: String = "test-aad".to_string();
240        let secure_rand: SystemRandom = SystemRandom::new();
241
242        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
243        let secret_key = encryption.generate_data_encryption_key().unwrap();
244
245        let empty_secret = SecretValue::new(Vec::new());
246
247        let encrypted_value = encryption
248            .encrypt_value(&mock_aad, &empty_secret, &secret_key)
249            .await
250            .unwrap();
251
252        let decrypted_value = encryption
253            .decrypt_value(&mock_aad, &encrypted_value, &secret_key)
254            .await
255            .unwrap();
256
257        assert_eq!(decrypted_value.ref_sensitive_value().len(), 0);
258    }
259
260    #[tokio::test]
261    async fn empty_aad_test() {
262        let empty_aad = b"";
263        let secure_rand: SystemRandom = SystemRandom::new();
264
265        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
266        let secret_key = encryption.generate_data_encryption_key().unwrap();
267
268        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
269
270        let encrypted_value = encryption
271            .encrypt_value(&empty_aad, &secret, &secret_key)
272            .await
273            .unwrap();
274
275        let decrypted_value = encryption
276            .decrypt_value(&empty_aad, &encrypted_value, &secret_key)
277            .await
278            .unwrap();
279
280        assert_eq!(decrypted_value, secret);
281    }
282
283    #[tokio::test]
284    async fn binary_aad_test() {
285        // Test with binary AAD containing null bytes and non-ASCII
286        let binary_aad: Vec<u8> = vec![0x00, 0xFF, 0xDE, 0xAD, 0xBE, 0xEF, 0x00];
287        let secure_rand: SystemRandom = SystemRandom::new();
288
289        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
290        let secret_key = encryption.generate_data_encryption_key().unwrap();
291
292        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
293
294        let encrypted_value = encryption
295            .encrypt_value(&binary_aad, &secret, &secret_key)
296            .await
297            .unwrap();
298
299        let decrypted_value = encryption
300            .decrypt_value(&binary_aad, &encrypted_value, &secret_key)
301            .await
302            .unwrap();
303
304        assert_eq!(decrypted_value, secret);
305    }
306
307    #[tokio::test]
308    async fn wrong_key_decryption_fails() {
309        let mock_aad: String = "test-aad".to_string();
310        let secure_rand: SystemRandom = SystemRandom::new();
311
312        let encryption = RingAeadEncryption::with_rand(secure_rand.clone()).unwrap();
313        let secret_key1 = encryption.generate_data_encryption_key().unwrap();
314        let secret_key2 = encryption.generate_data_encryption_key().unwrap();
315
316        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
317
318        let encrypted_value = encryption
319            .encrypt_value(&mock_aad, &secret, &secret_key1)
320            .await
321            .unwrap();
322
323        // Try to decrypt with wrong key
324        let result = encryption
325            .decrypt_value(&mock_aad, &encrypted_value, &secret_key2)
326            .await;
327
328        assert!(result.is_err(), "Decryption with wrong key should fail");
329    }
330
331    #[tokio::test]
332    async fn truncated_ciphertext_fails() {
333        let mock_aad: String = "test-aad".to_string();
334        let secure_rand: SystemRandom = SystemRandom::new();
335
336        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
337        let secret_key = encryption.generate_data_encryption_key().unwrap();
338
339        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
340
341        let encrypted_value = encryption
342            .encrypt_value(&mock_aad, &secret, &secret_key)
343            .await
344            .unwrap();
345
346        // Truncate the ciphertext
347        let mut truncated = encrypted_value.value().to_vec();
348        truncated.truncate(truncated.len() - 5);
349        let truncated_cipher = CipherText(truncated);
350
351        let result = encryption
352            .decrypt_value(&mock_aad, &truncated_cipher, &secret_key)
353            .await;
354
355        assert!(
356            result.is_err(),
357            "Decryption of truncated ciphertext should fail"
358        );
359    }
360
361    #[tokio::test]
362    async fn modified_ciphertext_fails() {
363        let mock_aad: String = "test-aad".to_string();
364        let secure_rand: SystemRandom = SystemRandom::new();
365
366        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
367        let secret_key = encryption.generate_data_encryption_key().unwrap();
368
369        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
370
371        let encrypted_value = encryption
372            .encrypt_value(&mock_aad, &secret, &secret_key)
373            .await
374            .unwrap();
375
376        // Flip a bit in the ciphertext (after nonce)
377        let mut modified = encrypted_value.value().to_vec();
378        if modified.len() > ring::aead::NONCE_LEN + 1 {
379            modified[ring::aead::NONCE_LEN + 1] ^= 0x01;
380        }
381        let modified_cipher = CipherText(modified);
382
383        let result = encryption
384            .decrypt_value(&mock_aad, &modified_cipher, &secret_key)
385            .await;
386
387        assert!(
388            result.is_err(),
389            "Decryption of modified ciphertext should fail"
390        );
391    }
392
393    #[tokio::test]
394    async fn modified_nonce_fails() {
395        let mock_aad: String = "test-aad".to_string();
396        let secure_rand: SystemRandom = SystemRandom::new();
397
398        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
399        let secret_key = encryption.generate_data_encryption_key().unwrap();
400
401        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
402
403        let encrypted_value = encryption
404            .encrypt_value(&mock_aad, &secret, &secret_key)
405            .await
406            .unwrap();
407
408        // Modify the nonce (first bytes)
409        let mut modified = encrypted_value.value().to_vec();
410        modified[0] ^= 0x01;
411        let modified_cipher = CipherText(modified);
412
413        let result = encryption
414            .decrypt_value(&mock_aad, &modified_cipher, &secret_key)
415            .await;
416
417        assert!(
418            result.is_err(),
419            "Decryption with modified nonce should fail"
420        );
421    }
422
423    #[tokio::test]
424    async fn too_short_ciphertext_fails() {
425        let mock_aad: String = "test-aad".to_string();
426        let secure_rand: SystemRandom = SystemRandom::new();
427
428        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
429        let secret_key = encryption.generate_data_encryption_key().unwrap();
430
431        // Ciphertext shorter than nonce length
432        let short_cipher = CipherText(vec![0u8; ring::aead::NONCE_LEN - 1]);
433
434        let result = encryption
435            .decrypt_value(&mock_aad, &short_cipher, &secret_key)
436            .await;
437
438        assert!(
439            result.is_err(),
440            "Decryption of too-short ciphertext should fail"
441        );
442    }
443
444    #[tokio::test]
445    async fn test_aes_256_gcm_algorithm() {
446        let mock_aad: String = "test-aad".to_string();
447        let secure_rand: SystemRandom = SystemRandom::new();
448
449        let encryption =
450            RingAeadEncryption::with_algorithm(&ring::aead::AES_256_GCM, secure_rand).unwrap();
451
452        let secret_key = encryption.generate_data_encryption_key().unwrap();
453        let secret = SecretValue::new("test-secret".as_bytes().to_vec());
454
455        let encrypted_value = encryption
456            .encrypt_value(&mock_aad, &secret, &secret_key)
457            .await
458            .unwrap();
459
460        let decrypted_value = encryption
461            .decrypt_value(&mock_aad, &encrypted_value, &secret_key)
462            .await
463            .unwrap();
464
465        assert_eq!(decrypted_value, secret);
466    }
467
468    #[test]
469    fn test_constant_time_cipher_text_comparison() {
470        let cipher1 = CipherText(vec![1, 2, 3, 4, 5]);
471        let cipher2 = CipherText(vec![1, 2, 3, 4, 5]);
472        let cipher3 = CipherText(vec![1, 2, 3, 4, 6]);
473
474        // PartialEq uses constant-time comparison internally
475        assert_eq!(cipher1, cipher2);
476        assert_ne!(cipher1, cipher3);
477    }
478
479    #[test]
480    fn test_constant_time_dek_comparison() {
481        let dek1 = DataEncryptionKey::from(SecretValue::new(vec![1, 2, 3, 4, 5]));
482        let dek2 = DataEncryptionKey::from(SecretValue::new(vec![1, 2, 3, 4, 5]));
483        let dek3 = DataEncryptionKey::from(SecretValue::new(vec![1, 2, 3, 4, 6]));
484
485        // PartialEq uses constant-time comparison internally
486        assert_eq!(dek1, dek2);
487        assert_ne!(dek1, dek3);
488    }
489
490    #[tokio::test]
491    async fn concurrent_encryption_test() {
492        use tokio::task;
493
494        let secure_rand: SystemRandom = SystemRandom::new();
495        let encryption = RingAeadEncryption::with_rand(secure_rand).unwrap();
496        let secret_key = encryption.generate_data_encryption_key().unwrap();
497
498        let mut handles = vec![];
499        for i in 0..10 {
500            let encryption_clone = RingAeadEncryption::with_rand(SystemRandom::new()).unwrap();
501            let key_clone = secret_key.clone();
502            let handle = task::spawn(async move {
503                let secret = SecretValue::new(format!("secret-{}", i).as_bytes().to_vec());
504                let aad = format!("aad-{}", i);
505
506                let encrypted = encryption_clone
507                    .encrypt_value(&aad, &secret, &key_clone)
508                    .await
509                    .unwrap();
510
511                let decrypted = encryption_clone
512                    .decrypt_value(&aad, &encrypted, &key_clone)
513                    .await
514                    .unwrap();
515
516                assert_eq!(decrypted, secret);
517            });
518            handles.push(handle);
519        }
520
521        for handle in handles {
522            handle.await.unwrap();
523        }
524    }
525}