Skip to main content

dryoc/
dryocaead.rs

1//! # Authenticated encryption with additional data
2//!
3//! [`DryocAead`] implements libsodium's XChaCha20-Poly1305-IETF AEAD
4//! construction. The [`chacha20poly1305_ietf`] module provides the RFC 8439
5//! ChaCha20-Poly1305-IETF variant with 96-bit nonces. Both encrypt messages,
6//! authenticate optional additional data, and use libsodium-compatible wire
7//! formats.
8//!
9//! Use [`DryocAead`] when you already manage nonces and need libsodium's
10//! `ciphertext || tag` wire format. Use [`DryocAeadEnvelope`] when you want
11//! dryoc to generate a random XChaCha nonce and store it with the ciphertext as
12//! `nonce || ciphertext || tag`.
13//!
14//! XChaCha20 nonces are public, but a nonce must never repeat with the same
15//! key. [`DryocAeadEnvelope`] generates and stores a nonce for each message;
16//! callers using [`DryocAead`] must manage this uniqueness themselves.
17//!
18//! If the `serde` feature is enabled,
19//! [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) and
20//! [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) are implemented
21//! for [`AeadBox`] and [`AeadEnvelope`].
22//! If the `wincode` feature is enabled,
23//! [`wincode::SchemaRead`](https://docs.rs/wincode/latest/wincode/trait.SchemaRead.html) and
24//! [`wincode::SchemaWrite`](https://docs.rs/wincode/latest/wincode/trait.SchemaWrite.html) are
25//! implemented for [`VecBox`] and [`VecEnvelope`].
26//!
27//! ## Rustaceous API example
28//!
29//! ```
30//! use dryoc::dryocaead::*;
31//!
32//! let key = Key::generate();
33//! let nonce = Nonce::generate();
34//! let message = b"Arbitrary data to encrypt";
35//! let aad = b"metadata";
36//!
37//! let dryocaead =
38//!     DryocAead::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt failed");
39//! let bytes = dryocaead.to_vec();
40//! let dryocaead = VecBox::from_bytes(&bytes).expect("from bytes");
41//! let decrypted = dryocaead
42//!     .decrypt_to_vec(Some(aad), &nonce, &key)
43//!     .expect("decrypt failed");
44//!
45//! assert_eq!(message, decrypted.as_slice());
46//! ```
47//!
48//! ## Generated nonce envelope example
49//!
50//! ```
51//! use dryoc::dryocaead::*;
52//!
53//! let key = Key::generate();
54//! let message = b"Arbitrary data to encrypt";
55//! let aad = b"metadata";
56//!
57//! let envelope = DryocAeadEnvelope::seal_to_vec(message, Some(aad), &key).expect("seal failed");
58//! let bytes = envelope.to_vec();
59//! let envelope = VecEnvelope::from_bytes(&bytes).expect("from bytes");
60//! let decrypted = envelope.open_to_vec(Some(aad), &key).expect("open failed");
61//!
62//! assert_eq!(message, decrypted.as_slice());
63//! ```
64
65use std::marker::PhantomData;
66
67#[cfg(feature = "serde")]
68use serde::{Deserialize, Serialize};
69use subtle::ConstantTimeEq;
70use zeroize::Zeroize;
71
72use crate::constants::{
73    CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
74    CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
75    CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES, CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES,
76};
77use crate::error::Error;
78pub use crate::types::*;
79
80mod sealed {
81    pub trait Sealed {}
82}
83
84/// Marker trait for AEAD algorithms supported by dryoc.
85///
86/// This trait is sealed so applications cannot plug in custom cryptographic
87/// algorithms while still allowing dryoc to add future AEAD constructions
88/// without changing the container types.
89pub trait AeadAlgorithm:
90    sealed::Sealed + Clone + Copy + std::fmt::Debug + Default + Eq + PartialEq
91{
92}
93
94/// XChaCha20-Poly1305-IETF AEAD algorithm marker.
95#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
96pub struct XChaCha20Poly1305Ietf;
97
98impl sealed::Sealed for XChaCha20Poly1305Ietf {}
99impl AeadAlgorithm for XChaCha20Poly1305Ietf {}
100
101/// ChaCha20-Poly1305-IETF AEAD algorithm marker.
102#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
103pub struct ChaCha20Poly1305Ietf;
104
105impl sealed::Sealed for ChaCha20Poly1305Ietf {}
106impl AeadAlgorithm for ChaCha20Poly1305Ietf {}
107
108/// Stack-allocated secret key for XChaCha20-Poly1305-IETF AEAD.
109pub type Key = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>;
110/// Stack-allocated public nonce for XChaCha20-Poly1305-IETF AEAD.
111pub type Nonce = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>;
112/// Stack-allocated authentication tag for XChaCha20-Poly1305-IETF AEAD.
113pub type Mac = StackByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>;
114
115/// XChaCha20-Poly1305-IETF AEAD box.
116pub type DryocAead<Mac, Data> = AeadBox<XChaCha20Poly1305Ietf, Mac, Data>;
117/// XChaCha20-Poly1305-IETF AEAD envelope with stored nonce.
118pub type DryocAeadEnvelope<Nonce, Mac, Data> =
119    AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>;
120/// [`Vec`]-based XChaCha20-Poly1305-IETF AEAD box.
121pub type VecBox = DryocAead<Mac, Vec<u8>>;
122/// [`Vec`]-based XChaCha20-Poly1305-IETF AEAD envelope.
123pub type VecEnvelope = DryocAeadEnvelope<Nonce, Mac, Vec<u8>>;
124
125/// Algorithm-specific aliases for XChaCha20-Poly1305-IETF.
126pub mod xchacha20poly1305_ietf {
127    #[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
128    pub use super::protected;
129    pub use super::{AeadAlgorithm, AeadBox, AeadEnvelope, XChaCha20Poly1305Ietf};
130
131    /// Stack-allocated secret key.
132    pub type Key = super::Key;
133    /// Stack-allocated public nonce.
134    pub type Nonce = super::Nonce;
135    /// Stack-allocated authentication tag.
136    pub type Mac = super::Mac;
137    /// XChaCha20-Poly1305-IETF AEAD box.
138    pub type DryocAead<Mac, Data> = super::DryocAead<Mac, Data>;
139    /// XChaCha20-Poly1305-IETF AEAD envelope with stored nonce.
140    pub type DryocAeadEnvelope<Nonce, Mac, Data> = super::DryocAeadEnvelope<Nonce, Mac, Data>;
141    /// [`Vec`]-based XChaCha20-Poly1305-IETF AEAD box.
142    pub type VecBox = super::VecBox;
143    /// [`Vec`]-based XChaCha20-Poly1305-IETF AEAD envelope.
144    pub type VecEnvelope = super::VecEnvelope;
145}
146
147/// ChaCha20-Poly1305-IETF Rustaceous AEAD API.
148///
149/// A nonce must never repeat with the same key. RFC 8439 requires callers to
150/// manage these 96-bit nonces uniquely, typically with a counter, rather than
151/// generate them randomly. Accordingly, this variant does not provide the
152/// generated-nonce [`AeadEnvelope::seal`] convenience available to XChaCha20.
153/// Use [`AeadBox::encrypt`] with an explicitly managed nonce; an
154/// [`AeadEnvelope`] can store that nonce via [`AeadEnvelope::from_parts`].
155///
156/// ## Rustaceous API example
157///
158/// ```
159/// use dryoc::dryocaead::chacha20poly1305_ietf::*;
160///
161/// let key = Key::generate();
162/// // This 96-bit nonce must be unique for every message encrypted with `key`.
163/// let nonce = Nonce::from([0u8; 12]);
164/// let message = b"Better three hours too soon than a minute too late.";
165/// let aad = b"metadata";
166///
167/// let dryocaead =
168///     VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt failed");
169/// let bytes = dryocaead.to_vec();
170/// let dryocaead = VecBox::from_bytes(&bytes).expect("from bytes");
171/// let decrypted = dryocaead
172///     .decrypt_to_vec(Some(aad), &nonce, &key)
173///     .expect("decrypt failed");
174///
175/// assert_eq!(message, decrypted.as_slice());
176/// ```
177pub mod chacha20poly1305_ietf {
178    pub use super::{AeadAlgorithm, AeadBox, AeadEnvelope, ChaCha20Poly1305Ietf};
179    use crate::constants::{
180        CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES, CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES,
181        CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES,
182    };
183    pub use crate::types::*;
184
185    /// Stack-allocated secret key.
186    pub type Key = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>;
187    /// Stack-allocated public nonce.
188    pub type Nonce = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>;
189    /// Stack-allocated authentication tag.
190    pub type Mac = StackByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>;
191    /// ChaCha20-Poly1305-IETF AEAD box.
192    pub type DryocAead<Mac, Data> = AeadBox<ChaCha20Poly1305Ietf, Mac, Data>;
193    /// ChaCha20-Poly1305-IETF AEAD envelope with stored nonce.
194    pub type DryocAeadEnvelope<Nonce, Mac, Data> =
195        AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>;
196    /// [`Vec`]-based ChaCha20-Poly1305-IETF AEAD box.
197    pub type VecBox = DryocAead<Mac, Vec<u8>>;
198    /// [`Vec`]-based ChaCha20-Poly1305-IETF AEAD envelope.
199    pub type VecEnvelope = DryocAeadEnvelope<Nonce, Mac, Vec<u8>>;
200
201    #[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
202    #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
203    pub mod protected {
204        //! Protected-memory aliases for ChaCha20-Poly1305-IETF.
205        use super::*;
206        pub use crate::protected::*;
207
208        /// Heap-allocated, page-aligned secret key.
209        pub type Key = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>;
210        /// Heap-allocated, page-aligned public nonce.
211        pub type Nonce = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>;
212        /// Heap-allocated, page-aligned authentication tag.
213        pub type Mac = HeapByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>;
214        /// Locked AEAD box.
215        pub type LockedBox = AeadBox<ChaCha20Poly1305Ietf, Locked<Mac>, LockedBytes>;
216        /// Locked AEAD envelope with stored nonce.
217        pub type LockedEnvelope =
218            AeadEnvelope<ChaCha20Poly1305Ietf, Locked<Nonce>, Locked<Mac>, LockedBytes>;
219    }
220}
221
222#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
223#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
224pub mod protected {
225    //! # Protected memory type aliases for [`AeadBox`] and [`AeadEnvelope`]
226    //!
227    //! This mod provides protected-memory type aliases for the
228    //! XChaCha20-Poly1305-IETF Rustaceous AEAD API.
229    use super::*;
230    pub use crate::protected::*;
231
232    /// Heap-allocated, page-aligned secret key for XChaCha20-Poly1305-IETF.
233    pub type Key = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>;
234    /// Heap-allocated, page-aligned public nonce for XChaCha20-Poly1305-IETF.
235    pub type Nonce = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>;
236    /// Heap-allocated, page-aligned authentication tag for
237    /// XChaCha20-Poly1305-IETF.
238    pub type Mac = HeapByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>;
239
240    /// Locked AEAD box, provided as a type alias for convenience.
241    pub type LockedBox = AeadBox<XChaCha20Poly1305Ietf, Locked<Mac>, LockedBytes>;
242    /// Locked AEAD envelope with stored nonce, provided as a type alias for
243    /// convenience.
244    pub type LockedEnvelope =
245        AeadEnvelope<XChaCha20Poly1305Ietf, Locked<Nonce>, Locked<Mac>, LockedBytes>;
246}
247
248#[cfg_attr(feature = "serde", derive(Clone, Debug, Serialize, Deserialize))]
249#[cfg_attr(not(feature = "serde"), derive(Clone, Debug))]
250/// Authenticated encrypted data for a concrete AEAD algorithm.
251///
252/// The byte representation for the supported algorithms is `ciphertext || tag`.
253pub struct AeadBox<Algorithm: AeadAlgorithm, Mac, Data> {
254    #[cfg_attr(feature = "serde", serde(skip))]
255    algorithm: PhantomData<Algorithm>,
256    tag: Mac,
257    data: Data,
258}
259
260#[cfg_attr(feature = "serde", derive(Clone, Debug, Serialize, Deserialize))]
261#[cfg_attr(not(feature = "serde"), derive(Clone, Debug))]
262/// Authenticated encrypted data with its nonce stored alongside it.
263///
264/// The byte representation for the supported algorithms is
265/// `nonce || ciphertext || tag`.
266pub struct AeadEnvelope<Algorithm: AeadAlgorithm, Nonce, Mac, Data> {
267    #[cfg_attr(feature = "serde", serde(skip))]
268    algorithm: PhantomData<Algorithm>,
269    nonce: Nonce,
270    tag: Mac,
271    data: Data,
272}
273
274#[cfg(feature = "wincode")]
275// SAFETY: The implementation writes exactly the fields used to reconstruct
276// `VecBox` below, using `wincode` schema implementations for each initialized
277// field and preserving their order.
278unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for VecBox {
279    type Src = Self;
280
281    fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
282        Ok(<Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
283            + <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<
284                C,
285            >>::size_of(src.tag.as_array())?)
286    }
287
288    fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
289        <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
290        <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
291            writer,
292            src.tag.as_array(),
293        )
294    }
295}
296
297#[cfg(feature = "wincode")]
298// SAFETY: The implementation fully initializes `dst` with a valid `VecBox`
299// after successfully reading each field in the same order as `SchemaWrite`.
300unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C> for VecBox {
301    type Dst = Self;
302
303    fn read(
304        mut reader: impl wincode::io::Reader<'de>,
305        dst: &mut std::mem::MaybeUninit<Self::Dst>,
306    ) -> wincode::ReadResult<()> {
307        let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
308        let tag = <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
309            'de,
310            C,
311        >>::get(reader)?;
312        dst.write(Self {
313            algorithm: PhantomData,
314            tag: tag.into(),
315            data,
316        });
317        Ok(())
318    }
319}
320
321#[cfg(feature = "wincode")]
322// SAFETY: The implementation writes exactly the fields used to reconstruct
323// `VecEnvelope` below, using `wincode` schema implementations for each
324// initialized field and preserving their order.
325unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for VecEnvelope {
326    type Src = Self;
327
328    fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
329        Ok(
330            <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<
331                C,
332            >>::size_of(src.nonce.as_array())?
333                + <Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
334                + <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<
335                    C,
336                >>::size_of(src.tag.as_array())?,
337        )
338    }
339
340    fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
341        <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<C>>::write(
342            writer.by_ref(),
343            src.nonce.as_array(),
344        )?;
345        <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
346        <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
347            writer,
348            src.tag.as_array(),
349        )
350    }
351}
352
353#[cfg(feature = "wincode")]
354// SAFETY: The implementation fully initializes `dst` with a valid
355// `VecEnvelope` after successfully reading each field in the same order as
356// `SchemaWrite`.
357unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C> for VecEnvelope {
358    type Dst = Self;
359
360    fn read(
361        mut reader: impl wincode::io::Reader<'de>,
362        dst: &mut std::mem::MaybeUninit<Self::Dst>,
363    ) -> wincode::ReadResult<()> {
364        let nonce = <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaRead<
365            'de,
366            C,
367        >>::get(reader.by_ref())?;
368        let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
369        let tag = <[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
370            'de,
371            C,
372        >>::get(reader)?;
373        dst.write(Self {
374            algorithm: PhantomData,
375            nonce: nonce.into(),
376            tag: tag.into(),
377            data,
378        });
379        Ok(())
380    }
381}
382
383#[cfg(feature = "wincode")]
384// SAFETY: The implementation writes exactly the fields used to reconstruct
385// `chacha20poly1305_ietf::VecBox` below, using `wincode` schema implementations
386// for each initialized field and preserving their order.
387unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for chacha20poly1305_ietf::VecBox {
388    type Src = Self;
389
390    fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
391        Ok(<Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
392            + <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::size_of(
393                src.tag.as_array(),
394            )?)
395    }
396
397    fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
398        <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
399        <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
400            writer,
401            src.tag.as_array(),
402        )
403    }
404}
405
406#[cfg(feature = "wincode")]
407// SAFETY: The implementation fully initializes `dst` with a valid
408// `chacha20poly1305_ietf::VecBox` after successfully reading each field in the
409// same order as `SchemaWrite`.
410unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C>
411    for chacha20poly1305_ietf::VecBox
412{
413    type Dst = Self;
414
415    fn read(
416        mut reader: impl wincode::io::Reader<'de>,
417        dst: &mut std::mem::MaybeUninit<Self::Dst>,
418    ) -> wincode::ReadResult<()> {
419        let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
420        let tag = <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
421            'de,
422            C,
423        >>::get(reader)?;
424        dst.write(Self {
425            algorithm: PhantomData,
426            tag: tag.into(),
427            data,
428        });
429        Ok(())
430    }
431}
432
433#[cfg(feature = "wincode")]
434// SAFETY: The implementation writes exactly the fields used to reconstruct
435// `chacha20poly1305_ietf::VecEnvelope` below, using `wincode` schema
436// implementations for each initialized field and preserving their order.
437unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C>
438    for chacha20poly1305_ietf::VecEnvelope
439{
440    type Src = Self;
441
442    fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
443        Ok(
444            <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<
445                C,
446            >>::size_of(src.nonce.as_array())?
447                + <Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?
448                + <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<
449                    C,
450                >>::size_of(src.tag.as_array())?,
451        )
452    }
453
454    fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
455        <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaWrite<C>>::write(
456            writer.by_ref(),
457            src.nonce.as_array(),
458        )?;
459        <Vec<u8> as wincode::SchemaWrite<C>>::write(writer.by_ref(), &src.data)?;
460        <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaWrite<C>>::write(
461            writer,
462            src.tag.as_array(),
463        )
464    }
465}
466
467#[cfg(feature = "wincode")]
468// SAFETY: The implementation fully initializes `dst` with a valid
469// `chacha20poly1305_ietf::VecEnvelope` after successfully reading each field in
470// the same order as `SchemaWrite`.
471unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C>
472    for chacha20poly1305_ietf::VecEnvelope
473{
474    type Dst = Self;
475
476    fn read(
477        mut reader: impl wincode::io::Reader<'de>,
478        dst: &mut std::mem::MaybeUninit<Self::Dst>,
479    ) -> wincode::ReadResult<()> {
480        let nonce = <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES] as wincode::SchemaRead<
481            'de,
482            C,
483        >>::get(reader.by_ref())?;
484        let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
485        let tag = <[u8; CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES] as wincode::SchemaRead<
486            'de,
487            C,
488        >>::get(reader)?;
489        dst.write(Self {
490            algorithm: PhantomData,
491            nonce: nonce.into(),
492            tag: tag.into(),
493            data,
494        });
495        Ok(())
496    }
497}
498
499impl<Algorithm: AeadAlgorithm, Mac: Zeroize, Data: Zeroize> Zeroize
500    for AeadBox<Algorithm, Mac, Data>
501{
502    fn zeroize(&mut self) {
503        self.tag.zeroize();
504        self.data.zeroize();
505    }
506}
507
508impl<Algorithm: AeadAlgorithm, Nonce: Zeroize, Mac: Zeroize, Data: Zeroize> Zeroize
509    for AeadEnvelope<Algorithm, Nonce, Mac, Data>
510{
511    fn zeroize(&mut self) {
512        self.nonce.zeroize();
513        self.tag.zeroize();
514        self.data.zeroize();
515    }
516}
517
518impl<
519    Mac: NewByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES> + Zeroize,
520    Data: NewBytes + ResizableBytes + Zeroize,
521> AeadBox<XChaCha20Poly1305Ietf, Mac, Data>
522{
523    /// Encrypts a message using `key`, `nonce`, and optional associated data.
524    ///
525    /// # Errors
526    ///
527    /// Returns an error if the message exceeds the construction's maximum
528    /// length or the output storage does not resize to the message length.
529    pub fn encrypt<
530        Message: Bytes + ?Sized,
531        Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>,
532        SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
533    >(
534        message: &Message,
535        associated_data: Option<&[u8]>,
536        nonce: &Nonce,
537        key: &SecretKey,
538    ) -> Result<Self, Error> {
539        use crate::classic::crypto_aead_xchacha20poly1305_ietf::crypto_aead_xchacha20poly1305_ietf_encrypt_detached;
540
541        let mut new = Self {
542            algorithm: PhantomData,
543            tag: Mac::new_byte_array(),
544            data: Data::new_bytes(),
545        };
546        new.data.resize(message.len(), 0);
547
548        crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
549            new.data.as_mut_slice(),
550            new.tag.as_mut_array(),
551            message.as_slice(),
552            associated_data,
553            nonce.as_array(),
554            key.as_array(),
555        )?;
556
557        Ok(new)
558    }
559}
560
561impl<
562    Nonce: NewByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES> + Zeroize,
563    Mac: NewByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES> + Zeroize,
564    Data: NewBytes + ResizableBytes + Zeroize,
565> AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>
566{
567    /// Encrypts a message with a generated nonce and stores that nonce with the
568    /// ciphertext and tag.
569    ///
570    /// # Errors
571    ///
572    /// Returns an error if the message exceeds the construction's maximum
573    /// length or the output storage does not resize to the message length.
574    ///
575    /// # Panics
576    ///
577    /// Panics if the operating system's random number generator fails.
578    pub fn seal<
579        Message: Bytes + ?Sized,
580        SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
581    >(
582        message: &Message,
583        associated_data: Option<&[u8]>,
584        key: &SecretKey,
585    ) -> Result<Self, Error> {
586        let nonce = Nonce::generate();
587        let aead_box = AeadBox::<XChaCha20Poly1305Ietf, Mac, Data>::encrypt(
588            message,
589            associated_data,
590            &nonce,
591            key,
592        )?;
593        let (tag, data) = aead_box.into_parts();
594
595        Ok(Self {
596            algorithm: PhantomData,
597            nonce,
598            tag,
599            data,
600        })
601    }
602}
603
604impl<
605    'a,
606    Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>
607        + std::convert::TryFrom<&'a [u8]>
608        + Zeroize,
609    Data: Bytes + From<&'a [u8]> + Zeroize,
610> AeadBox<XChaCha20Poly1305Ietf, Mac, Data>
611{
612    /// Initializes an [`AeadBox`] from `ciphertext || tag`.
613    ///
614    /// # Errors
615    ///
616    /// Returns an error if `bytes` is shorter than one authentication tag or
617    /// the tag cannot be converted to `Mac`.
618    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
619        if bytes.len() < CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES {
620            Err(length_error!(
621                crate::ErrorContext::AeadCiphertext,
622                bytes.len(),
623                min CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
624            ))
625        } else {
626            let (data, tag) =
627                bytes.split_at(bytes.len() - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
628            Ok(Self {
629                algorithm: PhantomData,
630                tag: Mac::try_from(tag)
631                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
632                data: Data::from(data),
633            })
634        }
635    }
636}
637
638impl<
639    'a,
640    Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>
641        + std::convert::TryFrom<&'a [u8]>
642        + Zeroize,
643    Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>
644        + std::convert::TryFrom<&'a [u8]>
645        + Zeroize,
646    Data: Bytes + From<&'a [u8]> + Zeroize,
647> AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>
648{
649    /// Initializes an [`AeadEnvelope`] from `nonce || ciphertext || tag`.
650    ///
651    /// # Errors
652    ///
653    /// Returns an error if `bytes` is shorter than one nonce plus one
654    /// authentication tag, or if either field cannot be converted to its
655    /// target type.
656    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
657        let minimum_len = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
658            + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
659        if bytes.len() < minimum_len {
660            Err(length_error!(crate::ErrorContext::AeadEnvelope, bytes.len(), min minimum_len))
661        } else {
662            let (nonce, rest) = bytes.split_at(CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES);
663            let (data, tag) = rest.split_at(rest.len() - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
664            Ok(Self {
665                algorithm: PhantomData,
666                nonce: Nonce::try_from(nonce)
667                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::Nonce))?,
668                tag: Mac::try_from(tag)
669                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
670                data: Data::from(data),
671            })
672        }
673    }
674}
675
676impl<
677    Mac: NewByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES> + Zeroize,
678    Data: NewBytes + ResizableBytes + Zeroize,
679> AeadBox<ChaCha20Poly1305Ietf, Mac, Data>
680{
681    /// Encrypts a message using `key`, `nonce`, and optional associated data.
682    ///
683    /// # Errors
684    ///
685    /// Returns an error if the message exceeds the construction's maximum
686    /// length or the output storage does not resize to the message length.
687    pub fn encrypt<
688        Message: Bytes + ?Sized,
689        Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>,
690        SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
691    >(
692        message: &Message,
693        associated_data: Option<&[u8]>,
694        nonce: &Nonce,
695        key: &SecretKey,
696    ) -> Result<Self, Error> {
697        use crate::classic::crypto_aead_chacha20poly1305_ietf::crypto_aead_chacha20poly1305_ietf_encrypt_detached;
698
699        let mut new = Self {
700            algorithm: PhantomData,
701            tag: Mac::new_byte_array(),
702            data: Data::new_bytes(),
703        };
704        new.data.resize(message.len(), 0);
705
706        crypto_aead_chacha20poly1305_ietf_encrypt_detached(
707            new.data.as_mut_slice(),
708            new.tag.as_mut_array(),
709            message.as_slice(),
710            associated_data,
711            nonce.as_array(),
712            key.as_array(),
713        )?;
714
715        Ok(new)
716    }
717}
718
719impl<
720    'a,
721    Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
722    Data: Bytes + From<&'a [u8]> + Zeroize,
723> AeadBox<ChaCha20Poly1305Ietf, Mac, Data>
724{
725    /// Initializes an [`AeadBox`] from `ciphertext || tag`.
726    ///
727    /// # Errors
728    ///
729    /// Returns an error if `bytes` is shorter than one authentication tag or
730    /// the tag cannot be converted to `Mac`.
731    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
732        if bytes.len() < CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES {
733            Err(length_error!(
734                crate::ErrorContext::AeadCiphertext,
735                bytes.len(),
736                min CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES
737            ))
738        } else {
739            let (data, tag) =
740                bytes.split_at(bytes.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES);
741            Ok(Self {
742                algorithm: PhantomData,
743                tag: Mac::try_from(tag)
744                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
745                data: Data::from(data),
746            })
747        }
748    }
749}
750
751impl<
752    'a,
753    Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>
754        + std::convert::TryFrom<&'a [u8]>
755        + Zeroize,
756    Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
757    Data: Bytes + From<&'a [u8]> + Zeroize,
758> AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>
759{
760    /// Initializes an [`AeadEnvelope`] from `nonce || ciphertext || tag`.
761    ///
762    /// # Errors
763    ///
764    /// Returns an error if `bytes` is shorter than one nonce plus one
765    /// authentication tag, or if either field cannot be converted to its
766    /// target type.
767    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
768        let minimum_len =
769            CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES + CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
770        if bytes.len() < minimum_len {
771            Err(length_error!(crate::ErrorContext::AeadEnvelope, bytes.len(), min minimum_len))
772        } else {
773            let (nonce, rest) = bytes.split_at(CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES);
774            let (data, tag) = rest.split_at(rest.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES);
775            Ok(Self {
776                algorithm: PhantomData,
777                nonce: Nonce::try_from(nonce)
778                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::Nonce))?,
779                tag: Mac::try_from(tag)
780                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
781                data: Data::from(data),
782            })
783        }
784    }
785}
786
787impl<Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>, Data: Bytes>
788    AeadBox<ChaCha20Poly1305Ietf, Mac, Data>
789{
790    /// Decrypts this box using `key`, `nonce`, and optional associated data.
791    ///
792    /// # Errors
793    ///
794    /// Returns an error if the ciphertext exceeds the construction's maximum
795    /// length, the output storage has the wrong length, or authentication
796    /// fails. Authentication fails when the key, nonce, associated data,
797    /// ciphertext, or tag does not match the value used during encryption.
798    pub fn decrypt<
799        Output: ResizableBytes + NewBytes,
800        Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>,
801        SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
802    >(
803        &self,
804        associated_data: Option<&[u8]>,
805        nonce: &Nonce,
806        key: &SecretKey,
807    ) -> Result<Output, Error> {
808        use crate::classic::crypto_aead_chacha20poly1305_ietf::crypto_aead_chacha20poly1305_ietf_decrypt_detached;
809
810        let mut message = Output::new_bytes();
811        message.resize(self.data.as_slice().len(), 0);
812
813        crypto_aead_chacha20poly1305_ietf_decrypt_detached(
814            message.as_mut_slice(),
815            self.data.as_slice(),
816            self.tag.as_array(),
817            associated_data,
818            nonce.as_array(),
819            key.as_array(),
820        )?;
821
822        Ok(message)
823    }
824}
825
826impl<
827    Nonce: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_NPUBBYTES>,
828    Mac: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES>,
829    Data: Bytes,
830> AeadEnvelope<ChaCha20Poly1305Ietf, Nonce, Mac, Data>
831{
832    /// Decrypts this envelope using `key` and optional associated data.
833    ///
834    /// # Errors
835    ///
836    /// Returns an error if the ciphertext exceeds the construction's maximum
837    /// length, the output storage has the wrong length, or authentication
838    /// fails. Authentication fails when the key, associated data, stored
839    /// nonce, ciphertext, or tag does not match the value used during
840    /// encryption.
841    pub fn open<
842        Output: ResizableBytes + NewBytes,
843        SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
844    >(
845        &self,
846        associated_data: Option<&[u8]>,
847        key: &SecretKey,
848    ) -> Result<Output, Error> {
849        use crate::classic::crypto_aead_chacha20poly1305_ietf::crypto_aead_chacha20poly1305_ietf_decrypt_detached;
850
851        let mut message = Output::new_bytes();
852        message.resize(self.data.as_slice().len(), 0);
853
854        crypto_aead_chacha20poly1305_ietf_decrypt_detached(
855            message.as_mut_slice(),
856            self.data.as_slice(),
857            self.tag.as_array(),
858            associated_data,
859            self.nonce.as_array(),
860            key.as_array(),
861        )?;
862
863        Ok(message)
864    }
865}
866
867impl AeadBox<ChaCha20Poly1305Ietf, chacha20poly1305_ietf::Mac, Vec<u8>> {
868    /// Encrypts a message and returns a [`VecBox`].
869    ///
870    /// # Errors
871    ///
872    /// Returns an error if the message exceeds the construction's maximum
873    /// length.
874    pub fn encrypt_to_vecbox<
875        Message: Bytes + ?Sized,
876        SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>,
877    >(
878        message: &Message,
879        associated_data: Option<&[u8]>,
880        nonce: &chacha20poly1305_ietf::Nonce,
881        key: &SecretKey,
882    ) -> Result<Self, Error> {
883        Self::encrypt(message, associated_data, nonce, key)
884    }
885
886    /// Decrypts this box and returns the plaintext as a [`Vec`].
887    ///
888    /// # Errors
889    ///
890    /// Returns an error if the ciphertext exceeds the construction's maximum
891    /// length or authentication fails because the key, nonce, associated data,
892    /// ciphertext, or tag does not match.
893    pub fn decrypt_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>>(
894        &self,
895        associated_data: Option<&[u8]>,
896        nonce: &chacha20poly1305_ietf::Nonce,
897        key: &SecretKey,
898    ) -> Result<Vec<u8>, Error> {
899        self.decrypt(associated_data, nonce, key)
900    }
901
902    /// Consumes this box and returns it as `ciphertext || tag`.
903    pub fn into_vec(mut self) -> Vec<u8> {
904        self.data.resize(
905            self.data.len() + CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES,
906            0,
907        );
908        let tag_offset = self.data.len() - CRYPTO_AEAD_CHACHA20POLY1305_IETF_ABYTES;
909        self.data[tag_offset..].copy_from_slice(self.tag.as_slice());
910        self.data
911    }
912}
913
914impl
915    AeadEnvelope<
916        ChaCha20Poly1305Ietf,
917        chacha20poly1305_ietf::Nonce,
918        chacha20poly1305_ietf::Mac,
919        Vec<u8>,
920    >
921{
922    /// Decrypts this envelope and returns the plaintext as a [`Vec`].
923    ///
924    /// # Errors
925    ///
926    /// Returns an error if the ciphertext exceeds the construction's maximum
927    /// length or authentication fails because the key, associated data, stored
928    /// nonce, ciphertext, or tag does not match.
929    pub fn open_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_CHACHA20POLY1305_IETF_KEYBYTES>>(
930        &self,
931        associated_data: Option<&[u8]>,
932        key: &SecretKey,
933    ) -> Result<Vec<u8>, Error> {
934        self.open(associated_data, key)
935    }
936
937    /// Consumes this envelope and returns it as `nonce || ciphertext || tag`.
938    pub fn into_vec(self) -> Vec<u8> {
939        let mut output = self.nonce.to_vec();
940        output.extend_from_slice(self.data.as_slice());
941        output.extend_from_slice(self.tag.as_slice());
942        output
943    }
944}
945
946impl<Algorithm: AeadAlgorithm, Mac, Data> AeadBox<Algorithm, Mac, Data> {
947    /// Returns a new AEAD box from `tag` and ciphertext `data`.
948    pub fn from_parts(tag: Mac, data: Data) -> Self {
949        Self {
950            algorithm: PhantomData,
951            tag,
952            data,
953        }
954    }
955
956    /// Returns the authentication tag.
957    pub fn tag(&self) -> &Mac {
958        &self.tag
959    }
960
961    /// Returns the ciphertext.
962    pub fn data(&self) -> &Data {
963        &self.data
964    }
965
966    /// Moves the tag and ciphertext out of this instance.
967    pub fn into_parts(self) -> (Mac, Data) {
968        (self.tag, self.data)
969    }
970}
971
972impl<Algorithm: AeadAlgorithm, Mac: Bytes, Data: Bytes> AeadBox<Algorithm, Mac, Data> {
973    /// Copies `self` into a new [`Vec`].
974    pub fn to_vec(&self) -> Vec<u8> {
975        self.to_bytes()
976    }
977
978    /// Copies `self` into the target as `ciphertext || tag`.
979    pub fn to_bytes<Output: NewBytes + ResizableBytes>(&self) -> Output {
980        let mut data = Output::new_bytes();
981        data.resize(self.data.len() + self.tag.len(), 0);
982        let s = data.as_mut_slice();
983        s[..self.data.len()].copy_from_slice(self.data.as_slice());
984        s[self.data.len()..].copy_from_slice(self.tag.as_slice());
985        data
986    }
987}
988
989impl<Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>, Data: Bytes>
990    AeadBox<XChaCha20Poly1305Ietf, Mac, Data>
991{
992    /// Decrypts this box using `key`, `nonce`, and optional associated data.
993    ///
994    /// # Errors
995    ///
996    /// Returns an error if the ciphertext exceeds the construction's maximum
997    /// length, the output storage has the wrong length, or authentication
998    /// fails. Authentication fails when the key, nonce, associated data,
999    /// ciphertext, or tag does not match the value used during encryption.
1000    pub fn decrypt<
1001        Output: ResizableBytes + NewBytes,
1002        Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>,
1003        SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1004    >(
1005        &self,
1006        associated_data: Option<&[u8]>,
1007        nonce: &Nonce,
1008        key: &SecretKey,
1009    ) -> Result<Output, Error> {
1010        use crate::classic::crypto_aead_xchacha20poly1305_ietf::crypto_aead_xchacha20poly1305_ietf_decrypt_detached;
1011
1012        let mut message = Output::new_bytes();
1013        message.resize(self.data.as_slice().len(), 0);
1014
1015        crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1016            message.as_mut_slice(),
1017            self.data.as_slice(),
1018            self.tag.as_array(),
1019            associated_data,
1020            nonce.as_array(),
1021            key.as_array(),
1022        )?;
1023
1024        Ok(message)
1025    }
1026}
1027
1028impl<Algorithm: AeadAlgorithm, Nonce, Mac, Data> AeadEnvelope<Algorithm, Nonce, Mac, Data> {
1029    /// Returns a new AEAD envelope from `nonce`, `tag`, and ciphertext `data`.
1030    pub fn from_parts(nonce: Nonce, tag: Mac, data: Data) -> Self {
1031        Self {
1032            algorithm: PhantomData,
1033            nonce,
1034            tag,
1035            data,
1036        }
1037    }
1038
1039    /// Returns the stored nonce.
1040    pub fn nonce(&self) -> &Nonce {
1041        &self.nonce
1042    }
1043
1044    /// Returns the authentication tag.
1045    pub fn tag(&self) -> &Mac {
1046        &self.tag
1047    }
1048
1049    /// Returns the ciphertext.
1050    pub fn data(&self) -> &Data {
1051        &self.data
1052    }
1053
1054    /// Moves the nonce, tag, and ciphertext out of this instance.
1055    pub fn into_parts(self) -> (Nonce, Mac, Data) {
1056        (self.nonce, self.tag, self.data)
1057    }
1058}
1059
1060impl<Algorithm: AeadAlgorithm, Nonce: Bytes, Mac: Bytes, Data: Bytes>
1061    AeadEnvelope<Algorithm, Nonce, Mac, Data>
1062{
1063    /// Copies `self` into a new [`Vec`].
1064    pub fn to_vec(&self) -> Vec<u8> {
1065        self.to_bytes()
1066    }
1067
1068    /// Copies `self` into the target as `nonce || ciphertext || tag`.
1069    pub fn to_bytes<Output: NewBytes + ResizableBytes>(&self) -> Output {
1070        let mut data = Output::new_bytes();
1071        data.resize(self.nonce.len() + self.data.len() + self.tag.len(), 0);
1072        let s = data.as_mut_slice();
1073        s[..self.nonce.len()].copy_from_slice(self.nonce.as_slice());
1074        s[self.nonce.len()..self.nonce.len() + self.data.len()]
1075            .copy_from_slice(self.data.as_slice());
1076        s[self.nonce.len() + self.data.len()..].copy_from_slice(self.tag.as_slice());
1077        data
1078    }
1079}
1080
1081impl<
1082    Nonce: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES>,
1083    Mac: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES>,
1084    Data: Bytes,
1085> AeadEnvelope<XChaCha20Poly1305Ietf, Nonce, Mac, Data>
1086{
1087    /// Decrypts this envelope using `key` and optional associated data.
1088    ///
1089    /// # Errors
1090    ///
1091    /// Returns an error if the ciphertext exceeds the construction's maximum
1092    /// length, the output storage has the wrong length, or authentication
1093    /// fails. Authentication fails when the key, associated data, stored
1094    /// nonce, ciphertext, or tag does not match the value used during
1095    /// encryption.
1096    pub fn open<
1097        Output: ResizableBytes + NewBytes,
1098        SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1099    >(
1100        &self,
1101        associated_data: Option<&[u8]>,
1102        key: &SecretKey,
1103    ) -> Result<Output, Error> {
1104        use crate::classic::crypto_aead_xchacha20poly1305_ietf::crypto_aead_xchacha20poly1305_ietf_decrypt_detached;
1105
1106        let mut message = Output::new_bytes();
1107        message.resize(self.data.as_slice().len(), 0);
1108
1109        crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1110            message.as_mut_slice(),
1111            self.data.as_slice(),
1112            self.tag.as_array(),
1113            associated_data,
1114            self.nonce.as_array(),
1115            key.as_array(),
1116        )?;
1117
1118        Ok(message)
1119    }
1120}
1121
1122impl DryocAead<Mac, Vec<u8>> {
1123    /// Encrypts a message and returns a [`VecBox`].
1124    ///
1125    /// # Errors
1126    ///
1127    /// Returns an error if the message exceeds the construction's maximum
1128    /// length.
1129    pub fn encrypt_to_vecbox<
1130        Message: Bytes + ?Sized,
1131        SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1132    >(
1133        message: &Message,
1134        associated_data: Option<&[u8]>,
1135        nonce: &Nonce,
1136        key: &SecretKey,
1137    ) -> Result<Self, Error> {
1138        Self::encrypt(message, associated_data, nonce, key)
1139    }
1140
1141    /// Decrypts this box and returns the plaintext as a [`Vec`].
1142    ///
1143    /// # Errors
1144    ///
1145    /// Returns an error if the ciphertext exceeds the construction's maximum
1146    /// length or authentication fails because the key, nonce, associated data,
1147    /// ciphertext, or tag does not match.
1148    pub fn decrypt_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>>(
1149        &self,
1150        associated_data: Option<&[u8]>,
1151        nonce: &Nonce,
1152        key: &SecretKey,
1153    ) -> Result<Vec<u8>, Error> {
1154        self.decrypt(associated_data, nonce, key)
1155    }
1156
1157    /// Consumes this box and returns it as `ciphertext || tag`.
1158    pub fn into_vec(mut self) -> Vec<u8> {
1159        self.data.resize(
1160            self.data.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES,
1161            0,
1162        );
1163        let tag_offset = self.data.len() - CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
1164        self.data[tag_offset..].copy_from_slice(self.tag.as_slice());
1165        self.data
1166    }
1167}
1168
1169impl DryocAeadEnvelope<Nonce, Mac, Vec<u8>> {
1170    /// Encrypts a message with a generated nonce and returns a [`VecEnvelope`].
1171    ///
1172    /// # Errors
1173    ///
1174    /// Returns an error if the message exceeds the construction's maximum
1175    /// length.
1176    ///
1177    /// # Panics
1178    ///
1179    /// Panics if the operating system's random number generator fails.
1180    pub fn seal_to_vec<
1181        Message: Bytes + ?Sized,
1182        SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>,
1183    >(
1184        message: &Message,
1185        associated_data: Option<&[u8]>,
1186        key: &SecretKey,
1187    ) -> Result<Self, Error> {
1188        Self::seal(message, associated_data, key)
1189    }
1190
1191    /// Decrypts this envelope and returns the plaintext as a [`Vec`].
1192    ///
1193    /// # Errors
1194    ///
1195    /// Returns an error if the ciphertext exceeds the construction's maximum
1196    /// length or authentication fails because the key, associated data, stored
1197    /// nonce, ciphertext, or tag does not match.
1198    pub fn open_to_vec<SecretKey: ByteArray<CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES>>(
1199        &self,
1200        associated_data: Option<&[u8]>,
1201        key: &SecretKey,
1202    ) -> Result<Vec<u8>, Error> {
1203        self.open(associated_data, key)
1204    }
1205
1206    /// Consumes this envelope and returns it as `nonce || ciphertext || tag`.
1207    pub fn into_vec(self) -> Vec<u8> {
1208        let mut output = self.nonce.to_vec();
1209        output.extend_from_slice(self.data.as_slice());
1210        output.extend_from_slice(self.tag.as_slice());
1211        output
1212    }
1213}
1214
1215impl<'a, Algorithm: AeadAlgorithm, Mac, Data: From<&'a [u8]>> AeadBox<Algorithm, Mac, Data> {
1216    /// Returns a new box with ciphertext copied from `input` and `tag`
1217    /// consumed.
1218    pub fn with_data_and_mac(tag: Mac, input: &'a [u8]) -> Self {
1219        Self {
1220            algorithm: PhantomData,
1221            tag,
1222            data: input.into(),
1223        }
1224    }
1225}
1226
1227impl<'a, Algorithm: AeadAlgorithm, Nonce, Mac, Data: From<&'a [u8]>>
1228    AeadEnvelope<Algorithm, Nonce, Mac, Data>
1229{
1230    /// Returns a new envelope with nonce and tag consumed and ciphertext copied
1231    /// from `input`.
1232    pub fn with_nonce_data_and_mac(nonce: Nonce, tag: Mac, input: &'a [u8]) -> Self {
1233        Self {
1234            algorithm: PhantomData,
1235            nonce,
1236            tag,
1237            data: input.into(),
1238        }
1239    }
1240}
1241
1242impl<Algorithm: AeadAlgorithm, Mac: Bytes, Data: Bytes> PartialEq
1243    for AeadBox<Algorithm, Mac, Data>
1244{
1245    fn eq(&self, other: &Self) -> bool {
1246        self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
1247            && self
1248                .data
1249                .as_slice()
1250                .ct_eq(other.data.as_slice())
1251                .unwrap_u8()
1252                == 1
1253    }
1254}
1255
1256impl<Algorithm: AeadAlgorithm, Nonce: Bytes, Mac: Bytes, Data: Bytes> PartialEq
1257    for AeadEnvelope<Algorithm, Nonce, Mac, Data>
1258{
1259    fn eq(&self, other: &Self) -> bool {
1260        self.nonce
1261            .as_slice()
1262            .ct_eq(other.nonce.as_slice())
1263            .unwrap_u8()
1264            == 1
1265            && self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
1266            && self
1267                .data
1268                .as_slice()
1269                .ct_eq(other.data.as_slice())
1270                .unwrap_u8()
1271                == 1
1272    }
1273}
1274
1275#[cfg(test)]
1276mod tests {
1277    use super::*;
1278
1279    #[test]
1280    fn test_explicit_box_layout() {
1281        let key = Key::generate();
1282        let nonce = Nonce::generate();
1283        let message = b"hello";
1284        let aad = b"metadata";
1285
1286        let aead = VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt");
1287        let bytes = aead.to_vec();
1288        assert_eq!(
1289            bytes.len(),
1290            message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1291        );
1292
1293        let parsed = VecBox::from_bytes(&bytes).expect("from bytes");
1294        let decrypted = parsed
1295            .decrypt_to_vec(Some(aad), &nonce, &key)
1296            .expect("decrypt");
1297        assert_eq!(decrypted, message);
1298    }
1299
1300    #[test]
1301    fn test_explicit_box_failures() {
1302        let key = Key::generate();
1303        let nonce = Nonce::generate();
1304        let message = b"hello";
1305        let aad = b"metadata";
1306
1307        let aead = VecBox::encrypt_to_vecbox(message, Some(aad), &nonce, &key).expect("encrypt");
1308
1309        aead.decrypt_to_vec(Some(b"wrong aad"), &nonce, &key)
1310            .expect_err("wrong aad should fail");
1311
1312        let mut wrong_key = key.clone();
1313        wrong_key.as_mut_slice()[0] ^= 1;
1314        aead.decrypt_to_vec(Some(aad), &nonce, &wrong_key)
1315            .expect_err("wrong key should fail");
1316
1317        let mut wrong_nonce = nonce.clone();
1318        wrong_nonce.as_mut_slice()[0] ^= 1;
1319        aead.decrypt_to_vec(Some(aad), &wrong_nonce, &key)
1320            .expect_err("wrong nonce should fail");
1321
1322        let mut modified_ciphertext = aead.clone();
1323        modified_ciphertext.data.as_mut_slice()[0] ^= 1;
1324        modified_ciphertext
1325            .decrypt_to_vec(Some(aad), &nonce, &key)
1326            .expect_err("modified ciphertext should fail");
1327
1328        let mut modified_tag = aead.clone();
1329        modified_tag.tag.as_mut_slice()[0] ^= 1;
1330        modified_tag
1331            .decrypt_to_vec(Some(aad), &nonce, &key)
1332            .expect_err("modified tag should fail");
1333    }
1334
1335    #[test]
1336    fn test_explicit_box_empty_message_and_no_aad() {
1337        let key = Key::generate();
1338        let nonce = Nonce::generate();
1339
1340        let aead = VecBox::encrypt_to_vecbox(&[], None, &nonce, &key).expect("encrypt");
1341        assert_eq!(
1342            aead.to_vec().len(),
1343            CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1344        );
1345
1346        let decrypted = aead
1347            .decrypt_to_vec(None, &nonce, &key)
1348            .expect("decrypt empty");
1349        assert!(decrypted.is_empty());
1350    }
1351
1352    #[test]
1353    fn test_envelope_layout() {
1354        let key = Key::generate();
1355        let message = b"hello";
1356        let aad = b"metadata";
1357
1358        let envelope = VecEnvelope::seal_to_vec(message, Some(aad), &key).expect("seal");
1359        let bytes = envelope.to_vec();
1360        assert_eq!(
1361            bytes.len(),
1362            CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1363                + message.len()
1364                + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1365        );
1366        assert_eq!(
1367            &bytes[..CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES],
1368            envelope.nonce().as_slice()
1369        );
1370
1371        let parsed = VecEnvelope::from_bytes(&bytes).expect("from bytes");
1372        let decrypted = parsed.open_to_vec(Some(aad), &key).expect("open");
1373        assert_eq!(decrypted, message);
1374    }
1375
1376    #[test]
1377    fn test_envelope_failures() {
1378        let key = Key::generate();
1379        let message = b"hello";
1380        let aad = b"metadata";
1381
1382        let envelope = VecEnvelope::seal_to_vec(message, Some(aad), &key).expect("seal");
1383
1384        envelope
1385            .open_to_vec(Some(b"wrong aad"), &key)
1386            .expect_err("wrong aad should fail");
1387
1388        let mut wrong_key = key.clone();
1389        wrong_key.as_mut_slice()[0] ^= 1;
1390        envelope
1391            .open_to_vec(Some(aad), &wrong_key)
1392            .expect_err("wrong key should fail");
1393
1394        let mut modified_nonce = envelope.clone();
1395        modified_nonce.nonce.as_mut_slice()[0] ^= 1;
1396        modified_nonce
1397            .open_to_vec(Some(aad), &key)
1398            .expect_err("modified nonce should fail");
1399
1400        let mut modified_ciphertext = envelope.clone();
1401        modified_ciphertext.data.as_mut_slice()[0] ^= 1;
1402        modified_ciphertext
1403            .open_to_vec(Some(aad), &key)
1404            .expect_err("modified ciphertext should fail");
1405
1406        let mut modified_tag = envelope.clone();
1407        modified_tag.tag.as_mut_slice()[0] ^= 1;
1408        modified_tag
1409            .open_to_vec(Some(aad), &key)
1410            .expect_err("modified tag should fail");
1411    }
1412
1413    #[test]
1414    fn test_envelope_empty_message_and_no_aad() {
1415        let key = Key::generate();
1416
1417        let envelope = VecEnvelope::seal_to_vec(&[], None, &key).expect("seal");
1418        assert_eq!(
1419            envelope.to_vec().len(),
1420            CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1421                + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1422        );
1423
1424        let decrypted = envelope.open_to_vec(None, &key).expect("open empty");
1425        assert!(decrypted.is_empty());
1426    }
1427
1428    #[test]
1429    fn test_from_bytes_boundaries() {
1430        assert!(VecBox::from_bytes(&[]).is_err());
1431
1432        let empty_box_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1433        let empty_box = VecBox::from_bytes(&empty_box_bytes).expect("empty box parses");
1434        assert!(empty_box.data().is_empty());
1435        assert_eq!(empty_box.tag().as_slice(), empty_box_bytes.as_slice());
1436
1437        let short_envelope_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1438            + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES
1439            - 1];
1440        assert!(VecEnvelope::from_bytes(&short_envelope_bytes).is_err());
1441
1442        let empty_envelope_bytes = [0u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1443            + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1444        let empty_envelope =
1445            VecEnvelope::from_bytes(&empty_envelope_bytes).expect("empty envelope parses");
1446        assert!(empty_envelope.data().is_empty());
1447        assert_eq!(
1448            empty_envelope.nonce().as_slice(),
1449            &empty_envelope_bytes[..CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]
1450        );
1451        assert_eq!(
1452            empty_envelope.tag().as_slice(),
1453            &empty_envelope_bytes[CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES..]
1454        );
1455    }
1456
1457    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1458    mod property_tests {
1459        use proptest::prelude::*;
1460
1461        use super::*;
1462        use crate::classic::crypto_aead_xchacha20poly1305_ietf::{
1463            Mac as ClassicMac, crypto_aead_xchacha20poly1305_ietf_decrypt,
1464            crypto_aead_xchacha20poly1305_ietf_decrypt_detached,
1465            crypto_aead_xchacha20poly1305_ietf_decrypt_inplace,
1466            crypto_aead_xchacha20poly1305_ietf_encrypt,
1467            crypto_aead_xchacha20poly1305_ietf_encrypt_detached,
1468            crypto_aead_xchacha20poly1305_ietf_encrypt_inplace,
1469        };
1470
1471        fn length_strategy(max: usize) -> impl Strategy<Value = usize> {
1472            prop_oneof![
1473                Just(0usize),
1474                Just(1),
1475                Just(15),
1476                Just(16),
1477                Just(17),
1478                Just(63),
1479                Just(64),
1480                Just(65),
1481                Just(max.saturating_sub(1)),
1482                Just(max),
1483                0usize..=max,
1484            ]
1485        }
1486
1487        fn bytes_strategy(max: usize) -> impl Strategy<Value = Vec<u8>> {
1488            length_strategy(max).prop_flat_map(|len| prop::collection::vec(any::<u8>(), len))
1489        }
1490
1491        fn aad_strategy() -> impl Strategy<Value = Option<Vec<u8>>> {
1492            prop::option::of(bytes_strategy(256))
1493        }
1494
1495        proptest! {
1496            #![proptest_config(ProptestConfig::with_cases(96))]
1497
1498            #[test]
1499            fn proptest_classic_modes_and_rustaceous_layouts_agree(
1500                key in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES]>(),
1501                nonce in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]>(),
1502                aad in aad_strategy(),
1503                message in bytes_strategy(512),
1504            ) {
1505                let aad = aad.as_deref();
1506
1507                let mut combined =
1508                    vec![0u8; message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1509                crypto_aead_xchacha20poly1305_ietf_encrypt(
1510                    &mut combined,
1511                    &message,
1512                    aad,
1513                    &nonce,
1514                    &key,
1515                )
1516                .expect("classic combined encrypt");
1517
1518                let mut decrypted = vec![0u8; message.len()];
1519                crypto_aead_xchacha20poly1305_ietf_decrypt(
1520                    &mut decrypted,
1521                    &combined,
1522                    aad,
1523                    &nonce,
1524                    &key,
1525                )
1526                .expect("classic combined decrypt");
1527                prop_assert_eq!(decrypted.as_slice(), message.as_slice());
1528
1529                let mut detached = vec![0u8; message.len()];
1530                let mut mac = ClassicMac::default();
1531                crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
1532                    &mut detached,
1533                    &mut mac,
1534                    &message,
1535                    aad,
1536                    &nonce,
1537                    &key,
1538                )
1539                .expect("classic detached encrypt");
1540                prop_assert_eq!(&detached, &combined[..message.len()]);
1541                prop_assert_eq!(mac.as_slice(), &combined[message.len()..]);
1542
1543                let mut detached_decrypted = vec![0u8; message.len()];
1544                crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
1545                    &mut detached_decrypted,
1546                    &detached,
1547                    &mac,
1548                    aad,
1549                    &nonce,
1550                    &key,
1551                )
1552                .expect("classic detached decrypt");
1553                prop_assert_eq!(detached_decrypted.as_slice(), message.as_slice());
1554
1555                let mut inplace = message.clone();
1556                inplace.resize(message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES, 0);
1557                crypto_aead_xchacha20poly1305_ietf_encrypt_inplace(
1558                    &mut inplace,
1559                    aad,
1560                    &nonce,
1561                    &key,
1562                )
1563                .expect("classic inplace encrypt");
1564                prop_assert_eq!(&inplace, &combined);
1565
1566                crypto_aead_xchacha20poly1305_ietf_decrypt_inplace(
1567                    &mut inplace,
1568                    aad,
1569                    &nonce,
1570                    &key,
1571                )
1572                .expect("classic inplace decrypt");
1573                prop_assert_eq!(&inplace[..message.len()], message.as_slice());
1574
1575                let rust_key = Key::from(key);
1576                let rust_nonce = Nonce::from(nonce);
1577                let aead = VecBox::encrypt_to_vecbox(&message, aad, &rust_nonce, &rust_key)
1578                    .expect("rustaceous encrypt");
1579                let aead_bytes = aead.to_vec();
1580                prop_assert_eq!(aead_bytes.as_slice(), combined.as_slice());
1581                let aead_decrypted = aead
1582                    .decrypt_to_vec(aad, &rust_nonce, &rust_key)
1583                    .expect("rustaceous decrypt");
1584                prop_assert_eq!(aead_decrypted.as_slice(), message.as_slice());
1585
1586                let mut envelope_bytes = rust_nonce.to_vec();
1587                envelope_bytes.extend_from_slice(&combined);
1588                let envelope = VecEnvelope::from_bytes(&envelope_bytes).expect("envelope parses");
1589                prop_assert_eq!(envelope.to_vec(), envelope_bytes);
1590                let envelope_decrypted = envelope
1591                    .open_to_vec(aad, &rust_key)
1592                    .expect("rustaceous envelope open");
1593                prop_assert_eq!(envelope_decrypted.as_slice(), message.as_slice());
1594            }
1595
1596            #[test]
1597            fn proptest_tampering_is_rejected_without_mutating_outputs(
1598                key in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES]>(),
1599                nonce in any::<[u8; CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES]>(),
1600                aad in aad_strategy(),
1601                message in bytes_strategy(512),
1602                tamper_index in any::<usize>(),
1603            ) {
1604                let aad = aad.as_deref();
1605                let rust_key = Key::from(key);
1606                let rust_nonce = Nonce::from(nonce);
1607                let mut combined =
1608                    vec![0u8; message.len() + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES];
1609                crypto_aead_xchacha20poly1305_ietf_encrypt(
1610                    &mut combined,
1611                    &message,
1612                    aad,
1613                    &nonce,
1614                    &key,
1615                )
1616                .expect("classic combined encrypt");
1617
1618                let mut tampered = combined;
1619                let tamper_index = tamper_index % tampered.len();
1620                tampered[tamper_index] ^= 1;
1621
1622                let mut output = vec![0xa5; message.len()];
1623                let original_output = output.clone();
1624                prop_assert!(
1625                    crypto_aead_xchacha20poly1305_ietf_decrypt(
1626                        &mut output,
1627                        &tampered,
1628                        aad,
1629                        &nonce,
1630                        &key,
1631                    )
1632                    .is_err()
1633                );
1634                prop_assert_eq!(output, original_output);
1635
1636                let parsed_box = VecBox::from_bytes(&tampered).expect("tampered box parses");
1637                prop_assert!(
1638                    parsed_box
1639                        .decrypt_to_vec(aad, &rust_nonce, &rust_key)
1640                        .is_err()
1641                );
1642
1643                let mut tampered_envelope = rust_nonce.to_vec();
1644                tampered_envelope.extend_from_slice(&tampered);
1645                let parsed_envelope =
1646                    VecEnvelope::from_bytes(&tampered_envelope).expect("tampered envelope parses");
1647                prop_assert!(parsed_envelope.open_to_vec(aad, &rust_key).is_err());
1648            }
1649
1650            #[test]
1651            fn proptest_from_bytes_round_trips_or_rejects_by_length(
1652                raw in bytes_strategy(768),
1653            ) {
1654                match VecBox::from_bytes(&raw) {
1655                    Ok(parsed) => {
1656                        prop_assert!(raw.len() >= CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
1657                        let bytes = parsed.to_vec();
1658                        prop_assert_eq!(bytes.as_slice(), raw.as_slice());
1659                    }
1660                    Err(_) => {
1661                        prop_assert!(raw.len() < CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES);
1662                    }
1663                }
1664
1665                let envelope_min_len = CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES
1666                    + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES;
1667                match VecEnvelope::from_bytes(&raw) {
1668                    Ok(parsed) => {
1669                        prop_assert!(raw.len() >= envelope_min_len);
1670                        let bytes = parsed.to_vec();
1671                        prop_assert_eq!(bytes.as_slice(), raw.as_slice());
1672                    }
1673                    Err(_) => {
1674                        prop_assert!(raw.len() < envelope_min_len);
1675                    }
1676                }
1677            }
1678        }
1679    }
1680}