1use crate::backend::{CipherKey, NONCE_LEN};
2use crate::Key;
3use std::any::Any;
4use vitaminc_aead::{
5 Cipher, CipherTextBuilder, Decipher, DecipherVisitor, Decrypt, Encrypt, IntoAad,
6 LocalCipherText, MapAccess, MapCipher, NonceGenerator, RandomNonceGenerator, SeqAccess,
7 SeqCipher, Unspecified,
8};
9use vitaminc_protected::Protected;
10
11#[derive(Debug)]
18pub enum AesCipherText {
19 Single(LocalCipherText),
21 Sequence(Vec<AesCipherText>),
23 Map(Vec<(String, AesCipherText)>),
26 None(LocalCipherText),
29 Passthrough(Box<dyn Any + Send + 'static>),
35}
36
37pub struct Aes256Cipher {
40 pub(crate) nonce_generator: RandomNonceGenerator<NONCE_LEN>,
41 pub(crate) key: CipherKey,
42}
43
44impl Aes256Cipher {
45 pub fn new(key: &Key) -> Result<Self, Unspecified> {
51 Ok(Self {
52 nonce_generator: RandomNonceGenerator::init()?,
53 key: key.cipher_key()?,
54 })
55 }
56}
57
58impl<'c> Cipher for &'c Aes256Cipher {
59 type Ok = AesCipherText;
60 type Error = Unspecified;
61 type SeqCipher = AesSeqCipher<'c>;
62 type MapCipher = AesMapCipher<'c>;
63
64 fn encrypt_bytes_vec<'a, A>(
65 self,
66 data: Protected<Vec<u8>>,
67 aad: A,
68 ) -> Result<Self::Ok, Self::Error>
69 where
70 A: IntoAad<'a>,
71 {
72 let nonce = self.nonce_generator.generate()?;
73 let nonce_bytes: [u8; NONCE_LEN] = nonce.as_ref().try_into().map_err(|_| Unspecified)?;
74 let aad = aad.into_aad();
75
76 CipherTextBuilder::new()
77 .append_nonce(nonce)
78 .append_target_plaintext(data)
79 .accepts_ciphertext_and_tag_ok(|mut buf| {
80 self.key
81 .seal(&nonce_bytes, aad.as_bytes(), &mut buf)
82 .map(|()| buf)
83 })
84 .build()
85 .map(AesCipherText::Single)
86 }
87
88 fn encrypt_seq(self, size_hint: Option<usize>) -> Self::SeqCipher {
89 AesSeqCipher {
90 cipher: self,
91 items: Vec::with_capacity(size_hint.unwrap_or(0)),
92 }
93 }
94
95 fn encrypt_map(self) -> Self::MapCipher {
96 AesMapCipher {
97 cipher: self,
98 entries: Vec::new(),
99 current_key: None,
100 }
101 }
102
103 fn encrypt_none<'a, A>(self, aad: A) -> Result<Self::Ok, Self::Error>
104 where
105 A: IntoAad<'a>,
106 {
107 let nonce = self.nonce_generator.generate()?;
108 let nonce_bytes: [u8; NONCE_LEN] = nonce.as_ref().try_into().map_err(|_| Unspecified)?;
109 let aad = aad.into_aad();
110
111 CipherTextBuilder::new()
112 .append_nonce(nonce)
113 .append_target_plaintext(Vec::<u8>::new())
114 .accepts_ciphertext_and_tag_ok(|mut buf| {
115 self.key
116 .seal(&nonce_bytes, aad.as_bytes(), &mut buf)
117 .map(|()| buf)
118 })
119 .build()
120 .map(AesCipherText::None)
121 }
122
123 fn passthrough<T>(self, value: T) -> Result<Self::Ok, Self::Error>
124 where
125 T: Any + Send + 'static,
126 {
127 Ok(AesCipherText::Passthrough(Box::new(value)))
128 }
129}
130
131pub struct AesSeqCipher<'c> {
135 cipher: &'c Aes256Cipher,
136 items: Vec<AesCipherText>,
137}
138
139impl<'c> SeqCipher for AesSeqCipher<'c> {
140 type Ok = AesCipherText;
141 type Error = Unspecified;
142
143 fn encrypt_next<'a, T, A>(mut self, data: T, aad: A) -> Result<Self, Self::Error>
144 where
145 T: Encrypt,
146 A: IntoAad<'a>,
147 {
148 let encrypted = data.encrypt_with_aad(self.cipher, aad)?;
149 self.items.push(encrypted);
150 Ok(self)
151 }
152
153 fn passthrough_next<T>(mut self, value: T) -> Result<Self, Self::Error>
154 where
155 T: Any + Send + 'static,
156 {
157 self.items.push(AesCipherText::Passthrough(Box::new(value)));
158 Ok(self)
159 }
160
161 fn end(self) -> Result<Self::Ok, Self::Error> {
162 Ok(AesCipherText::Sequence(self.items))
163 }
164}
165
166pub struct AesMapCipher<'c> {
182 cipher: &'c Aes256Cipher,
183 entries: Vec<(String, AesCipherText)>,
184 current_key: Option<&'static str>,
185}
186
187impl<'c> MapCipher for AesMapCipher<'c> {
188 type Ok = AesCipherText;
189 type Error = Unspecified;
190
191 fn encrypt_key(mut self, key: &'static str) -> Result<Self, Self::Error> {
192 if self.current_key.is_some() {
196 return Err(Unspecified);
197 }
198 self.current_key = Some(key);
199 Ok(self)
200 }
201
202 fn encrypt_value<'a, U, A>(mut self, value: U, aad: A) -> Result<Self, Self::Error>
203 where
204 U: Encrypt,
205 A: IntoAad<'a>,
206 {
207 let key = self.current_key.take().ok_or(Unspecified)?;
208 let encrypted = value.encrypt_with_aad(self.cipher, aad)?;
209 self.entries.push((key.to_string(), encrypted));
210 Ok(self)
211 }
212
213 fn passthrough_entry<T>(mut self, key: &'static str, value: T) -> Result<Self, Self::Error>
214 where
215 T: Any + Send + 'static,
216 {
217 if self.current_key.is_some() {
222 return Err(Unspecified);
223 }
224 self.entries
225 .push((key.to_string(), AesCipherText::Passthrough(Box::new(value))));
226 Ok(self)
227 }
228
229 fn end(self) -> Result<Self::Ok, Self::Error> {
230 if self.current_key.is_some() {
232 return Err(Unspecified);
233 }
234 Ok(AesCipherText::Map(self.entries))
235 }
236}
237
238impl Aes256Cipher {
239 pub fn decrypt<'c, T: Decrypt<'c> + 'c>(
242 &'c self,
243 ciphertext: AesCipherText,
244 ) -> Result<T, Unspecified> {
245 self.decrypt_with_aad(ciphertext, ())
246 }
247
248 pub fn decrypt_with_aad<'c, 'a, T, A>(
253 &'c self,
254 ciphertext: AesCipherText,
255 aad: A,
256 ) -> Result<T, Unspecified>
257 where
258 T: Decrypt<'c> + 'c,
259 A: IntoAad<'a>,
260 {
261 let aad = aad.into_aad();
262 T::decrypt(AesDecipher {
263 cipher: self,
264 ciphertext,
265 aad: aad.as_bytes().to_vec(),
266 })
267 }
268}
269
270struct AesDecipher<'c> {
271 cipher: &'c Aes256Cipher,
272 ciphertext: AesCipherText,
273 aad: Vec<u8>,
274}
275
276impl AesDecipher<'_> {
277 fn decrypt_local_ciphertext(
278 cipher: &Aes256Cipher,
279 ct: LocalCipherText,
280 aad: &[u8],
281 ) -> Result<Protected<Vec<u8>>, Unspecified> {
282 let (nonce, reader) = ct.into_reader().read_nonce::<NONCE_LEN>()?;
283 let nonce_bytes = nonce.into_inner();
284
285 reader
286 .accepts_plaintext_ok(|data| cipher.key.open(&nonce_bytes, aad, data))
287 .read()
288 }
289}
290
291impl<'c> Decipher<'c> for AesDecipher<'c> {
292 type Ok<T>
293 = Result<T, Unspecified>
294 where
295 T: Send + 'c;
296
297 fn map_ok<T, U, F>(ok: Self::Ok<T>, f: F) -> Self::Ok<U>
298 where
299 T: Send + 'c,
300 U: Send + 'c,
301 F: FnOnce(T) -> U,
302 {
303 ok.map(f)
304 }
305
306 fn decrypt_bytes<V: DecipherVisitor<'c> + Send + 'c>(self, visitor: V) -> Self::Ok<V::Value> {
307 match self.ciphertext {
308 AesCipherText::Single(ct) => {
309 let bytes = Self::decrypt_local_ciphertext(self.cipher, ct, &self.aad)?;
310 visitor.visit_bytes_vec(bytes)
311 }
312 _ => Err(Unspecified),
313 }
314 }
315
316 fn decrypt_seq<V: DecipherVisitor<'c> + Send + 'c>(self, visitor: V) -> Self::Ok<V::Value> {
317 match self.ciphertext {
318 AesCipherText::Sequence(items) => {
319 let seq_access = AesSeqAccess {
320 cipher: self.cipher,
321 items: items.into_iter(),
322 aad: self.aad,
323 };
324 visitor.visit_seq(seq_access)
325 }
326 _ => Err(Unspecified),
327 }
328 }
329
330 fn decrypt_map<V: DecipherVisitor<'c> + Send + 'c>(self, visitor: V) -> Self::Ok<V::Value> {
331 match self.ciphertext {
332 AesCipherText::Map(entries) => {
333 let map_access = AesMapAccess {
334 cipher: self.cipher,
335 entries: entries.into_iter(),
336 aad: self.aad,
337 };
338 visitor.visit_map(map_access)
339 }
340 _ => Err(Unspecified),
341 }
342 }
343
344 fn decrypt_passthrough<T>(self) -> Self::Ok<T>
345 where
346 T: Any + Send + 'static,
347 {
348 match self.ciphertext {
349 AesCipherText::Passthrough(boxed) => {
350 boxed.downcast::<T>().map(|b| *b).map_err(|_| Unspecified)
351 }
352 _ => Err(Unspecified),
353 }
354 }
355
356 fn decrypt_option<T>(self) -> Self::Ok<Option<T>>
357 where
358 T: Decrypt<'c> + 'c,
359 {
360 match self.ciphertext {
361 AesCipherText::None(ct) => {
362 Self::decrypt_local_ciphertext(self.cipher, ct, &self.aad)?;
364 Ok(None)
365 }
366 AesCipherText::Passthrough(_) => Err(Unspecified),
368 other => {
381 let inner = AesDecipher {
382 cipher: self.cipher,
383 ciphertext: other,
384 aad: self.aad,
385 };
386 T::decrypt(inner).map(Some)
387 }
388 }
389 }
390}
391
392struct AesSeqAccess<'c> {
393 cipher: &'c Aes256Cipher,
394 items: std::vec::IntoIter<AesCipherText>,
395 aad: Vec<u8>,
396}
397
398impl<'c> SeqAccess<'c> for AesSeqAccess<'c> {
399 type Error = Unspecified;
400
401 fn next_element<T: Decrypt<'c> + 'c>(&mut self) -> Result<Option<T>, Self::Error> {
402 let ct = match self.items.next() {
403 Some(ct) => ct,
404 None => return Ok(None),
405 };
406 let decipher = AesDecipher {
407 cipher: self.cipher,
408 ciphertext: ct,
409 aad: self.aad.clone(),
410 };
411 T::decrypt(decipher).map(Some)
412 }
413}
414
415struct AesMapAccess<'c> {
416 cipher: &'c Aes256Cipher,
417 entries: std::vec::IntoIter<(String, AesCipherText)>,
418 aad: Vec<u8>,
419}
420
421impl<'c> MapAccess<'c> for AesMapAccess<'c> {
422 type Error = Unspecified;
423
424 fn next_entry<T: Decrypt<'c> + 'c>(&mut self) -> Result<Option<(String, T)>, Self::Error> {
425 let (key, ct) = match self.entries.next() {
426 Some(entry) => entry,
427 None => return Ok(None),
428 };
429 let decipher = AesDecipher {
430 cipher: self.cipher,
431 ciphertext: ct,
432 aad: self.aad.clone(),
433 };
434 let value = T::decrypt(decipher)?;
435 Ok(Some((key, value)))
436 }
437}
438
439#[cfg(all(test, not(target_arch = "wasm32")))]
445#[allow(clippy::unwrap_used)]
446mod test {
447 use super::*;
448 use crate::key::tests::DifferingKeyPair;
449 use quickcheck_macros::quickcheck;
450 use std::collections::HashMap;
451 use vitaminc_aead::Encrypt;
452
453 #[quickcheck]
454 fn roundtrip_byte_array(key: Key, plaintext: [u8; 16]) -> bool {
455 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
456 let ciphertext = plaintext.encrypt(&cipher).expect("Encryption failed");
457 let decrypted: [u8; 16] = cipher.decrypt(ciphertext).expect("Decryption failed");
458 decrypted == plaintext
459 }
460
461 #[quickcheck]
462 fn roundtrip_string(key: Key, plaintext: String) -> bool {
463 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
464 let ciphertext = plaintext
465 .clone()
466 .encrypt(&cipher)
467 .expect("Encryption failed");
468 let decrypted: String = cipher.decrypt(ciphertext).expect("Decryption failed");
469 decrypted == plaintext
470 }
471
472 #[quickcheck]
473 fn roundtrip_str(key: Key, plaintext: String) -> bool {
474 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
475 let ciphertext = plaintext
476 .as_str()
477 .encrypt(&cipher)
478 .expect("Encryption failed");
479 let decrypted: String = cipher.decrypt(ciphertext).expect("Decryption failed");
480 decrypted == plaintext
481 }
482
483 #[quickcheck]
484 fn roundtrip_u32(key: Key, plaintext: u32) -> bool {
485 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
486 let ciphertext = plaintext.encrypt(&cipher).expect("Encryption failed");
487 let decrypted: u32 = cipher.decrypt(ciphertext).expect("Decryption failed");
488 decrypted == plaintext
489 }
490
491 #[quickcheck]
492 fn roundtrip_vec_of_strings(key: Key, plaintext: Vec<String>) -> bool {
493 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
494 let ciphertext = plaintext
495 .clone()
496 .encrypt(&cipher)
497 .expect("Encryption failed");
498 let decrypted: Vec<String> = cipher.decrypt(ciphertext).expect("Decryption failed");
499 decrypted == plaintext
500 }
501
502 #[quickcheck]
503 fn roundtrip_string_with_aad(key: Key, plaintext: String) -> bool {
504 let aad = "test-aad";
505 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
506 let ciphertext = plaintext
507 .clone()
508 .encrypt_with_aad(&cipher, aad)
509 .expect("Encryption failed");
510 let decrypted: String = cipher
511 .decrypt_with_aad(ciphertext, aad)
512 .expect("Decryption failed");
513 decrypted == plaintext
514 }
515
516 #[quickcheck]
517 fn decrypt_fails_with_wrong_aad(key: Key, plaintext: String) -> bool {
518 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
519 let ciphertext = plaintext
520 .encrypt_with_aad(&cipher, "correct-aad")
521 .expect("Encryption failed");
522 cipher
523 .decrypt_with_aad::<String, _>(ciphertext, "wrong-aad")
524 .is_err()
525 }
526
527 #[quickcheck]
528 fn decrypt_fails_with_wrong_key(keys: DifferingKeyPair, plaintext: String) -> bool {
529 let DifferingKeyPair(key_a, key_b) = keys;
532 let cipher_a = Aes256Cipher::new(&key_a).expect("Failed to create cipher A");
533 let cipher_b = Aes256Cipher::new(&key_b).expect("Failed to create cipher B");
534 let ciphertext = plaintext.encrypt(&cipher_a).expect("Encryption failed");
535 cipher_b.decrypt::<String>(ciphertext).is_err()
536 }
537
538 #[test]
539 fn roundtrip_hashmap() {
540 let key = Key::from([42u8; 32]);
541 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
542
543 let mut plaintext = HashMap::new();
544 plaintext.insert("name", "Alice");
545 plaintext.insert("city", "Sydney");
546
547 let ciphertext = plaintext.encrypt(&cipher).expect("Encryption failed");
548 let decrypted: HashMap<String, String> =
549 cipher.decrypt(ciphertext).expect("Decryption failed");
550
551 assert_eq!(decrypted.len(), 2);
552 assert_eq!(decrypted.get("name").unwrap(), "Alice");
553 assert_eq!(decrypted.get("city").unwrap(), "Sydney");
554 }
555
556 #[test]
557 fn roundtrip_hashmap_with_aad() {
558 let key = Key::from([42u8; 32]);
559 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
560 let aad = "map-context";
561
562 let mut plaintext = HashMap::new();
563 plaintext.insert("name", "Alice");
564 plaintext.insert("city", "Sydney");
565
566 let ciphertext = plaintext
567 .encrypt_with_aad(&cipher, aad)
568 .expect("Encryption failed");
569 let decrypted: HashMap<String, String> = cipher
570 .decrypt_with_aad(ciphertext, aad)
571 .expect("Decryption failed");
572
573 assert_eq!(decrypted.len(), 2);
574 assert_eq!(decrypted.get("name").unwrap(), "Alice");
575 assert_eq!(decrypted.get("city").unwrap(), "Sydney");
576 }
577
578 #[test]
579 fn decrypt_hashmap_fails_with_wrong_aad() {
580 let key = Key::from([42u8; 32]);
581 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
582
583 let mut plaintext = HashMap::new();
584 plaintext.insert("name", "Alice");
585
586 let ciphertext = plaintext
587 .encrypt_with_aad(&cipher, "correct-aad")
588 .expect("Encryption failed");
589 assert!(cipher
590 .decrypt_with_aad::<HashMap<String, String>, _>(ciphertext, "wrong-aad")
591 .is_err());
592 }
593
594 #[quickcheck]
595 fn roundtrip_protected_string(key: Key, plaintext: String) -> bool {
596 use vitaminc_protected::{Controlled, Protected};
597
598 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
599 let protected = Protected::new(plaintext.clone());
600 let ciphertext = protected.encrypt(&cipher).expect("Encryption failed");
601 let decrypted: Protected<String> = cipher.decrypt(ciphertext).expect("Decryption failed");
602 decrypted.risky_unwrap() == plaintext
603 }
604
605 #[quickcheck]
606 fn roundtrip_option_some_string(key: Key, plaintext: String) -> bool {
607 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
608 let value = Some(plaintext.clone());
609 let ciphertext = value.encrypt(&cipher).expect("Encryption failed");
610 let decrypted: Option<String> = cipher.decrypt(ciphertext).expect("Decryption failed");
611 decrypted == Some(plaintext)
612 }
613
614 #[test]
615 fn roundtrip_option_none_string() {
616 let key = Key::from([7u8; 32]);
617 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
618 let value: Option<String> = None;
619 let ciphertext = value.encrypt(&cipher).expect("Encryption failed");
620 let decrypted: Option<String> = cipher.decrypt(ciphertext).expect("Decryption failed");
621 assert_eq!(decrypted, None);
622 }
623
624 #[quickcheck]
625 fn roundtrip_option_some_with_aad(key: Key, plaintext: String) -> bool {
626 let aad = "opt-aad";
627 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
628 let ciphertext = Some(plaintext.clone())
629 .encrypt_with_aad(&cipher, aad)
630 .expect("Encryption failed");
631 let decrypted: Option<String> = cipher
632 .decrypt_with_aad(ciphertext, aad)
633 .expect("Decryption failed");
634 decrypted == Some(plaintext)
635 }
636
637 #[quickcheck]
638 fn roundtrip_option_none_with_aad(key: Key) -> bool {
639 let aad = "opt-none-aad";
640 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
641 let ciphertext = None::<String>
642 .encrypt_with_aad(&cipher, aad)
643 .expect("Encryption failed");
644 let decrypted: Option<String> = cipher
645 .decrypt_with_aad(ciphertext, aad)
646 .expect("Decryption failed");
647 decrypted.is_none()
648 }
649
650 #[quickcheck]
651 fn decrypt_option_none_fails_with_wrong_aad(key: Key) -> bool {
652 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
653 let ciphertext = None::<String>
654 .encrypt_with_aad(&cipher, "correct")
655 .expect("Encryption failed");
656 cipher
657 .decrypt_with_aad::<Option<String>, _>(ciphertext, "wrong")
658 .is_err()
659 }
660
661 #[test]
662 fn decrypt_string_rejects_none_ciphertext() {
663 let key = Key::from([9u8; 32]);
666 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
667 let ciphertext = None::<String>.encrypt(&cipher).expect("Encryption failed");
668 assert!(cipher.decrypt::<String>(ciphertext).is_err());
669 }
670
671 #[test]
672 fn decrypt_option_rejects_passthrough_ciphertext() {
673 let key = Key::from([11u8; 32]);
675 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
676 let ciphertext = (&cipher).passthrough(42u32).expect("passthrough failed");
677 assert!(cipher.decrypt::<Option<u32>>(ciphertext).is_err());
678 }
679
680 #[test]
681 fn roundtrip_passthrough_u32() {
682 let key = Key::from([1u8; 32]);
683 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
684 let ciphertext = (&cipher).passthrough(12345u32).expect("passthrough failed");
685 let decrypted: u32 = decrypt_passthrough_via(&cipher, ciphertext).expect("decode failed");
686 assert_eq!(decrypted, 12345u32);
687 }
688
689 #[test]
690 fn roundtrip_passthrough_string() {
691 let key = Key::from([2u8; 32]);
692 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
693 let ciphertext = (&cipher)
694 .passthrough(String::from("version-tag"))
695 .expect("passthrough failed");
696 let decrypted: String =
697 decrypt_passthrough_via(&cipher, ciphertext).expect("decode failed");
698 assert_eq!(decrypted, "version-tag");
699 }
700
701 #[test]
702 fn passthrough_type_mismatch_returns_err() {
703 let key = Key::from([3u8; 32]);
704 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
705 let ciphertext = (&cipher).passthrough(42u32).expect("passthrough failed");
706 let result: Result<String, _> = decrypt_passthrough_via(&cipher, ciphertext);
707 assert!(result.is_err());
708 }
709
710 fn decrypt_passthrough_via<T>(
714 cipher: &Aes256Cipher,
715 ciphertext: AesCipherText,
716 ) -> Result<T, Unspecified>
717 where
718 T: Any + Send + 'static,
719 {
720 let decipher = AesDecipher {
721 cipher,
722 ciphertext,
723 aad: Vec::new(),
724 };
725 decipher.decrypt_passthrough::<T>()
726 }
727
728 #[quickcheck]
729 fn roundtrip_vec_of_option_string(key: Key, items: Vec<Option<String>>) -> bool {
730 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
731 let ciphertext = items.clone().encrypt(&cipher).expect("Encryption failed");
732 let decrypted: Vec<Option<String>> = cipher.decrypt(ciphertext).expect("Decryption failed");
733 decrypted == items
734 }
735
736 #[test]
744 fn encrypt_key_twice_without_value_fails() {
745 let key = Key::from([42u8; 32]);
746 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
747 let map = (&cipher).encrypt_map().encrypt_key("first").unwrap();
748 assert!(map.encrypt_key("second").is_err());
749 }
750
751 #[test]
752 fn encrypt_value_without_pending_key_fails() {
753 let key = Key::from([42u8; 32]);
754 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
755 let map = (&cipher).encrypt_map();
756 assert!(map.encrypt_value("orphan-value", ()).is_err());
757 }
758
759 #[test]
760 fn passthrough_entry_with_pending_key_fails() {
761 let key = Key::from([42u8; 32]);
762 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
763 let map = (&cipher).encrypt_map().encrypt_key("pending").unwrap();
764 assert!(map.passthrough_entry("other", 42u32).is_err());
766 }
767
768 #[test]
769 fn end_with_pending_key_fails() {
770 let key = Key::from([42u8; 32]);
771 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
772 let map = (&cipher).encrypt_map().encrypt_key("pending").unwrap();
773 assert!(map.end().is_err());
774 }
775
776 #[test]
777 fn passthrough_entry_succeeds_with_no_pending_key() {
778 let key = Key::from([42u8; 32]);
779 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
780 let ciphertext = (&cipher)
782 .encrypt_map()
783 .passthrough_entry("version", 1u32)
784 .and_then(|m| m.end())
785 .expect("passthrough_entry should succeed without a pending key");
786 match ciphertext {
787 AesCipherText::Map(entries) => {
788 assert_eq!(entries.len(), 1);
789 assert_eq!(entries[0].0, "version");
790 assert!(matches!(entries[0].1, AesCipherText::Passthrough(_)));
791 }
792 _ => panic!("expected Map ciphertext"),
793 }
794 }
795
796 #[quickcheck]
799 fn nonce_is_unique_across_encryptions(key: Key, plaintext: String) -> bool {
800 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
808 let ct1 = plaintext
809 .clone()
810 .encrypt(&cipher)
811 .expect("Encryption failed");
812 let ct2 = plaintext.encrypt(&cipher).expect("Encryption failed");
813 match (ct1, ct2) {
814 (AesCipherText::Single(a), AesCipherText::Single(b)) => a.as_ref() != b.as_ref(),
815 _ => panic!("expected Single ciphertexts"),
816 }
817 }
818
819 fn single_ct(cipher: &Aes256Cipher) -> AesCipherText {
829 "single".to_string().encrypt(cipher).expect("encrypt")
830 }
831 fn sequence_ct(cipher: &Aes256Cipher) -> AesCipherText {
832 vec!["a".to_string(), "b".to_string()]
833 .encrypt(cipher)
834 .expect("encrypt")
835 }
836 fn map_ct(cipher: &Aes256Cipher) -> AesCipherText {
837 let mut m = HashMap::new();
838 m.insert("k", "v");
839 m.encrypt(cipher).expect("encrypt")
840 }
841 fn none_ct(cipher: &Aes256Cipher) -> AesCipherText {
842 None::<String>.encrypt(cipher).expect("encrypt")
843 }
844 fn passthrough_ct(cipher: &Aes256Cipher) -> AesCipherText {
845 cipher.passthrough(7u32).expect("passthrough")
846 }
847
848 #[test]
849 fn decrypt_bytes_rejects_non_single_variants() {
850 let cipher = Aes256Cipher::new(&Key::from([20u8; 32])).expect("Failed to create cipher");
851 assert!(
853 cipher.decrypt::<String>(sequence_ct(&cipher)).is_err(),
854 "Sequence"
855 );
856 assert!(cipher.decrypt::<String>(map_ct(&cipher)).is_err(), "Map");
857 assert!(cipher.decrypt::<String>(none_ct(&cipher)).is_err(), "None");
858 assert!(
859 cipher.decrypt::<String>(passthrough_ct(&cipher)).is_err(),
860 "Passthrough"
861 );
862 }
863
864 #[test]
865 fn decrypt_seq_rejects_non_sequence_variants() {
866 let cipher = Aes256Cipher::new(&Key::from([21u8; 32])).expect("Failed to create cipher");
867 assert!(
869 cipher.decrypt::<Vec<String>>(single_ct(&cipher)).is_err(),
870 "Single"
871 );
872 assert!(
873 cipher.decrypt::<Vec<String>>(map_ct(&cipher)).is_err(),
874 "Map"
875 );
876 assert!(
877 cipher.decrypt::<Vec<String>>(none_ct(&cipher)).is_err(),
878 "None"
879 );
880 assert!(
881 cipher
882 .decrypt::<Vec<String>>(passthrough_ct(&cipher))
883 .is_err(),
884 "Passthrough"
885 );
886 }
887
888 #[test]
889 fn decrypt_map_rejects_non_map_variants() {
890 let cipher = Aes256Cipher::new(&Key::from([22u8; 32])).expect("Failed to create cipher");
891 assert!(
893 cipher
894 .decrypt::<HashMap<String, String>>(single_ct(&cipher))
895 .is_err(),
896 "Single"
897 );
898 assert!(
899 cipher
900 .decrypt::<HashMap<String, String>>(sequence_ct(&cipher))
901 .is_err(),
902 "Sequence"
903 );
904 assert!(
905 cipher
906 .decrypt::<HashMap<String, String>>(none_ct(&cipher))
907 .is_err(),
908 "None"
909 );
910 assert!(
911 cipher
912 .decrypt::<HashMap<String, String>>(passthrough_ct(&cipher))
913 .is_err(),
914 "Passthrough"
915 );
916 }
917
918 #[test]
919 fn decrypt_passthrough_rejects_non_passthrough_variants() {
920 let cipher = Aes256Cipher::new(&Key::from([23u8; 32])).expect("Failed to create cipher");
921 assert!(
923 decrypt_passthrough_via::<u32>(&cipher, single_ct(&cipher)).is_err(),
924 "Single"
925 );
926 assert!(
927 decrypt_passthrough_via::<u32>(&cipher, sequence_ct(&cipher)).is_err(),
928 "Sequence"
929 );
930 assert!(
931 decrypt_passthrough_via::<u32>(&cipher, map_ct(&cipher)).is_err(),
932 "Map"
933 );
934 assert!(
935 decrypt_passthrough_via::<u32>(&cipher, none_ct(&cipher)).is_err(),
936 "None"
937 );
938 }
939
940 #[quickcheck]
948 fn roundtrip_option_some_u32(key: Key, value: u32) -> bool {
949 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
950 let ct = Some(value).encrypt(&cipher).expect("Encryption failed");
951 cipher
952 .decrypt::<Option<u32>>(ct)
953 .expect("Decryption failed")
954 == Some(value)
955 }
956
957 #[quickcheck]
958 fn roundtrip_option_some_vec_of_strings(items: Vec<String>) -> bool {
959 let cipher = Aes256Cipher::new(&Key::from([31u8; 32])).expect("Failed to create cipher");
960 let ct = Some(items.clone())
961 .encrypt(&cipher)
962 .expect("Encryption failed");
963 cipher
964 .decrypt::<Option<Vec<String>>>(ct)
965 .expect("Decryption failed")
966 == Some(items)
967 }
968
969 #[test]
970 fn roundtrip_option_some_hashmap() {
971 let cipher = Aes256Cipher::new(&Key::from([32u8; 32])).expect("Failed to create cipher");
972 let mut m = HashMap::new();
973 m.insert("name", "Alice");
974 let ct = Some(m).encrypt(&cipher).expect("Encryption failed");
975 let decoded: Option<HashMap<String, String>> =
976 cipher.decrypt(ct).expect("Decryption failed");
977 assert_eq!(
978 decoded.unwrap().get("name").map(String::as_str),
979 Some("Alice")
980 );
981 }
982
983 #[quickcheck]
984 fn roundtrip_option_some_protected_string(plaintext: String) -> bool {
985 use vitaminc_protected::{Controlled, Protected};
986 let cipher = Aes256Cipher::new(&Key::from([33u8; 32])).expect("Failed to create cipher");
987 let ct = Some(Protected::new(plaintext.clone()))
988 .encrypt(&cipher)
989 .expect("Encryption failed");
990 let decoded: Option<Protected<String>> = cipher.decrypt(ct).expect("Decryption failed");
991 decoded.map(Controlled::risky_unwrap) == Some(plaintext)
992 }
993
994 #[quickcheck]
995 fn decrypt_option_some_fails_with_wrong_aad(plaintext: String) -> bool {
996 let cipher = Aes256Cipher::new(&Key::from([34u8; 32])).expect("Failed to create cipher");
999 let ct = Some(plaintext)
1000 .encrypt_with_aad(&cipher, "correct")
1001 .expect("Encryption failed");
1002 cipher
1003 .decrypt_with_aad::<Option<String>, _>(ct, "wrong")
1004 .is_err()
1005 }
1006
1007 #[test]
1008 fn nested_option_shape_is_caller_decided() {
1009 let cipher = Aes256Cipher::new(&Key::from([35u8; 32])).expect("Failed to create cipher");
1014 let outer: Option<Option<String>> = Some(Some("x".into()));
1015 let ct = outer.encrypt(&cipher).expect("Encryption failed");
1016 let decoded: Option<String> = cipher.decrypt(ct).expect("Decryption failed");
1017 assert_eq!(decoded, Some("x".into()));
1018 }
1019
1020 #[test]
1023 fn seq_cipher_accepts_mixed_encrypt_next_and_passthrough_next() {
1024 let cipher = Aes256Cipher::new(&Key::from([40u8; 32])).expect("Failed to create cipher");
1025 let ct = (&cipher)
1026 .encrypt_seq(Some(3))
1027 .encrypt_next("first", ())
1028 .unwrap()
1029 .passthrough_next(99u32)
1030 .unwrap()
1031 .encrypt_next("third", ())
1032 .unwrap()
1033 .end()
1034 .unwrap();
1035 match ct {
1036 AesCipherText::Sequence(items) => {
1037 assert_eq!(items.len(), 3);
1038 assert!(matches!(items[0], AesCipherText::Single(_)));
1039 assert!(matches!(items[1], AesCipherText::Passthrough(_)));
1040 assert!(matches!(items[2], AesCipherText::Single(_)));
1041 }
1042 _ => panic!("expected Sequence"),
1043 }
1044 }
1045
1046 #[test]
1047 fn map_with_mixed_entries_cannot_decode_as_uniform_hashmap() {
1048 let cipher = Aes256Cipher::new(&Key::from([41u8; 32])).expect("Failed to create cipher");
1052 let ct = (&cipher)
1053 .encrypt_map()
1054 .encrypt_key("name")
1055 .unwrap()
1056 .encrypt_value("Alice".to_string(), ())
1057 .unwrap()
1058 .passthrough_entry("schema_version", 1u32)
1059 .unwrap()
1060 .end()
1061 .unwrap();
1062 assert!(cipher.decrypt::<HashMap<String, String>>(ct).is_err());
1063 }
1064
1065 #[test]
1066 fn roundtrip_hashmap_of_option_values() {
1067 let cipher = Aes256Cipher::new(&Key::from([42u8; 32])).expect("Failed to create cipher");
1070 let mut m: HashMap<&'static str, Option<String>> = HashMap::new();
1071 m.insert("present", Some("Alice".to_string()));
1072 m.insert("absent", None);
1073 let ct = m.encrypt(&cipher).expect("Encryption failed");
1074 let decoded: HashMap<String, Option<String>> =
1075 cipher.decrypt(ct).expect("Decryption failed");
1076 assert_eq!(decoded.get("present"), Some(&Some("Alice".to_string())));
1077 assert_eq!(decoded.get("absent"), Some(&None));
1078 }
1079
1080 #[test]
1081 fn passthrough_option_is_distinct_type_from_inner() {
1082 let cipher = Aes256Cipher::new(&Key::from([43u8; 32])).expect("Failed to create cipher");
1086 let ct = cipher.passthrough(Some(42u32)).expect("passthrough failed");
1087 let decoded: Option<u32> =
1088 decrypt_passthrough_via(&cipher, ct).expect("decode as Option<u32> should succeed");
1089 assert_eq!(decoded, Some(42u32));
1090
1091 let ct2 = cipher.passthrough(Some(42u32)).expect("passthrough failed");
1092 assert!(
1093 decrypt_passthrough_via::<u32>(&cipher, ct2).is_err(),
1094 "Option<u32> passthrough must not downcast to u32"
1095 );
1096 }
1097
1098 #[quickcheck]
1099 fn decrypt_byte_array_ciphertext_as_vec_u8(key: Key, bytes: [u8; 16]) -> bool {
1100 let cipher = Aes256Cipher::new(&key).expect("Failed to create cipher");
1106 let ct = bytes.encrypt(&cipher).expect("Encryption failed");
1107 let decoded: Vec<u8> = cipher.decrypt(ct).expect("Decryption failed");
1108 decoded == bytes.to_vec()
1109 }
1110}