1#[cfg(feature = "alloc")]
75use alloc::vec::Vec;
76use core::marker::PhantomData;
77
78#[cfg(feature = "serde")]
79use serde::{Deserialize, Serialize};
80use zeroize::Zeroize;
81
82use crate::constants::{
83 CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
84 CRYPTO_AEAD_CHACHA20POLY1305_IETF_MESSAGEBYTES_MAX,
85 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
86 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES,
87 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_MESSAGEBYTES_MAX,
88 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES,
89};
90use crate::error::{Error, ErrorContext};
91use crate::types::*;
92use crate::utils::{ct_eq_bytes, split_suffix};
93
94mod sealed {
95 pub trait Sealed {
99 const NPUBBYTES: usize;
100 const ABYTES: usize;
101 }
102}
103
104pub trait AeadAlgorithm:
110 sealed::Sealed + Clone + Copy + core::fmt::Debug + Default + Eq + PartialEq
111{
112}
113
114#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
116pub struct XChaCha20Poly1305Ietf;
117
118impl sealed::Sealed for XChaCha20Poly1305Ietf {
119 const ABYTES: usize = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
120 const NPUBBYTES: usize = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES;
121}
122impl AeadAlgorithm for XChaCha20Poly1305Ietf {}
123
124#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
126pub struct ChaCha20Poly1305Ietf;
127
128impl sealed::Sealed for ChaCha20Poly1305Ietf {
129 const ABYTES: usize = CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
130 const NPUBBYTES: usize = CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES;
131}
132impl AeadAlgorithm for ChaCha20Poly1305Ietf {}
133
134pub use xchacha20poly1305_ietf::*;
135
136pub mod xchacha20poly1305_ietf {
142 #[cfg(feature = "alloc")]
143 use alloc::vec::Vec;
144
145 pub use super::{AeadAlgorithm, AeadBox, AeadEnvelope, XChaCha20Poly1305Ietf};
146 use crate::constants::{
147 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES,
148 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES,
149 };
150 use crate::types::*;
151
152 pub type Key = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>;
154 pub type Nonce = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>;
156 pub type Mac = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>;
158
159 pub type DryocAead<Mac, Data> = AeadBox<XChaCha20Poly1305Ietf, Mac, Data>;
161 pub type DryocAeadEnvelope<Nonce, Mac, Data> =
163 AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>;
164 #[cfg(feature = "alloc")]
166 pub type VecBox = DryocAead<Mac, Vec<u8>>;
167 #[cfg(feature = "alloc")]
169 pub type VecEnvelope = DryocAeadEnvelope<Nonce, Mac, Vec<u8>>;
170
171 #[cfg(any(
172 all(feature = "protected", any(unix, windows)),
173 all(doc, not(doctest), feature = "std")
174 ))]
175 #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
176 pub mod protected {
177 use super::*;
182 pub use crate::protected::*;
183
184 pub type Key = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>;
186 pub type Nonce = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>;
189 pub type Mac = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>;
192
193 pub type LockedBox = AeadBox<XChaCha20Poly1305Ietf, Locked<Mac>, LockedBytes>;
195 pub type LockedEnvelope =
198 AeadEnvelope<XChaCha20Poly1305Ietf, Locked<Nonce>, Locked<Mac>, LockedBytes>;
199 }
200}
201
202pub mod chacha20poly1305_ietf {
237 #[cfg(feature = "alloc")]
238 use alloc::vec::Vec;
239
240 pub use super::{AeadAlgorithm, AeadBox, AeadEnvelope, ChaCha20Poly1305Ietf};
241 use crate::constants::{
242 CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
243 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES,
244 };
245 use crate::types::*;
246
247 pub type Key = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>;
249 pub type Nonce = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>;
251 pub type Mac = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>;
253 pub type DryocAead<Mac, Data> = AeadBox<ChaCha20Poly1305Ietf, Mac, Data>;
255 pub type DryocAeadEnvelope<Nonce, Mac, Data> =
257 AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>;
258 #[cfg(feature = "alloc")]
260 pub type VecBox = DryocAead<Mac, Vec<u8>>;
261 #[cfg(feature = "alloc")]
263 pub type VecEnvelope = DryocAeadEnvelope<Nonce, Mac, Vec<u8>>;
264
265 #[cfg(any(
266 all(feature = "protected", any(unix, windows)),
267 all(doc, not(doctest), feature = "std")
268 ))]
269 #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
270 pub mod protected {
271 use super::*;
273 pub use crate::protected::*;
274
275 pub type Key = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>;
277 pub type Nonce = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>;
279 pub type Mac = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>;
281 pub type LockedBox = AeadBox<ChaCha20Poly1305Ietf, Locked<Mac>, LockedBytes>;
283 pub type LockedEnvelope =
285 AeadEnvelope<ChaCha20Poly1305Ietf, Locked<Nonce>, Locked<Mac>, LockedBytes>;
286 }
287}
288
289#[derive(Clone, Debug)]
290#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
291pub struct AeadBox<Algorithm: AeadAlgorithm, Mac, Data> {
295 #[cfg_attr(feature = "serde", serde(skip))]
296 algorithm: PhantomData<Algorithm>,
297 tag: Mac,
298 data: Data,
299}
300
301#[derive(Clone, Debug)]
302#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
303pub struct AeadEnvelope<Algorithm: AeadAlgorithm, Nonce, Mac, Data> {
308 #[cfg_attr(feature = "serde", serde(skip))]
309 algorithm: PhantomData<Algorithm>,
310 nonce: Nonce,
311 tag: Mac,
312 data: Data,
313}
314
315#[cfg(feature = "wincode_0_6")]
319macro_rules! impl_wincode_aead {
320 ($box:ty, $envelope:ty, $abytes:expr, $npubbytes:expr) => {
321 impl_wincode_schema!($box {
322 data: Vec<u8> = (src => &src.data, data => data),
323 tag: [u8; $abytes] = (src => src.tag.as_array(), tag => tag.into()),
324 } extra { algorithm: PhantomData });
325
326 impl_wincode_schema!($envelope {
327 nonce: [u8; $npubbytes] = (src => src.nonce.as_array(), nonce => nonce.into()),
328 data: Vec<u8> = (src => &src.data, data => data),
329 tag: [u8; $abytes] = (src => src.tag.as_array(), tag => tag.into()),
330 } extra { algorithm: PhantomData });
331 };
332}
333
334#[cfg(feature = "wincode_0_6")]
335impl_wincode_aead!(
336 VecBox,
337 VecEnvelope,
338 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
339 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
340);
341#[cfg(feature = "wincode_0_6")]
342impl_wincode_aead!(
343 chacha20poly1305_ietf::VecBox,
344 chacha20poly1305_ietf::VecEnvelope,
345 CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES,
346 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES
347);
348
349macro_rules! impl_aead_algorithm {
354 (
355 algorithm:
356 $algorithm:ident,module:
357 $module:ident,encrypt_detached:
358 $encrypt_detached:ident,decrypt_detached:
359 $decrypt_detached:ident,keybytes:
360 $keybytes:expr,npubbytes:
361 $npubbytes:expr,abytes:
362 $abytes:expr,messagebytes_max:
363 $messagebytes_max:expr
364 ) => {
365 impl<Mac: NewByteArray<$abytes> + Zeroize, Data: NewBytes + ResizableBytes + Zeroize>
366 AeadBox<$algorithm, Mac, Data>
367 {
368 pub fn encrypt<
375 Message: Bytes + ?Sized,
376 Nonce: ByteArray<$npubbytes>,
377 SecretKey: ByteArray<$keybytes>,
378 >(
379 message: &Message,
380 associated_data: Option<&[u8]>,
381 nonce: &Nonce,
382 key: &SecretKey,
383 ) -> Result<Self, Error> {
384 use crate::classic::$module::$encrypt_detached;
385
386 validate_length!(max $messagebytes_max, message.len(), ErrorContext::Message);
388 let mut new = Self {
389 algorithm: PhantomData,
390 tag: Mac::new_byte_array(),
391 data: Data::new_bytes(),
392 };
393 new.data.resize(message.len(), 0);
394
395 $encrypt_detached(
396 new.data.as_mut_slice(),
397 new.tag.as_mut_array(),
398 message.as_slice(),
399 associated_data,
400 nonce.as_array(),
401 key.as_array(),
402 )?;
403
404 Ok(new)
405 }
406 }
407
408 impl<
409 'a,
410 Mac: ByteArray<$abytes> + core::convert::TryFrom<&'a [u8]> + Zeroize,
411 Data: Bytes + From<&'a [u8]> + Zeroize,
412 > AeadBox<$algorithm, Mac, Data>
413 {
414 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
421 let (data, tag) = split_suffix(bytes, $abytes, ErrorContext::AeadCiphertext)?;
422 Ok(Self {
423 algorithm: PhantomData,
424 tag: Mac::try_from(tag)
425 .map_err(|_| Error::invalid_encoding(ErrorContext::AuthenticationTag))?,
426 data: Data::from(data),
427 })
428 }
429 }
430
431 impl<Mac: ByteArray<$abytes>, Data: Bytes> AeadBox<$algorithm, Mac, Data> {
432 pub fn decrypt<
441 Output: ResizableBytes + NewBytes,
442 Nonce: ByteArray<$npubbytes>,
443 SecretKey: ByteArray<$keybytes>,
444 >(
445 &self,
446 associated_data: Option<&[u8]>,
447 nonce: &Nonce,
448 key: &SecretKey,
449 ) -> Result<Output, Error> {
450 use crate::classic::$module::$decrypt_detached;
451
452 validate_length!(max $messagebytes_max, self.data.as_slice().len(), ErrorContext::Message);
454 let mut message = Output::new_bytes();
455 message.resize(self.data.as_slice().len(), 0);
456
457 $decrypt_detached(
458 message.as_mut_slice(),
459 self.data.as_slice(),
460 self.tag.as_array(),
461 associated_data,
462 nonce.as_array(),
463 key.as_array(),
464 )?;
465
466 Ok(message)
467 }
468 }
469
470 impl<
471 'a,
472 Nonce: ByteArray<$npubbytes> + core::convert::TryFrom<&'a [u8]> + Zeroize,
473 Mac: ByteArray<$abytes> + core::convert::TryFrom<&'a [u8]> + Zeroize,
474 Data: Bytes + From<&'a [u8]> + Zeroize,
475 > AeadEnvelope<$algorithm, Nonce, Mac, Data>
476 {
477 pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
485 validate_length!(min $npubbytes + $abytes, bytes.len(), ErrorContext::AeadEnvelope);
486 let (nonce, rest) = bytes.split_at($npubbytes);
487 let (data, tag) = rest.split_at(rest.len() - $abytes);
488 Ok(Self {
489 algorithm: PhantomData,
490 nonce: Nonce::try_from(nonce)
491 .map_err(|_| Error::invalid_encoding(ErrorContext::Nonce))?,
492 tag: Mac::try_from(tag)
493 .map_err(|_| Error::invalid_encoding(ErrorContext::AuthenticationTag))?,
494 data: Data::from(data),
495 })
496 }
497 }
498
499 impl<Nonce: ByteArray<$npubbytes>, Mac: ByteArray<$abytes>, Data: Bytes>
500 AeadEnvelope<$algorithm, Nonce, Mac, Data>
501 {
502 pub fn open<Output: ResizableBytes + NewBytes, SecretKey: ByteArray<$keybytes>>(
512 &self,
513 associated_data: Option<&[u8]>,
514 key: &SecretKey,
515 ) -> Result<Output, Error> {
516 use crate::classic::$module::$decrypt_detached;
517
518 validate_length!(max $messagebytes_max, self.data.as_slice().len(), ErrorContext::Message);
520 let mut message = Output::new_bytes();
521 message.resize(self.data.as_slice().len(), 0);
522
523 $decrypt_detached(
524 message.as_mut_slice(),
525 self.data.as_slice(),
526 self.tag.as_array(),
527 associated_data,
528 self.nonce.as_array(),
529 key.as_array(),
530 )?;
531
532 Ok(message)
533 }
534 }
535
536 #[cfg(feature = "alloc")]
537 impl AeadBox<$algorithm, StackByteArray<$abytes>, Vec<u8>> {
538 pub fn encrypt_to_vecbox<
545 Message: Bytes + ?Sized,
546 Nonce: ByteArray<$npubbytes>,
547 SecretKey: ByteArray<$keybytes>,
548 >(
549 message: &Message,
550 associated_data: Option<&[u8]>,
551 nonce: &Nonce,
552 key: &SecretKey,
553 ) -> Result<Self, Error> {
554 Self::encrypt(message, associated_data, nonce, key)
555 }
556
557 pub fn decrypt_to_vec<Nonce: ByteArray<$npubbytes>, SecretKey: ByteArray<$keybytes>>(
565 &self,
566 associated_data: Option<&[u8]>,
567 nonce: &Nonce,
568 key: &SecretKey,
569 ) -> Result<Vec<u8>, Error> {
570 self.decrypt(associated_data, nonce, key)
571 }
572
573 #[must_use]
575 pub fn into_vec(mut self) -> Vec<u8> {
576 self.data.resize(self.data.len() + $abytes, 0);
577 let tag_offset = self.data.len() - $abytes;
578 self.data[tag_offset..].copy_from_slice(self.tag.as_slice());
579 self.data
580 }
581 }
582
583 #[cfg(feature = "alloc")]
584 impl
585 AeadEnvelope<$algorithm, StackByteArray<$npubbytes>, StackByteArray<$abytes>, Vec<u8>>
586 {
587 pub fn open_to_vec<SecretKey: ByteArray<$keybytes>>(
595 &self,
596 associated_data: Option<&[u8]>,
597 key: &SecretKey,
598 ) -> Result<Vec<u8>, Error> {
599 self.open(associated_data, key)
600 }
601
602 #[must_use]
604 pub fn into_vec(self) -> Vec<u8> {
605 let mut output = self.nonce.to_vec();
606 output.extend_from_slice(self.data.as_slice());
607 output.extend_from_slice(self.tag.as_slice());
608 output
609 }
610 }
611 };
612}
613
614macro_rules! impl_aead_envelope_seal {
617 (
618 algorithm:
619 $algorithm:ident,keybytes:
620 $keybytes:expr,npubbytes:
621 $npubbytes:expr,abytes:
622 $abytes:expr
623 ) => {
624 impl<
625 Nonce: NewByteArray<$npubbytes> + Zeroize,
626 Mac: NewByteArray<$abytes> + Zeroize,
627 Data: NewBytes + ResizableBytes + Zeroize,
628 > AeadEnvelope<$algorithm, Nonce, Mac, Data>
629 {
630 pub fn seal<Message: Bytes + ?Sized, SecretKey: ByteArray<$keybytes>>(
642 message: &Message,
643 associated_data: Option<&[u8]>,
644 key: &SecretKey,
645 ) -> Result<Self, Error> {
646 let nonce = Nonce::generate();
647 let aead_box = AeadBox::<$algorithm, Mac, Data>::encrypt(
648 message,
649 associated_data,
650 &nonce,
651 key,
652 )?;
653 let (tag, data) = aead_box.into_parts();
654
655 Ok(Self {
656 algorithm: PhantomData,
657 nonce,
658 tag,
659 data,
660 })
661 }
662 }
663
664 #[cfg(feature = "alloc")]
665 impl
666 AeadEnvelope<$algorithm, StackByteArray<$npubbytes>, StackByteArray<$abytes>, Vec<u8>>
667 {
668 pub fn seal_to_vecbox<Message: Bytes + ?Sized, SecretKey: ByteArray<$keybytes>>(
679 message: &Message,
680 associated_data: Option<&[u8]>,
681 key: &SecretKey,
682 ) -> Result<Self, Error> {
683 Self::seal(message, associated_data, key)
684 }
685 }
686 };
687}
688
689impl_aead_algorithm! {
690 algorithm: XChaCha20Poly1305Ietf,
691 module: crypto_aead_xchacha20poly1305_ietf,
692 encrypt_detached: crypto_aead_xchacha20poly1305_ietf_encrypt_detached,
693 decrypt_detached: crypto_aead_xchacha20poly1305_ietf_decrypt_detached,
694 keybytes: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES,
695 npubbytes: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES,
696 abytes: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
697 messagebytes_max: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_MESSAGEBYTES_MAX
698}
699
700impl_aead_envelope_seal! {
701 algorithm: XChaCha20Poly1305Ietf,
702 keybytes: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES,
703 npubbytes: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES,
704 abytes: CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
705}
706
707impl_aead_algorithm! {
708 algorithm: ChaCha20Poly1305Ietf,
709 module: crypto_aead_chacha20poly1305_ietf,
710 encrypt_detached: crypto_aead_chacha20poly1305_ietf_encrypt_detached,
711 decrypt_detached: crypto_aead_chacha20poly1305_ietf_decrypt_detached,
712 keybytes: CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
713 npubbytes: CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES,
714 abytes: CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES,
715 messagebytes_max: CRYPTO_AEAD_CHACHA20POLY1305_IETF_MESSAGEBYTES_MAX
716}
717
718impl<Algorithm: AeadAlgorithm, Mac: Zeroize, Data: Zeroize> Zeroize
719 for AeadBox<Algorithm, Mac, Data>
720{
721 fn zeroize(&mut self) {
722 self.tag.zeroize();
723 self.data.zeroize();
724 }
725}
726
727impl<Algorithm: AeadAlgorithm, Nonce: Zeroize, Mac: Zeroize, Data: Zeroize> Zeroize
728 for AeadEnvelope<Algorithm, Nonce, Mac, Data>
729{
730 fn zeroize(&mut self) {
731 self.nonce.zeroize();
732 self.tag.zeroize();
733 self.data.zeroize();
734 }
735}
736
737impl<Algorithm: AeadAlgorithm, Mac, Data> AeadBox<Algorithm, Mac, Data> {
738 #[must_use]
740 pub fn from_parts(tag: Mac, data: Data) -> Self {
741 Self {
742 algorithm: PhantomData,
743 tag,
744 data,
745 }
746 }
747
748 pub fn tag(&self) -> &Mac {
750 &self.tag
751 }
752
753 pub fn data(&self) -> &Data {
755 &self.data
756 }
757
758 #[must_use]
760 pub fn into_parts(self) -> (Mac, Data) {
761 (self.tag, self.data)
762 }
763}
764
765impl<Algorithm: AeadAlgorithm, Mac: Bytes, Data: Bytes> AeadBox<Algorithm, Mac, Data> {
766 #[cfg(feature = "alloc")]
768 #[must_use]
769 pub fn to_vec(&self) -> Vec<u8> {
770 self.to_bytes()
771 }
772
773 #[must_use]
780 pub fn to_bytes<Output: NewBytes + ResizableBytes>(&self) -> Output {
781 concat_bytes(
782 self.data.as_slice(),
783 &self.tag.as_slice()[..Algorithm::ABYTES],
784 )
785 }
786}
787
788impl<Algorithm: AeadAlgorithm, Nonce, Mac, Data> AeadEnvelope<Algorithm, Nonce, Mac, Data> {
789 #[must_use]
791 pub fn from_parts(nonce: Nonce, tag: Mac, data: Data) -> Self {
792 Self {
793 algorithm: PhantomData,
794 nonce,
795 tag,
796 data,
797 }
798 }
799
800 pub fn nonce(&self) -> &Nonce {
802 &self.nonce
803 }
804
805 pub fn tag(&self) -> &Mac {
807 &self.tag
808 }
809
810 pub fn data(&self) -> &Data {
812 &self.data
813 }
814
815 #[must_use]
817 pub fn into_parts(self) -> (Nonce, Mac, Data) {
818 (self.nonce, self.tag, self.data)
819 }
820}
821
822impl<Algorithm: AeadAlgorithm, Nonce: Bytes, Mac: Bytes, Data: Bytes>
823 AeadEnvelope<Algorithm, Nonce, Mac, Data>
824{
825 #[cfg(feature = "alloc")]
827 #[must_use]
828 pub fn to_vec(&self) -> Vec<u8> {
829 self.to_bytes()
830 }
831
832 #[must_use]
840 pub fn to_bytes<Output: NewBytes + ResizableBytes>(&self) -> Output {
841 let nonce = &self.nonce.as_slice()[..Algorithm::NPUBBYTES];
842 let tag = &self.tag.as_slice()[..Algorithm::ABYTES];
843 let mut data = Output::new_bytes();
844 data.resize(nonce.len() + self.data.len() + tag.len(), 0);
845 let s = data.as_mut_slice();
846 s[..nonce.len()].copy_from_slice(nonce);
847 s[nonce.len()..nonce.len() + self.data.len()].copy_from_slice(self.data.as_slice());
848 s[nonce.len() + self.data.len()..].copy_from_slice(tag);
849 data
850 }
851}
852
853impl<Algorithm: AeadAlgorithm, Mac: Bytes, Data: Bytes> PartialEq
854 for AeadBox<Algorithm, Mac, Data>
855{
856 fn eq(&self, other: &Self) -> bool {
857 ct_eq_bytes(self.tag.as_slice(), other.tag.as_slice())
858 && ct_eq_bytes(self.data.as_slice(), other.data.as_slice())
859 }
860}
861
862impl<Algorithm: AeadAlgorithm, Nonce: Bytes, Mac: Bytes, Data: Bytes> PartialEq
863 for AeadEnvelope<Algorithm, Nonce, Mac, Data>
864{
865 fn eq(&self, other: &Self) -> bool {
866 ct_eq_bytes(self.nonce.as_slice(), other.nonce.as_slice())
867 && ct_eq_bytes(self.tag.as_slice(), other.tag.as_slice())
868 && ct_eq_bytes(self.data.as_slice(), other.data.as_slice())
869 }
870}
871
872#[cfg(all(test, feature = "alloc"))]
873mod tests {
874 use super::*;
875
876 #[test]
877 fn test_explicit_box_layout() {
878 let key = Key::generate();
879 let nonce = Nonce::generate();
880 let message = b"hello";
881 let aad = b"metadata";
882
883 let aead = VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt");
884 let bytes = aead.to_vec();
885 assert_eq!(
886 bytes.len(),
887 message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
888 );
889
890 let parsed = VecBox::from_bytes(&bytes).expect("from bytes");
891 let decrypted = parsed
892 .decrypt_to_vec(Some(aad), &nonce, &key)
893 .expect("decrypt");
894 assert_eq!(decrypted, message);
895 }
896
897 #[test]
898 fn test_explicit_box_failures() {
899 let key = Key::generate();
900 let nonce = Nonce::generate();
901 let message = b"hello";
902 let aad = b"metadata";
903
904 let aead = VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt");
905
906 aead.decrypt_to_vec(Some(b"wrong aad"), &nonce, &key)
907 .expect_err("wrong aad should fail");
908
909 let mut wrong_key = key.clone();
910 wrong_key.as_mut_slice()[0] ^= 1;
911 aead.decrypt_to_vec(Some(aad), &nonce, &wrong_key)
912 .expect_err("wrong key should fail");
913
914 let mut wrong_nonce = nonce.clone();
915 wrong_nonce.as_mut_slice()[0] ^= 1;
916 aead.decrypt_to_vec(Some(aad), &wrong_nonce, &key)
917 .expect_err("wrong nonce should fail");
918
919 let mut modified_ciphertext = aead.clone();
920 modified_ciphertext.data.as_mut_slice()[0] ^= 1;
921 modified_ciphertext
922 .decrypt_to_vec(Some(aad), &nonce, &key)
923 .expect_err("modified ciphertext should fail");
924
925 let mut modified_tag = aead.clone();
926 modified_tag.tag.as_mut_slice()[0] ^= 1;
927 modified_tag
928 .decrypt_to_vec(Some(aad), &nonce, &key)
929 .expect_err("modified tag should fail");
930 }
931
932 #[test]
933 fn test_explicit_box_empty_message_and_no_aad() {
934 let key = Key::generate();
935 let nonce = Nonce::generate();
936
937 let aead = VecBox::encrypt_to_vecbox(&[], None, &nonce, &key).expect("encrypt");
938 assert_eq!(
939 aead.to_vec().len(),
940 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
941 );
942
943 let decrypted = aead
944 .decrypt_to_vec(None, &nonce, &key)
945 .expect("decrypt empty");
946 assert!(decrypted.is_empty());
947 }
948
949 #[test]
950 fn test_envelope_layout() {
951 let key = Key::generate();
952 let message = b"hello";
953 let aad = b"metadata";
954
955 let envelope = VecEnvelope::seal_to_vecbox(message, Some(aad), &key).expect("seal");
956 let bytes = envelope.to_vec();
957 assert_eq!(
958 bytes.len(),
959 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
960 + message.len()
961 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
962 );
963 assert_eq!(
964 &bytes[..CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES],
965 envelope.nonce().as_slice()
966 );
967
968 let parsed = VecEnvelope::from_bytes(&bytes).expect("from bytes");
969 let decrypted = parsed.open_to_vec(Some(aad), &key).expect("open");
970 assert_eq!(decrypted, message);
971 }
972
973 #[test]
974 fn test_envelope_failures() {
975 let key = Key::generate();
976 let message = b"hello";
977 let aad = b"metadata";
978
979 let envelope = VecEnvelope::seal_to_vecbox(message, Some(aad), &key).expect("seal");
980
981 envelope
982 .open_to_vec(Some(b"wrong aad"), &key)
983 .expect_err("wrong aad should fail");
984
985 let mut wrong_key = key.clone();
986 wrong_key.as_mut_slice()[0] ^= 1;
987 envelope
988 .open_to_vec(Some(aad), &wrong_key)
989 .expect_err("wrong key should fail");
990
991 let mut modified_nonce = envelope.clone();
992 modified_nonce.nonce.as_mut_slice()[0] ^= 1;
993 modified_nonce
994 .open_to_vec(Some(aad), &key)
995 .expect_err("modified nonce should fail");
996
997 let mut modified_ciphertext = envelope.clone();
998 modified_ciphertext.data.as_mut_slice()[0] ^= 1;
999 modified_ciphertext
1000 .open_to_vec(Some(aad), &key)
1001 .expect_err("modified ciphertext should fail");
1002
1003 let mut modified_tag = envelope.clone();
1004 modified_tag.tag.as_mut_slice()[0] ^= 1;
1005 modified_tag
1006 .open_to_vec(Some(aad), &key)
1007 .expect_err("modified tag should fail");
1008 }
1009
1010 #[test]
1011 fn test_envelope_empty_message_and_no_aad() {
1012 let key = Key::generate();
1013
1014 let envelope = VecEnvelope::seal_to_vecbox(&[], None, &key).expect("seal");
1015 assert_eq!(
1016 envelope.to_vec().len(),
1017 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1018 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1019 );
1020
1021 let decrypted = envelope.open_to_vec(None, &key).expect("open empty");
1022 assert!(decrypted.is_empty());
1023 }
1024
1025 #[test]
1026 fn test_from_bytes_boundaries() {
1027 assert!(VecBox::from_bytes(&[]).is_err());
1028
1029 let empty_box_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1030 let empty_box = VecBox::from_bytes(&empty_box_bytes).expect("empty box parses");
1031 assert!(empty_box.data().is_empty());
1032 assert_eq!(empty_box.tag().as_slice(), empty_box_bytes.as_slice());
1033
1034 let short_envelope_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1035 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1036 - 1];
1037 const ENVELOPE_MIN: usize = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1038 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
1039 for short in [&short_envelope_bytes[..], &short_envelope_bytes[..1]] {
1042 assert!(matches!(
1043 VecEnvelope::from_bytes(short),
1044 Err(Error::InvalidLength {
1045 context: crate::ErrorContext::AeadEnvelope,
1046 actual,
1047 constraint: crate::error::LengthConstraint::AtLeast(ENVELOPE_MIN),
1048 }) if actual == short.len()
1049 ));
1050 }
1051
1052 let empty_envelope_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1053 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1054 let empty_envelope =
1055 VecEnvelope::from_bytes(&empty_envelope_bytes).expect("empty envelope parses");
1056 assert!(empty_envelope.data().is_empty());
1057 assert_eq!(
1058 empty_envelope.nonce().as_slice(),
1059 &empty_envelope_bytes[..CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]
1060 );
1061 assert_eq!(
1062 empty_envelope.tag().as_slice(),
1063 &empty_envelope_bytes[CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES..]
1064 );
1065 }
1066
1067 mod kat {
1074 use super::*;
1075 use crate::classic::crypto_aead_chacha20poly1305_ietf::{
1076 crypto_aead_chacha20poly1305_ietf_decrypt, crypto_aead_chacha20poly1305_ietf_encrypt,
1077 };
1078 use crate::classic::crypto_aead_xchacha20poly1305_ietf::{
1079 crypto_aead_xchacha20poly1305_ietf_decrypt, crypto_aead_xchacha20poly1305_ietf_encrypt,
1080 };
1081 use crate::dryocaead::chacha20poly1305_ietf as chacha;
1082
1083 const MESSAGE: &[u8] = b"Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it.";
1084 const AD: &[u8] = &[
1085 0x50, 0x51, 0x52, 0x53, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
1086 ];
1087 const KEY: [u8; 32] = [
1088 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
1089 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
1090 0x9c, 0x9d, 0x9e, 0x9f,
1091 ];
1092 const CHACHA_NONCE: [u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] = [
1093 0x07, 0x00, 0x00, 0x00, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
1094 ];
1095 const CHACHA_EXPECTED: &str = concat!(
1096 "d31a8d34648e60db7b86afbc53ef7ec2a4aded51296e08fea9e2b5a736ee62d63dbea45e8ca9671282fafb69",
1097 "da92728b1a71de0a9e060b2905d6a5b67ecd3b3692ddbd7f2d778b8c9803aee328091b58fab324e4fad67594",
1098 "5585808b4831d7bc3ff4def08e4b7a9de576d26586cec64b61161ae10b594f09e26a7e902ecbd0600691",
1099 );
1100 const XCHACHA_NONCE: [u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] = [
1101 0xf2, 0x8a, 0x50, 0xa7, 0x8a, 0x7e, 0x23, 0xc9, 0xcb, 0xa6, 0x78, 0x34, 0x66, 0xf8,
1102 0x03, 0x59, 0x0f, 0x04, 0xe9, 0x22, 0x31, 0xa3, 0x2d, 0x5d,
1103 ];
1104 const XCHACHA_EXPECTED: &str = concat!(
1105 "20f1ae75e1e5e00040294f0fb10ebb0810c593c7dba4ec104c1e5ef9507faeef58fc2898bbd0e47b2f5331fb",
1106 "c367d3c2784e3648ce1eaa7787ad186db2685ee89ae4d3441f6ea0b2224cd5a134161b554d8b48350b4ad401",
1107 "15db81ea820968e943892f2b8051cb5f7a8666e7e7ef7f84c0a2f80a12d06680c8eebbd93004109de842",
1108 );
1109
1110 fn chacha_expected() -> Vec<u8> {
1111 hex::decode(CHACHA_EXPECTED).expect("hex")
1112 }
1113
1114 fn xchacha_expected() -> Vec<u8> {
1115 hex::decode(XCHACHA_EXPECTED).expect("hex")
1116 }
1117
1118 #[test]
1119 fn chacha_box_and_envelope_match_rfc_8439_and_classic() {
1120 let key = chacha::Key::from(KEY);
1121 let nonce = chacha::Nonce::from(CHACHA_NONCE);
1122 let expected = chacha_expected();
1123
1124 let aead = chacha::VecBox::encrypt_to_vecbox(MESSAGE, Some(AD), &nonce, &key)
1125 .expect("encrypt");
1126 assert_eq!(aead.to_vec(), expected);
1127 assert_eq!(aead.clone().into_vec(), expected);
1128 assert_eq!(aead.data(), &expected[..MESSAGE.len()]);
1129 assert_eq!(aead.tag().as_slice(), &expected[MESSAGE.len()..]);
1130
1131 let mut classic_decrypted = vec![0u8; MESSAGE.len()];
1133 crypto_aead_chacha20poly1305_ietf_decrypt(
1134 &mut classic_decrypted,
1135 &aead.to_vec(),
1136 Some(AD),
1137 &CHACHA_NONCE,
1138 &KEY,
1139 )
1140 .expect("classic decrypt");
1141 assert_eq!(classic_decrypted, MESSAGE);
1142
1143 let mut classic_ciphertext =
1144 vec![0u8; MESSAGE.len() + CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES];
1145 crypto_aead_chacha20poly1305_ietf_encrypt(
1146 &mut classic_ciphertext,
1147 MESSAGE,
1148 Some(AD),
1149 &CHACHA_NONCE,
1150 &KEY,
1151 )
1152 .expect("classic encrypt");
1153 let parsed = chacha::VecBox::from_bytes(&classic_ciphertext).expect("parse");
1154 assert_eq!(parsed, aead);
1155 assert_eq!(
1156 parsed
1157 .decrypt_to_vec(Some(AD), &nonce, &key)
1158 .expect("decrypt"),
1159 MESSAGE
1160 );
1161
1162 let (tag, data) = parsed.into_parts();
1163 let envelope = chacha::VecEnvelope::from_parts(nonce.clone(), tag, data);
1164 let mut envelope_bytes = CHACHA_NONCE.to_vec();
1165 envelope_bytes.extend_from_slice(&expected);
1166 assert_eq!(envelope.to_vec(), envelope_bytes);
1167 assert_eq!(envelope.clone().into_vec(), envelope_bytes);
1168 let envelope = chacha::VecEnvelope::from_bytes(&envelope_bytes).expect("parse");
1169 assert_eq!(envelope.nonce(), &nonce);
1170 assert_eq!(envelope.open_to_vec(Some(AD), &key).expect("open"), MESSAGE);
1171 }
1172
1173 #[test]
1174 fn xchacha_box_and_envelope_match_libsodium_vector_and_classic() {
1175 let key = Key::from(KEY);
1176 let nonce = Nonce::from(XCHACHA_NONCE);
1177 let expected = xchacha_expected();
1178
1179 let aead = VecBox::encrypt_to_vecbox(MESSAGE, Some(AD), &nonce, &key).expect("encrypt");
1180 assert_eq!(aead.to_vec(), expected);
1181 assert_eq!(aead.clone().into_vec(), expected);
1182
1183 let mut classic_decrypted = vec![0u8; MESSAGE.len()];
1184 crypto_aead_xchacha20poly1305_ietf_decrypt(
1185 &mut classic_decrypted,
1186 &aead.to_vec(),
1187 Some(AD),
1188 &XCHACHA_NONCE,
1189 &KEY,
1190 )
1191 .expect("classic decrypt");
1192 assert_eq!(classic_decrypted, MESSAGE);
1193
1194 let mut classic_ciphertext =
1195 vec![0u8; MESSAGE.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1196 crypto_aead_xchacha20poly1305_ietf_encrypt(
1197 &mut classic_ciphertext,
1198 MESSAGE,
1199 Some(AD),
1200 &XCHACHA_NONCE,
1201 &KEY,
1202 )
1203 .expect("classic encrypt");
1204 let parsed = VecBox::from_bytes(&classic_ciphertext).expect("parse");
1205 assert_eq!(parsed, aead);
1206 assert_eq!(
1207 parsed
1208 .decrypt_to_vec(Some(AD), &nonce, &key)
1209 .expect("decrypt"),
1210 MESSAGE
1211 );
1212
1213 let mut envelope_bytes = XCHACHA_NONCE.to_vec();
1214 envelope_bytes.extend_from_slice(&expected);
1215 let envelope = VecEnvelope::from_bytes(&envelope_bytes).expect("parse");
1216 assert_eq!(envelope.to_vec(), envelope_bytes);
1217 assert_eq!(envelope.open_to_vec(Some(AD), &key).expect("open"), MESSAGE);
1218 let (parsed_nonce, tag, data) = envelope.into_parts();
1219 assert_eq!(parsed_nonce, nonce);
1220 assert_eq!(
1221 VecEnvelope::from_parts(parsed_nonce, tag, data).into_vec(),
1222 envelope_bytes
1223 );
1224 }
1225
1226 #[test]
1227 fn chacha_tampering_and_wrong_inputs_are_rejected() {
1228 let key = chacha::Key::from(KEY);
1229 let nonce = chacha::Nonce::from(CHACHA_NONCE);
1230 let expected = chacha_expected();
1231 let aead = chacha::VecBox::from_bytes(&expected).expect("parse");
1232
1233 assert!(matches!(
1234 aead.decrypt_to_vec(None, &nonce, &key),
1235 Err(Error::AuthenticationFailed)
1236 ));
1237 assert!(matches!(
1238 aead.decrypt_to_vec(Some(&AD[..AD.len() - 1]), &nonce, &key),
1239 Err(Error::AuthenticationFailed)
1240 ));
1241
1242 let mut wrong_key = key.clone();
1243 wrong_key[31] ^= 1;
1244 assert!(matches!(
1245 aead.decrypt_to_vec(Some(AD), &nonce, &wrong_key),
1246 Err(Error::AuthenticationFailed)
1247 ));
1248
1249 let mut wrong_nonce = nonce.clone();
1250 wrong_nonce[0] ^= 1;
1251 assert!(matches!(
1252 aead.decrypt_to_vec(Some(AD), &wrong_nonce, &key),
1253 Err(Error::AuthenticationFailed)
1254 ));
1255
1256 let tag_start = expected.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
1257 for index in [0, tag_start - 1, tag_start, expected.len() - 1] {
1258 let mut tampered = expected.clone();
1259 tampered[index] ^= 0x80;
1260 let tampered = chacha::VecBox::from_bytes(&tampered).expect("parse");
1261 assert!(matches!(
1262 tampered.decrypt_to_vec(Some(AD), &nonce, &key),
1263 Err(Error::AuthenticationFailed)
1264 ));
1265
1266 let mut envelope_bytes = CHACHA_NONCE.to_vec();
1267 envelope_bytes.extend_from_slice(tampered.to_vec().as_slice());
1268 let envelope = chacha::VecEnvelope::from_bytes(&envelope_bytes).expect("parse");
1269 assert!(matches!(
1270 envelope.open_to_vec(Some(AD), &key),
1271 Err(Error::AuthenticationFailed)
1272 ));
1273 }
1274
1275 let mut xnonce = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES];
1278 xnonce[..CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES].copy_from_slice(&CHACHA_NONCE);
1279 let as_xchacha = VecBox::from_bytes(&expected).expect("parse");
1280 assert!(
1281 as_xchacha
1282 .decrypt_to_vec(Some(AD), &Nonce::from(xnonce), &Key::from(KEY))
1283 .is_err()
1284 );
1285
1286 assert_eq!(
1287 aead.decrypt_to_vec(Some(AD), &nonce, &key)
1288 .expect("decrypt"),
1289 MESSAGE
1290 );
1291 }
1292
1293 #[test]
1294 fn chacha_from_bytes_boundaries() {
1295 const BOX_MIN: usize = CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
1296 const ENVELOPE_MIN: usize = CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES
1297 + CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
1298
1299 for len in [0, 1, BOX_MIN - 1] {
1300 assert!(matches!(
1301 chacha::VecBox::from_bytes(&vec![0u8; len]),
1302 Err(Error::InvalidLength {
1303 context: ErrorContext::AeadCiphertext,
1304 actual,
1305 constraint: crate::error::LengthConstraint::AtLeast(BOX_MIN),
1306 }) if actual == len
1307 ));
1308 }
1309 let empty_box = chacha::VecBox::from_bytes(&[0x5au8; BOX_MIN]).expect("empty box");
1310 assert!(empty_box.data().is_empty());
1311 assert_eq!(empty_box.tag().as_slice(), &[0x5au8; BOX_MIN]);
1312
1313 for len in [
1314 0,
1315 1,
1316 CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES,
1317 ENVELOPE_MIN - 1,
1318 ] {
1319 assert!(matches!(
1320 chacha::VecEnvelope::from_bytes(&vec![0u8; len]),
1321 Err(Error::InvalidLength {
1322 context: ErrorContext::AeadEnvelope,
1323 actual,
1324 constraint: crate::error::LengthConstraint::AtLeast(ENVELOPE_MIN),
1325 }) if actual == len
1326 ));
1327 }
1328 let mut envelope_bytes = CHACHA_NONCE.to_vec();
1329 envelope_bytes.extend_from_slice(&[0x5au8; BOX_MIN]);
1330 let empty_envelope =
1331 chacha::VecEnvelope::from_bytes(&envelope_bytes).expect("empty envelope");
1332 assert!(empty_envelope.data().is_empty());
1333 assert_eq!(empty_envelope.nonce().as_slice(), &CHACHA_NONCE);
1334 assert_eq!(empty_envelope.tag().as_slice(), &[0x5au8; BOX_MIN]);
1335
1336 let xchacha_min = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1340 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1341 assert_eq!(
1342 chacha::VecEnvelope::from_bytes(&xchacha_min)
1343 .expect("parses")
1344 .data()
1345 .len(),
1346 CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1347 - CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES
1348 );
1349 assert!(VecEnvelope::from_bytes(&envelope_bytes).is_err());
1350 }
1351
1352 #[cfg(feature = "serde")]
1353 #[test]
1354 fn chacha_and_xchacha_json_round_trips_decrypt_with_classic() {
1355 let expected = chacha_expected();
1356 let aead = chacha::VecBox::from_bytes(&expected).expect("parse");
1357 let json = serde_json::to_string(&aead).expect("serialize box");
1358 let decoded: chacha::VecBox = serde_json::from_str(&json).expect("deserialize box");
1359 assert_eq!(decoded, aead);
1360 let mut decrypted = vec![0u8; MESSAGE.len()];
1361 crypto_aead_chacha20poly1305_ietf_decrypt(
1362 &mut decrypted,
1363 &decoded.to_vec(),
1364 Some(AD),
1365 &CHACHA_NONCE,
1366 &KEY,
1367 )
1368 .expect("classic decrypt");
1369 assert_eq!(decrypted, MESSAGE);
1370
1371 let mut envelope_bytes = CHACHA_NONCE.to_vec();
1372 envelope_bytes.extend_from_slice(&expected);
1373 let envelope = chacha::VecEnvelope::from_bytes(&envelope_bytes).expect("parse");
1374 let json = serde_json::to_string(&envelope).expect("serialize envelope");
1375 let decoded: chacha::VecEnvelope =
1376 serde_json::from_str(&json).expect("deserialize envelope");
1377 assert_eq!(decoded, envelope);
1378 assert_eq!(decoded.to_vec(), envelope_bytes);
1379 assert_eq!(
1380 decoded
1381 .open_to_vec(Some(AD), &chacha::Key::from(KEY))
1382 .expect("open"),
1383 MESSAGE
1384 );
1385
1386 let xexpected = xchacha_expected();
1387 let xaead = VecBox::from_bytes(&xexpected).expect("parse");
1388 let decoded: VecBox =
1389 serde_json::from_str(&serde_json::to_string(&xaead).expect("ser")).expect("de");
1390 assert_eq!(decoded.to_vec(), xexpected);
1391 let mut xenvelope_bytes = XCHACHA_NONCE.to_vec();
1392 xenvelope_bytes.extend_from_slice(&xexpected);
1393 let xenvelope = VecEnvelope::from_bytes(&xenvelope_bytes).expect("parse");
1394 let decoded: VecEnvelope =
1395 serde_json::from_str(&serde_json::to_string(&xenvelope).expect("ser")).expect("de");
1396 assert_eq!(decoded.to_vec(), xenvelope_bytes);
1397 let mut decrypted = vec![0u8; MESSAGE.len()];
1398 crypto_aead_xchacha20poly1305_ietf_decrypt(
1399 &mut decrypted,
1400 &decoded.to_vec()[CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES..],
1401 Some(AD),
1402 &XCHACHA_NONCE,
1403 &KEY,
1404 )
1405 .expect("classic decrypt");
1406 assert_eq!(decrypted, MESSAGE);
1407 }
1408
1409 #[cfg(feature = "wincode_0_6")]
1410 #[test]
1411 fn chacha_wincode_round_trips_decrypt_with_classic() {
1412 let expected = chacha_expected();
1413 let aead = chacha::VecBox::from_bytes(&expected).expect("parse");
1414 let encoded = wincode::serialize(&aead).expect("serialize box");
1415 let decoded: chacha::VecBox = wincode::deserialize(&encoded).expect("deserialize box");
1416 assert_eq!(decoded, aead);
1417 let mut decrypted = vec![0u8; MESSAGE.len()];
1418 crypto_aead_chacha20poly1305_ietf_decrypt(
1419 &mut decrypted,
1420 &decoded.to_vec(),
1421 Some(AD),
1422 &CHACHA_NONCE,
1423 &KEY,
1424 )
1425 .expect("classic decrypt");
1426 assert_eq!(decrypted, MESSAGE);
1427
1428 let mut envelope_bytes = CHACHA_NONCE.to_vec();
1429 envelope_bytes.extend_from_slice(&expected);
1430 let envelope = chacha::VecEnvelope::from_bytes(&envelope_bytes).expect("parse");
1431 let encoded = wincode::serialize(&envelope).expect("serialize envelope");
1432 let decoded: chacha::VecEnvelope =
1433 wincode::deserialize(&encoded).expect("deserialize envelope");
1434 assert_eq!(decoded, envelope);
1435 assert_eq!(decoded.to_vec(), envelope_bytes);
1436 assert_eq!(
1437 decoded
1438 .open_to_vec(Some(AD), &chacha::Key::from(KEY))
1439 .expect("open"),
1440 MESSAGE
1441 );
1442
1443 for encoded in [wincode::serialize(&aead).expect("ser"), encoded] {
1445 assert!(
1446 wincode::deserialize::<chacha::VecBox>(&encoded[..encoded.len() - 1]).is_err()
1447 );
1448 assert!(
1449 wincode::deserialize::<chacha::VecEnvelope>(&encoded[..encoded.len() - 1])
1450 .is_err()
1451 );
1452 }
1453 }
1454 }
1455
1456 #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1457 mod property_tests {
1458 use proptest::prelude::*;
1459
1460 use super::*;
1461 use crate::classic::crypto_aead_xchacha20poly1305_ietf::{
1462 Mac as ClassicMac, crypto_aead_xchacha20poly1305_ietf_decrypt,
1463 crypto_aead_xchacha20poly1305_ietf_decrypt_detached,
1464 crypto_aead_xchacha20poly1305_ietf_decrypt_inplace,
1465 crypto_aead_xchacha20poly1305_ietf_encrypt,
1466 crypto_aead_xchacha20poly1305_ietf_encrypt_detached,
1467 crypto_aead_xchacha20poly1305_ietf_encrypt_inplace,
1468 };
1469
1470 fn length_strategy(max: usize) -> impl Strategy<Value = usize> {
1471 prop_oneof![
1472 Just(0usize),
1473 Just(1),
1474 Just(15),
1475 Just(16),
1476 Just(17),
1477 Just(63),
1478 Just(64),
1479 Just(65),
1480 Just(max.saturating_sub(1)),
1481 Just(max),
1482 0usize..=max,
1483 ]
1484 }
1485
1486 fn bytes_strategy(max: usize) -> impl Strategy<Value = Vec<u8>> {
1487 length_strategy(max).prop_flat_map(|len| prop::collection::vec(any::<u8>(), len))
1488 }
1489
1490 fn aad_strategy() -> impl Strategy<Value = Option<Vec<u8>>> {
1491 prop::option::of(bytes_strategy(256))
1492 }
1493
1494 proptest! {
1495 #![proptest_config(crate::utils::test_util::proptest_config(96))]
1496
1497 #[test]
1498 fn proptest_classic_modes_and_rustaceous_layouts_agree(
1499 key in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES]>(),
1500 nonce in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]>(),
1501 aad in aad_strategy(),
1502 message in bytes_strategy(512),
1503 ) {
1504 let aad = aad.as_deref();
1505
1506 let mut combined =
1507 vec![0u8; message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1508 crypto_aead_xchacha20poly1305_ietf_encrypt(
1509 &mut combined,
1510 &message,
1511 aad,
1512 &nonce,
1513 &key,
1514 )
1515 .expect("classic combined encrypt");
1516
1517 let mut decrypted = vec![0u8; message.len()];
1518 crypto_aead_xchacha20poly1305_ietf_decrypt(
1519 &mut decrypted,
1520 &combined,
1521 aad,
1522 &nonce,
1523 &key,
1524 )
1525 .expect("classic combined decrypt");
1526 prop_assert_eq!(decrypted.as_slice(), message.as_slice());
1527
1528 let mut detached = vec![0u8; message.len()];
1529 let mut mac = ClassicMac::default();
1530 crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
1531 &mut detached,
1532 &mut mac,
1533 &message,
1534 aad,
1535 &nonce,
1536 &key,
1537 )
1538 .expect("classic detached encrypt");
1539 prop_assert_eq!(&detached, &combined[..message.len()]);
1540 prop_assert_eq!(mac.as_slice(), &combined[message.len()..]);
1541
1542 let mut detached_decrypted = vec![0u8; message.len()];
1543 crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1544 &mut detached_decrypted,
1545 &detached,
1546 &mac,
1547 aad,
1548 &nonce,
1549 &key,
1550 )
1551 .expect("classic detached decrypt");
1552 prop_assert_eq!(detached_decrypted.as_slice(), message.as_slice());
1553
1554 let mut inplace = message.clone();
1555 inplace.resize(message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, 0);
1556 crypto_aead_xchacha20poly1305_ietf_encrypt_inplace(
1557 &mut inplace,
1558 aad,
1559 &nonce,
1560 &key,
1561 )
1562 .expect("classic inplace encrypt");
1563 prop_assert_eq!(&inplace, &combined);
1564
1565 crypto_aead_xchacha20poly1305_ietf_decrypt_inplace(
1566 &mut inplace,
1567 aad,
1568 &nonce,
1569 &key,
1570 )
1571 .expect("classic inplace decrypt");
1572 prop_assert_eq!(&inplace[..message.len()], message.as_slice());
1573
1574 let rust_key = Key::from(key);
1575 let rust_nonce = Nonce::from(nonce);
1576 let aead = VecBox::encrypt_to_vecbox(&message, aad, &rust_nonce, &rust_key)
1577 .expect("rustaceous encrypt");
1578 let aead_bytes = aead.to_vec();
1579 prop_assert_eq!(aead_bytes.as_slice(), combined.as_slice());
1580 let aead_decrypted = aead
1581 .decrypt_to_vec(aad, &rust_nonce, &rust_key)
1582 .expect("rustaceous decrypt");
1583 prop_assert_eq!(aead_decrypted.as_slice(), message.as_slice());
1584
1585 let mut envelope_bytes = rust_nonce.to_vec();
1586 envelope_bytes.extend_from_slice(&combined);
1587 let envelope = VecEnvelope::from_bytes(&envelope_bytes).expect("envelope parses");
1588 prop_assert_eq!(envelope.to_vec(), envelope_bytes);
1589 let envelope_decrypted = envelope
1590 .open_to_vec(aad, &rust_key)
1591 .expect("rustaceous envelope open");
1592 prop_assert_eq!(envelope_decrypted.as_slice(), message.as_slice());
1593 }
1594
1595 #[test]
1596 fn proptest_tampering_is_rejected_without_mutating_outputs(
1597 key in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES]>(),
1598 nonce in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]>(),
1599 aad in aad_strategy(),
1600 message in bytes_strategy(512),
1601 tamper_index in any::<usize>(),
1602 ) {
1603 let aad = aad.as_deref();
1604 let rust_key = Key::from(key);
1605 let rust_nonce = Nonce::from(nonce);
1606 let mut combined =
1607 vec![0u8; message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1608 crypto_aead_xchacha20poly1305_ietf_encrypt(
1609 &mut combined,
1610 &message,
1611 aad,
1612 &nonce,
1613 &key,
1614 )
1615 .expect("classic combined encrypt");
1616
1617 let mut tampered = combined;
1618 let tamper_index = tamper_index % tampered.len();
1619 tampered[tamper_index] ^= 1;
1620
1621 let mut output = vec![0xa5; message.len()];
1622 let original_output = output.clone();
1623 prop_assert!(
1624 crypto_aead_xchacha20poly1305_ietf_decrypt(
1625 &mut output,
1626 &tampered,
1627 aad,
1628 &nonce,
1629 &key,
1630 )
1631 .is_err()
1632 );
1633 prop_assert_eq!(output, original_output);
1634
1635 let parsed_box = VecBox::from_bytes(&tampered).expect("tampered box parses");
1636 prop_assert!(
1637 parsed_box
1638 .decrypt_to_vec(aad, &rust_nonce, &rust_key)
1639 .is_err()
1640 );
1641
1642 let mut tampered_envelope = rust_nonce.to_vec();
1643 tampered_envelope.extend_from_slice(&tampered);
1644 let parsed_envelope =
1645 VecEnvelope::from_bytes(&tampered_envelope).expect("tampered envelope parses");
1646 prop_assert!(parsed_envelope.open_to_vec(aad, &rust_key).is_err());
1647 }
1648
1649 #[test]
1650 fn proptest_from_bytes_round_trips_or_rejects_by_length(
1651 raw in bytes_strategy(768),
1652 ) {
1653 match VecBox::from_bytes(&raw) {
1654 Ok(parsed) => {
1655 prop_assert!(raw.len() >= CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
1656 let bytes = parsed.to_vec();
1657 prop_assert_eq!(bytes.as_slice(), raw.as_slice());
1658 }
1659 Err(_) => {
1660 prop_assert!(raw.len() < CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
1661 }
1662 }
1663
1664 let envelope_min_len = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1665 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
1666 match VecEnvelope::from_bytes(&raw) {
1667 Ok(parsed) => {
1668 prop_assert!(raw.len() >= envelope_min_len);
1669 let bytes = parsed.to_vec();
1670 prop_assert_eq!(bytes.as_slice(), raw.as_slice());
1671 }
1672 Err(_) => {
1673 prop_assert!(raw.len() < envelope_min_len);
1674 }
1675 }
1676 }
1677 }
1678 }
1679}