Skip to main content

dryoc/
dryocbox.rs

1//! # Public-key authenticated encryption
2//!
3//! [`DryocBox`] provides libsodium-compatible public-key authenticated
4//! encryption, also known as a _box_. It uses X25519 to establish a shared
5//! key, XSalsa20 to encrypt the message, and Poly1305 to detect tampering.
6//!
7//! Use a [`DryocBox`] when a sender and recipient have each other's public keys
8//! and need to exchange encrypted messages. The recipient can verify that a
9//! message was created with the sender's secret key. A box is not a public
10//! signature: the recipient can also create messages that appear to come from
11//! the sender.
12//!
13//! [`DryocBox::seal`] provides anonymous encryption instead. It creates a new
14//! temporary keypair for each message and stores the temporary public key with
15//! the ciphertext. A sealed box proves that the ciphertext was not changed,
16//! but it does not identify the sender. For sealed boxes that stay
17//! confidential against future quantum computers, use
18//! [`DryocSealedBox`](crate::dryocsealedbox::DryocSealedBox), which has the
19//! same `seal`/`open` methods.
20//!
21//! Nonces are public, but a nonce must never repeat for the same sender and
22//! recipient keypair. The two parties share one nonce space unless they use
23//! separate keys for each direction. Callers of [`DryocBox::encrypt`] must
24//! enforce this rule. [`DryocBox::seal`] handles nonce generation internally.
25//!
26//! With the `serde` feature,
27//! [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) and
28//! [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) are implemented
29//! for [`DryocBox`]. With `wincode_0_6`,
30//! [`wincode::SchemaRead`](https://docs.rs/wincode/0.6/wincode/trait.SchemaRead.html) and
31//! [`wincode::SchemaWrite`](https://docs.rs/wincode/0.6/wincode/trait.SchemaWrite.html) are
32//! implemented for [`VecBox`].
33//!
34//! ## Rustaceous API example
35//!
36//! ```
37//! # #[cfg(feature = "alloc")]
38//! # {
39//! use dryoc::dryocbox::*;
40//! use dryoc::types::*;
41//!
42//! // In a real exchange, each party keeps its secret key private and shares
43//! // only its public key.
44//! let sender_keypair = StackKeyPair::generate();
45//! let recipient_keypair = StackKeyPair::generate();
46//!
47//! // Generate a random nonce. At 24 bytes, the chance of a random nonce
48//! // repeating is negligible.
49//! let nonce = Nonce::generate();
50//!
51//! let message = b"All that glitters is not gold";
52//!
53//! // Encrypt the message into a Vec<u8>-based box.
54//! let dryocbox = DryocBox::encrypt_to_vecbox(
55//!     message,
56//!     &nonce,
57//!     &recipient_keypair.public_key,
58//!     &sender_keypair.secret_key,
59//! )
60//! .expect("unable to encrypt");
61//!
62//! // Serialize the box in libsodium's wire format, then read it back.
63//! let sodium_box = dryocbox.to_vec();
64//! let dryocbox = DryocBox::from_bytes(&sodium_box).expect("failed to read box");
65//!
66//! // Decrypt with the recipient's secret key and the sender's public key.
67//! let decrypted = dryocbox
68//!     .decrypt_to_vec(
69//!         &nonce,
70//!         &sender_keypair.public_key,
71//!         &recipient_keypair.secret_key,
72//!     )
73//!     .expect("unable to decrypt");
74//!
75//! assert_eq!(message, decrypted.as_slice());
76//! # }
77//! ```
78//!
79//! ## Sealed box example
80//!
81//! ```
82//! # #[cfg(feature = "alloc")]
83//! # {
84//! use dryoc::dryocbox::*;
85//!
86//! let recipient_keypair = StackKeyPair::generate();
87//! let message = b"Now is the winter of our discontent.";
88//!
89//! let dryocbox = DryocBox::seal_to_vecbox(message, &recipient_keypair.public_key.clone())
90//!     .expect("unable to seal");
91//!
92//! let decrypted = dryocbox
93//!     .open_to_vec(&recipient_keypair)
94//!     .expect("unable to open");
95//!
96//! assert_eq!(message, decrypted.as_slice());
97//! # }
98//! ```
99//!
100//! ## Additional resources
101//!
102//! * See the [libsodium documentation](https://doc.libsodium.org/public-key_cryptography/authenticated_encryption)
103//!   for more about authenticated public-key encryption
104//! * For shared-key encryption, see [`DryocSecretBox`](crate::dryocsecretbox)
105//! * For encrypted message streams, see [`DryocStream`](crate::dryocstream)
106//! * See the [`protected`] module for an example that stores keys in protected
107//!   memory
108
109#[cfg(feature = "alloc")]
110use alloc::vec::Vec;
111
112#[cfg(feature = "serde")]
113use serde::{Deserialize, Serialize};
114use zeroize::{Zeroize, Zeroizing};
115
116use crate::constants::{
117    CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_MACBYTES, CRYPTO_BOX_NONCEBYTES,
118    CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SEALBYTES, CRYPTO_BOX_SECRETKEYBYTES,
119};
120use crate::error::*;
121use crate::types::*;
122use crate::utils::{ct_eq_bytes, split_prefix};
123
124/// Stack-allocated public key for authenticated public-key boxes.
125pub type PublicKey = StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
126/// Stack-allocated secret key for authenticated public-key boxes.
127pub type SecretKey = StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
128/// Stack-allocated nonce for authenticated public-key boxes.
129pub type Nonce = StackByteArray<CRYPTO_BOX_NONCEBYTES>;
130/// Stack-allocated message authentication code for authenticated public-key
131/// boxes.
132pub type Mac = StackByteArray<CRYPTO_BOX_MACBYTES>;
133/// Stack-allocated public/secret keypair for authenticated public-key
134/// boxes.
135pub type StackKeyPair = crate::keypair::KeyPair<PublicKey, SecretKey>;
136
137#[cfg(any(
138    all(feature = "protected", any(unix, windows)),
139    all(doc, not(doctest), feature = "std")
140))]
141#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
142pub mod protected {
143    //! # Protected memory type aliases for [`DryocBox`]
144    //!
145    //! Type aliases for using [`DryocBox`] with protected memory.
146    //!
147    //! ## Example
148    //!
149    //! ```
150    //! use dryoc::dryocbox::DryocBox;
151    //! use dryoc::dryocbox::protected::*;
152    //!
153    //! // Generate a random sender and recipient keypair, into locked, readonly
154    //! // memory.
155    //! let sender_keypair = LockedROKeyPair::generate_readonly_locked_keypair().expect("keypair");
156    //! let recipient_keypair = LockedROKeyPair::generate_readonly_locked_keypair().expect("keypair");
157    //!
158    //! // Generate a random nonce, into locked, readonly memory.
159    //! let nonce = Nonce::generate_readonly_locked().expect("nonce failed");
160    //!
161    //! // Read message into locked, readonly memory.
162    //! let message = HeapBytes::from_slice_into_readonly_locked(b"Secret message from Santa Claus")
163    //!     .expect("message failed");
164    //!
165    //! // Encrypt message into a locked box.
166    //! let dryocbox: LockedBox = DryocBox::encrypt(
167    //!     &message,
168    //!     &nonce,
169    //!     &recipient_keypair.public_key,
170    //!     &sender_keypair.secret_key,
171    //! )
172    //! .expect("encrypt failed");
173    //!
174    //! // Decrypt message into locked bytes.
175    //! let decrypted: LockedBytes = dryocbox
176    //!     .decrypt(
177    //!         &nonce,
178    //!         &sender_keypair.public_key,
179    //!         &recipient_keypair.secret_key,
180    //!     )
181    //!     .expect("decrypt failed");
182    //!
183    //! assert_eq!(message.as_slice(), decrypted.as_slice());
184    //! ```
185    use super::*;
186    pub use crate::protected::*;
187
188    /// Heap-allocated, page-aligned public key for authenticated public-key
189    /// boxes, for use with protected memory.
190    pub type PublicKey = HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
191    /// Heap-allocated, page-aligned secret key for authenticated public-key
192    /// boxes, for use with protected memory.
193    pub type SecretKey = HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
194    /// Heap-allocated, page-aligned nonce for authenticated public-key
195    /// boxes, for use with protected memory.
196    pub type Nonce = HeapByteArray<CRYPTO_BOX_NONCEBYTES>;
197    /// Heap-allocated, page-aligned message authentication code for
198    /// authenticated public-key boxes, for use with protected memory.
199    pub type Mac = HeapByteArray<CRYPTO_BOX_MACBYTES>;
200
201    /// Heap-allocated, page-aligned public/secret keypair for
202    /// authenticated public-key boxes, for use with protected memory.
203    pub type LockedKeyPair = crate::keypair::KeyPair<Locked<PublicKey>, Locked<SecretKey>>;
204    /// Heap-allocated, page-aligned public/secret keypair for
205    /// authenticated public-key boxes, for use with protected memory.
206    pub type LockedROKeyPair = crate::keypair::KeyPair<LockedRO<PublicKey>, LockedRO<SecretKey>>;
207    /// Locked [DryocBox], provided as a type alias for convenience.
208    pub type LockedBox = DryocBox<Locked<PublicKey>, Locked<Mac>, LockedBytes>;
209}
210
211#[derive(Zeroize, Clone, Debug)]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213/// A libsodium public-key authenticated encrypted box.
214///
215/// Refer to [crate::dryocbox] for sample usage.
216pub struct DryocBox<
217    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
218    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
219    Data: Bytes + Zeroize,
220> {
221    ephemeral_pk: Option<EphemeralPublicKey>,
222    tag: Mac,
223    data: Data,
224}
225
226/// [Vec]-based authenticated public-key box.
227#[cfg(feature = "alloc")]
228pub type VecBox = DryocBox<PublicKey, Mac, Vec<u8>>;
229
230#[cfg(feature = "wincode_0_6")]
231impl_wincode_schema!(VecBox {
232    ephemeral_pk: Option<[u8; CRYPTO_BOX_PUBLICKEYBYTES]> = (
233        src => &src.ephemeral_pk.as_ref().map(|epk| *epk.as_array()),
234        epk => epk.map(Into::into)
235    ),
236    tag: [u8; CRYPTO_BOX_MACBYTES] = (src => src.tag.as_array(), tag => tag.into()),
237    data: Vec<u8> = (src => &src.data, data => data),
238});
239
240impl<
241    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
242    Mac: NewByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
243    Data: NewBytes + ResizableBytes + Zeroize,
244> DryocBox<EphemeralPublicKey, Mac, Data>
245{
246    /// Encrypts a message using `sender_secret_key` for `recipient_public_key`,
247    /// and returns a new [`DryocBox`] with ciphertext and tag.
248    ///
249    /// # Errors
250    ///
251    /// Returns an error if the message is too long, `recipient_public_key` is
252    /// an unacceptable low-order key, or the output storage does not resize to
253    /// the message length.
254    pub fn encrypt<
255        Message: Bytes + ?Sized,
256        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
257        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
258        SenderSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
259    >(
260        message: &Message,
261        nonce: &Nonce,
262        recipient_public_key: &RecipientPublicKey,
263        sender_secret_key: &SenderSecretKey,
264    ) -> Result<Self, Error> {
265        use crate::classic::crypto_box::crypto_box_detached;
266
267        let mut dryocbox = Self {
268            ephemeral_pk: None,
269            tag: Mac::new_byte_array(),
270            data: Data::new_bytes(),
271        };
272
273        dryocbox.data.resize(message.as_slice().len(), 0);
274
275        crypto_box_detached(
276            dryocbox.data.as_mut_slice(),
277            dryocbox.tag.as_mut_array(),
278            message.as_slice(),
279            nonce.as_array(),
280            recipient_public_key.as_array(),
281            sender_secret_key.as_array(),
282        )?;
283
284        Ok(dryocbox)
285    }
286
287    /// Encrypts a message using `precalc_secret_key`, and returns a new
288    /// [`DryocBox`] with ciphertext and tag.
289    ///
290    /// # Errors
291    ///
292    /// Returns an error if the message is too long or the output storage does
293    /// not resize to the message length.
294    pub fn precalc_encrypt<
295        Message: Bytes + ?Sized,
296        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
297        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
298    >(
299        message: &Message,
300        nonce: &Nonce,
301        precalc_secret_key: &PrecalcSecretKey,
302    ) -> Result<Self, Error> {
303        use crate::classic::crypto_box::crypto_box_detached_afternm;
304
305        let mut dryocbox = Self {
306            ephemeral_pk: None,
307            tag: Mac::new_byte_array(),
308            data: Data::new_bytes(),
309        };
310
311        dryocbox.data.resize(message.as_slice().len(), 0);
312
313        crypto_box_detached_afternm(
314            dryocbox.data.as_mut_slice(),
315            dryocbox.tag.as_mut_array(),
316            message.as_slice(),
317            nonce.as_array(),
318            precalc_secret_key.as_array(),
319        )?;
320
321        Ok(dryocbox)
322    }
323}
324
325impl<
326    EphemeralPublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
327    Mac: NewByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
328    Data: NewBytes + ResizableBytes + Zeroize,
329> DryocBox<EphemeralPublicKey, Mac, Data>
330{
331    /// Encrypts a message for `recipient_public_key`, using an ephemeral secret
332    /// key and nonce. Returns a new [`DryocBox`] with ciphertext, tag, and
333    /// ephemeral public key.
334    ///
335    /// # Errors
336    ///
337    /// Returns an error if the message is too long, `recipient_public_key` is
338    /// an unacceptable low-order key, or the output storage does not resize to
339    /// the message length.
340    ///
341    /// # Panics
342    ///
343    /// Panics if the operating system's random number generator fails while
344    /// creating the ephemeral keypair.
345    pub fn seal<
346        Message: Bytes + ?Sized,
347        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
348    >(
349        message: &Message,
350        recipient_public_key: &RecipientPublicKey,
351    ) -> Result<Self, Error> {
352        use crate::classic::crypto_box::{
353            crypto_box_detached, crypto_box_keypair, crypto_box_seal_nonce,
354        };
355
356        let mut nonce = Nonce::new_byte_array();
357        let (epk, esk) = crypto_box_keypair();
358        let esk = Zeroizing::new(esk);
359        crypto_box_seal_nonce(nonce.as_mut_array(), &epk, recipient_public_key.as_array());
360
361        let mut pk = EphemeralPublicKey::new_byte_array();
362        pk.copy_from_slice(&epk);
363
364        let mut dryocbox = Self {
365            ephemeral_pk: Some(pk),
366            tag: Mac::new_byte_array(),
367            data: Data::new_bytes(),
368        };
369
370        dryocbox.data.resize(message.as_slice().len(), 0);
371
372        crypto_box_detached(
373            dryocbox.data.as_mut_slice(),
374            dryocbox.tag.as_mut_array(),
375            message.as_slice(),
376            nonce.as_array(),
377            recipient_public_key.as_array(),
378            &esk,
379        )?;
380
381        Ok(dryocbox)
382    }
383}
384
385impl<
386    'a,
387    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + core::convert::TryFrom<&'a [u8]> + Zeroize,
388    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + core::convert::TryFrom<&'a [u8]> + Zeroize,
389    Data: Bytes + From<&'a [u8]> + Zeroize,
390> DryocBox<EphemeralPublicKey, Mac, Data>
391{
392    /// Initializes a [`DryocBox`] from a slice. Expects the first
393    /// [`CRYPTO_BOX_MACBYTES`] bytes to contain the message authentication tag,
394    /// with the remaining bytes containing the encrypted message.
395    ///
396    /// # Errors
397    ///
398    /// Returns an error if `bytes` is shorter than one authentication tag or
399    /// the tag cannot be converted to `Mac`.
400    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
401        let (tag, data) = split_prefix(bytes, CRYPTO_BOX_MACBYTES, ErrorContext::Box)?;
402        Ok(Self {
403            ephemeral_pk: None,
404            tag: Mac::try_from(tag)
405                .map_err(|_| Error::invalid_encoding(ErrorContext::AuthenticationTag))?,
406            data: Data::from(data),
407        })
408    }
409
410    /// Initializes a sealed [`DryocBox`] from a slice. Expects the first
411    /// [`CRYPTO_BOX_PUBLICKEYBYTES`] bytes to contain the ephemeral public key,
412    /// the next [`CRYPTO_BOX_MACBYTES`] bytes to be the message authentication
413    /// tag, with the remaining bytes containing the encrypted message.
414    ///
415    /// # Errors
416    ///
417    /// Returns an error if `bytes` is shorter than one ephemeral public key
418    /// plus one authentication tag, or if either field cannot be converted to
419    /// its target type.
420    pub fn from_sealed_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
421        validate_length!(min CRYPTO_BOX_SEALBYTES, bytes.len(), crate::ErrorContext::SealedBox);
422
423        let (seal, data) = bytes.split_at(CRYPTO_BOX_SEALBYTES);
424        let (epk, tag) = seal.split_at(CRYPTO_BOX_PUBLICKEYBYTES);
425        Ok(Self {
426            ephemeral_pk: Some(
427                EphemeralPublicKey::try_from(epk)
428                    .map_err(|_| Error::invalid_key(crate::ErrorContext::EphemeralPublicKey))?,
429            ),
430            tag: Mac::try_from(tag)
431                .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
432            data: Data::from(data),
433        })
434    }
435}
436
437impl<
438    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
439    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
440    Data: Bytes + Zeroize,
441> DryocBox<EphemeralPublicKey, Mac, Data>
442{
443    /// Returns a new box from the (optional) `ephemeral_pk`, `tag`, and
444    /// `data`, consuming each. The order matches the wire format of
445    /// [`DryocBox::to_bytes`].
446    #[must_use]
447    pub fn from_parts(ephemeral_pk: Option<EphemeralPublicKey>, tag: Mac, data: Data) -> Self {
448        Self {
449            ephemeral_pk,
450            tag,
451            data,
452        }
453    }
454
455    /// Returns the ephemeral public key of a sealed box, or [`None`] for a
456    /// regular box.
457    pub fn ephemeral_pk(&self) -> Option<&EphemeralPublicKey> {
458        self.ephemeral_pk.as_ref()
459    }
460
461    /// Returns the authentication tag.
462    pub fn tag(&self) -> &Mac {
463        &self.tag
464    }
465
466    /// Returns the ciphertext.
467    pub fn data(&self) -> &Data {
468        &self.data
469    }
470
471    /// Copies `self` into a new [`Vec`]
472    #[cfg(feature = "alloc")]
473    #[must_use]
474    pub fn to_vec(&self) -> Vec<u8> {
475        self.to_bytes()
476    }
477
478    /// Moves the (optional) ephemeral public key, tag, and data out of this
479    /// instance, returning them as a tuple in wire order.
480    #[must_use]
481    pub fn into_parts(self) -> (Option<EphemeralPublicKey>, Mac, Data) {
482        (self.ephemeral_pk, self.tag, self.data)
483    }
484
485    /// Decrypts this box using `nonce`, `recipient_secret_key`, and
486    /// `sender_public_key`, returning the decrypted message upon success.
487    ///
488    /// # Errors
489    ///
490    /// Returns an error if the ciphertext is too long, `sender_public_key` is
491    /// an unacceptable low-order key, the output storage has the wrong length,
492    /// or authentication fails. Authentication fails for a wrong key, nonce,
493    /// tag, or ciphertext.
494    pub fn decrypt<
495        Output: ResizableBytes + NewBytes,
496        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
497        SenderPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
498        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
499    >(
500        &self,
501        nonce: &Nonce,
502        sender_public_key: &SenderPublicKey,
503        recipient_secret_key: &RecipientSecretKey,
504    ) -> Result<Output, Error> {
505        use crate::classic::crypto_box::*;
506
507        let mut message = Output::new_bytes();
508        message.resize(self.data.as_slice().len(), 0);
509
510        crypto_box_open_detached(
511            message.as_mut_slice(),
512            self.data.as_slice(),
513            self.tag.as_array(),
514            nonce.as_array(),
515            sender_public_key.as_array(),
516            recipient_secret_key.as_array(),
517        )?;
518
519        Ok(message)
520    }
521
522    /// Decrypts this box using `nonce` and `precalc_secret_key`, returning the
523    /// decrypted message upon success.
524    ///
525    /// # Errors
526    ///
527    /// Returns an error if the ciphertext is too long, the output storage has
528    /// the wrong length, or authentication fails because the precomputed key,
529    /// nonce, tag, or ciphertext does not match.
530    pub fn precalc_decrypt<
531        Output: ResizableBytes + NewBytes,
532        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
533        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
534    >(
535        &self,
536        nonce: &Nonce,
537        precalc_secret_key: &PrecalcSecretKey,
538    ) -> Result<Output, Error> {
539        use crate::classic::crypto_box::crypto_box_open_detached_afternm;
540
541        let mut message = Output::new_bytes();
542        message.resize(self.data.as_slice().len(), 0);
543
544        crypto_box_open_detached_afternm(
545            message.as_mut_slice(),
546            self.data.as_slice(),
547            self.tag.as_array(),
548            nonce.as_array(),
549            precalc_secret_key.as_array(),
550        )?;
551
552        Ok(message)
553    }
554
555    /// Decrypts this sealed box using `recipient_keypair`.
556    ///
557    /// # Errors
558    ///
559    /// Returns an error if the ciphertext is too long, the box has no
560    /// ephemeral public key, that key is an unacceptable low-order key, the
561    /// output storage has the wrong length, or authentication fails.
562    /// Authentication fails for the wrong recipient key pair or modified box
563    /// data.
564    pub fn open<
565        Output: ResizableBytes + NewBytes + Zeroize,
566        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
567        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
568    >(
569        &self,
570        recipient_keypair: &crate::keypair::KeyPair<RecipientPublicKey, RecipientSecretKey>,
571    ) -> Result<Output, Error> {
572        use crate::classic::crypto_box::*;
573
574        match &self.ephemeral_pk {
575            Some(epk) => {
576                let mut nonce = Nonce::new_byte_array();
577                crypto_box_seal_nonce(
578                    nonce.as_mut_array(),
579                    epk.as_array(),
580                    recipient_keypair.public_key.as_array(),
581                );
582
583                let mut message = Output::new_bytes();
584                message.resize(self.data.as_slice().len(), 0);
585
586                crypto_box_open_detached(
587                    message.as_mut_slice(),
588                    self.data.as_slice(),
589                    self.tag.as_array(),
590                    nonce.as_array(),
591                    epk.as_array(),
592                    recipient_keypair.secret_key.as_array(),
593                )?;
594
595                Ok(message)
596            }
597            None => Err(Error::missing_data(crate::ErrorContext::EphemeralPublicKey)),
598        }
599    }
600
601    /// Copies `self` into the target. Can be used with protected memory.
602    #[must_use]
603    pub fn to_bytes<Bytes: NewBytes + ResizableBytes>(&self) -> Bytes {
604        match &self.ephemeral_pk {
605            Some(epk) => {
606                let mut data = Bytes::new_bytes();
607                data.resize(CRYPTO_BOX_SEALBYTES + self.data.len(), 0);
608                let s = data.as_mut_slice();
609                s[..CRYPTO_BOX_PUBLICKEYBYTES].copy_from_slice(epk.as_array());
610                s[CRYPTO_BOX_PUBLICKEYBYTES..CRYPTO_BOX_SEALBYTES]
611                    .copy_from_slice(self.tag.as_array());
612                s[CRYPTO_BOX_SEALBYTES..].copy_from_slice(self.data.as_slice());
613                data
614            }
615            None => concat_bytes(self.tag.as_array(), self.data.as_slice()),
616        }
617    }
618}
619
620#[cfg(feature = "alloc")]
621impl DryocBox<PublicKey, Mac, Vec<u8>> {
622    /// Encrypts a message using `sender_secret_key` for `recipient_public_key`,
623    /// and returns a new [`DryocBox`] with ciphertext and tag.
624    ///
625    /// # Errors
626    ///
627    /// Returns an error if the message is too long or `recipient_public_key` is
628    /// an unacceptable low-order key.
629    pub fn encrypt_to_vecbox<
630        Message: Bytes + ?Sized,
631        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
632        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
633        SenderSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
634    >(
635        message: &Message,
636        nonce: &Nonce,
637        recipient_public_key: &RecipientPublicKey,
638        sender_secret_key: &SenderSecretKey,
639    ) -> Result<Self, Error> {
640        Self::encrypt(message, nonce, recipient_public_key, sender_secret_key)
641    }
642
643    /// Encrypts a message using `precalc_secret_key`, and returns a new
644    /// [`DryocBox`] with ciphertext and tag.
645    ///
646    /// # Errors
647    ///
648    /// Returns an error if the message is too long or the output storage cannot
649    /// hold the ciphertext.
650    pub fn precalc_encrypt_to_vecbox<
651        Message: Bytes + ?Sized,
652        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
653        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
654    >(
655        message: &Message,
656        nonce: &Nonce,
657        precalc_secret_key: &PrecalcSecretKey,
658    ) -> Result<Self, Error> {
659        Self::precalc_encrypt(message, nonce, precalc_secret_key)
660    }
661
662    /// Encrypts a message for `recipient_public_key`, using an ephemeral secret
663    /// key and nonce, and returns a new [`DryocBox`] with the ciphertext,
664    /// ephemeral public key, and tag.
665    ///
666    /// # Errors
667    ///
668    /// Returns an error if the message is too long or `recipient_public_key` is
669    /// an unacceptable low-order key.
670    ///
671    /// # Panics
672    ///
673    /// Panics if the operating system's random number generator fails while
674    /// creating the ephemeral keypair.
675    pub fn seal_to_vecbox<
676        Message: Bytes + ?Sized,
677        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
678    >(
679        message: &Message,
680        recipient_public_key: &RecipientPublicKey,
681    ) -> Result<Self, Error> {
682        Self::seal(message, recipient_public_key)
683    }
684
685    /// Decrypts this box using `nonce`, `recipient_secret_key` and
686    /// `sender_public_key`, returning the decrypted message upon success.
687    ///
688    /// # Errors
689    ///
690    /// Returns an error if the ciphertext is too long, `sender_public_key` is
691    /// an unacceptable low-order key, or authentication fails because a key,
692    /// nonce, tag, or ciphertext is wrong.
693    pub fn decrypt_to_vec<
694        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
695        SenderPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
696        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
697    >(
698        &self,
699        nonce: &Nonce,
700        sender_public_key: &SenderPublicKey,
701        recipient_secret_key: &RecipientSecretKey,
702    ) -> Result<Vec<u8>, Error> {
703        self.decrypt(nonce, sender_public_key, recipient_secret_key)
704    }
705
706    /// Decrypts this box using `nonce` and
707    /// `precalc_secret_key`, returning the decrypted message upon
708    /// success.
709    ///
710    /// # Errors
711    ///
712    /// Returns an error if the ciphertext is too long or authentication fails
713    /// because the precomputed key, nonce, tag, or ciphertext does not match.
714    pub fn precalc_decrypt_to_vec<
715        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
716        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
717    >(
718        &self,
719        nonce: &Nonce,
720        precalc_secret_key: &PrecalcSecretKey,
721    ) -> Result<Vec<u8>, Error> {
722        self.precalc_decrypt(nonce, precalc_secret_key)
723    }
724
725    /// Decrypts this sealed box using `recipient_keypair`.
726    ///
727    /// # Errors
728    ///
729    /// Returns an error if the ciphertext is too long, the box has no
730    /// ephemeral public key, that key is an unacceptable low-order key, or
731    /// authentication fails because the recipient key pair or box data is
732    /// wrong.
733    pub fn open_to_vec<
734        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
735        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
736    >(
737        &self,
738        recipient_keypair: &crate::keypair::KeyPair<RecipientPublicKey, RecipientSecretKey>,
739    ) -> Result<Vec<u8>, Error> {
740        self.open(recipient_keypair)
741    }
742}
743
744impl<
745    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
746    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
747    Data: Bytes + Zeroize,
748> PartialEq<DryocBox<EphemeralPublicKey, Mac, Data>> for DryocBox<EphemeralPublicKey, Mac, Data>
749{
750    fn eq(&self, other: &Self) -> bool {
751        if let Some(our_epk) = &self.ephemeral_pk {
752            if let Some(their_epk) = &other.ephemeral_pk {
753                ct_eq_bytes(self.tag.as_slice(), other.tag.as_slice())
754                    && ct_eq_bytes(self.data.as_slice(), other.data.as_slice())
755                    && ct_eq_bytes(our_epk.as_slice(), their_epk.as_slice())
756            } else {
757                false
758            }
759        } else if other.ephemeral_pk.is_none() {
760            ct_eq_bytes(self.tag.as_slice(), other.tag.as_slice())
761                && ct_eq_bytes(self.data.as_slice(), other.data.as_slice())
762        } else {
763            false
764        }
765    }
766}
767
768#[cfg(all(test, feature = "alloc"))]
769mod tests {
770    use super::*;
771    use crate::constants::CRYPTO_BOX_SEEDBYTES;
772    use crate::precalc::PrecalcSecretKey;
773    use crate::test_prelude::*;
774
775    #[test]
776    fn open_requires_an_ephemeral_public_key() {
777        let box_without_ephemeral_key =
778            VecBox::from_bytes(&[0u8; CRYPTO_BOX_MACBYTES]).expect("a regular box should parse");
779        let recipient_keypair = StackKeyPair::generate();
780
781        let error = box_without_ephemeral_key
782            .open::<Vec<u8>, _, _>(&recipient_keypair)
783            .expect_err("a regular box cannot be opened as a sealed box");
784        assert!(matches!(
785            error,
786            Error::MissingData {
787                context: crate::ErrorContext::EphemeralPublicKey,
788            }
789        ));
790    }
791
792    /// NaCl `tests/box.c` vector (also RFC 7748 section 6.1 keys): Alice's
793    /// and Bob's X25519 keys, the nonce, the 131-byte message, and the
794    /// 147-byte `tag || ciphertext` output. `beforenm(bobpk, alicesk)` is the
795    /// `firstkey` used by NaCl's secretbox vector.
796    const ALICE_SK: &str = "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a";
797    const ALICE_PK: &str = "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a";
798    const BOB_SK: &str = "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb";
799    const BOB_PK: &str = "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f";
800    const SHARED_KEY: &str = "1b27556473e985d462cd51197a9a46c76009549eac6474f206c4ee0844f68389";
801    const NONCE: &str = "69696ee955b62b73cd62bda875fc73d68219e0036b7a0b37";
802    const MESSAGE: &str = concat!(
803        "be075fc53c81f2d5cf141316ebeb0c7b5228c52a4c62cbd44b66849b64244ffce5ecbaaf33bd751a1ac728d4",
804        "5e6c61296cdc3c01233561f41db66cce314adb310e3be8250c46f06dceea3a7fa1348057e2f6556ad6b1318a",
805        "024a838f21af1fde048977eb48f59ffd4924ca1c60902e52f0a089bc76897040e082f937763848645e0705",
806    );
807    const BOXED: &str = concat!(
808        "f3ffc7703f9400e52a7dfb4b3d3305d98e993b9f48681273c29650ba32fc76ce48332ea7164d96a4476fb8c5",
809        "31a1186ac0dfc17c98dce87b4da7f011ec48c97271d2c20f9b928fe2270d6fb863d51738b48eeee314a7cc8a",
810        "b932164548e526ae90224368517acfeabd6bb3732bc0e9da99832b61ca01b6de56244a9e88d5f9b37973f622",
811        "a43d14a6599b1f654cb45a74e355a5",
812    );
813
814    fn array<const N: usize>(hex: &str) -> StackByteArray<N> {
815        StackByteArray::try_from(hex::decode(hex).expect("hex").as_slice()).expect("length")
816    }
817
818    struct NaclVector {
819        alice: StackKeyPair,
820        bob: StackKeyPair,
821        nonce: Nonce,
822        message: Vec<u8>,
823        boxed: Vec<u8>,
824    }
825
826    fn nacl_vector() -> NaclVector {
827        NaclVector {
828            alice: StackKeyPair::from_slices(
829                &hex::decode(ALICE_PK).expect("hex"),
830                &hex::decode(ALICE_SK).expect("hex"),
831            )
832            .expect("alice keypair"),
833            bob: StackKeyPair::from_slices(
834                &hex::decode(BOB_PK).expect("hex"),
835                &hex::decode(BOB_SK).expect("hex"),
836            )
837            .expect("bob keypair"),
838            nonce: array(NONCE),
839            message: hex::decode(MESSAGE).expect("hex"),
840            boxed: hex::decode(BOXED).expect("hex"),
841        }
842    }
843
844    #[test]
845    fn nacl_vector_encrypts_to_known_bytes_and_decrypts_with_swapped_keys() {
846        let v = nacl_vector();
847        assert_eq!(
848            StackKeyPair::from_secret_key(v.alice.secret_key.clone()).public_key,
849            v.alice.public_key
850        );
851        assert_eq!(
852            StackKeyPair::from_secret_key(v.bob.secret_key.clone()).public_key,
853            v.bob.public_key
854        );
855
856        let dryocbox = DryocBox::encrypt_to_vecbox(
857            &v.message,
858            &v.nonce,
859            &v.bob.public_key,
860            &v.alice.secret_key,
861        )
862        .expect("encrypt failed");
863        assert_eq!(dryocbox.to_vec(), v.boxed);
864        assert_eq!(dryocbox.tag.as_slice(), &v.boxed[..CRYPTO_BOX_MACBYTES]);
865        assert_eq!(dryocbox.data, v.boxed[CRYPTO_BOX_MACBYTES..]);
866        assert!(dryocbox.ephemeral_pk.is_none());
867
868        let parsed = VecBox::from_bytes(&v.boxed).expect("known-good box should parse");
869        assert_eq!(parsed, dryocbox);
870        assert_eq!(
871            parsed
872                .decrypt_to_vec(&v.nonce, &v.alice.public_key, &v.bob.secret_key)
873                .expect("decrypt failed"),
874            v.message
875        );
876
877        // The DH shared key is symmetric, so Bob-to-Alice under the same nonce
878        // is the identical box; this is why both directions share one nonce
879        // space.
880        let reverse = DryocBox::encrypt_to_vecbox(
881            &v.message,
882            &v.nonce,
883            &v.alice.public_key,
884            &v.bob.secret_key,
885        )
886        .expect("encrypt failed");
887        assert_eq!(reverse.to_vec(), v.boxed);
888    }
889
890    #[test]
891    fn nacl_vector_precalculated_key_and_ciphertext_match() {
892        let v = nacl_vector();
893        let expected_key: StackByteArray<CRYPTO_BOX_BEFORENMBYTES> = array(SHARED_KEY);
894
895        let alice_side = PrecalcSecretKey::precalculate(&v.bob.public_key, &v.alice.secret_key)
896            .expect("precalculation failed");
897        let bob_side = v
898            .bob
899            .precalculate(&v.alice.public_key)
900            .expect("precalculation failed");
901        assert_eq!(alice_side.as_array(), expected_key.as_array());
902        assert_eq!(alice_side, bob_side);
903
904        let dryocbox = DryocBox::precalc_encrypt_to_vecbox(&v.message, &v.nonce, &alice_side)
905            .expect("encrypt failed");
906        assert_eq!(dryocbox.to_vec(), v.boxed);
907
908        let parsed = VecBox::from_bytes(&v.boxed).expect("parse");
909        assert_eq!(
910            parsed
911                .precalc_decrypt_to_vec(&v.nonce, &bob_side)
912                .expect("decrypt failed"),
913            v.message
914        );
915        // Precalculated and direct decryption interoperate.
916        assert_eq!(
917            parsed
918                .decrypt_to_vec(&v.nonce, &v.alice.public_key, &v.bob.secret_key)
919                .expect("decrypt failed"),
920            v.message
921        );
922    }
923
924    #[test]
925    fn tampering_and_wrong_keys_are_rejected_and_the_box_stays_usable() {
926        let v = nacl_vector();
927        let dryocbox = VecBox::from_bytes(&v.boxed).expect("parse");
928        let precalc = v.bob.precalculate(&v.alice.public_key).expect("precalc");
929        let stranger = StackKeyPair::from_seed(&[9u8; CRYPTO_BOX_SEEDBYTES]);
930
931        // Wrong sender: authentication binds the sender's public key.
932        assert!(matches!(
933            dryocbox.decrypt_to_vec(&v.nonce, &stranger.public_key, &v.bob.secret_key),
934            Err(Error::AuthenticationFailed)
935        ));
936        // Wrong recipient.
937        assert!(matches!(
938            dryocbox.decrypt_to_vec(&v.nonce, &v.alice.public_key, &stranger.secret_key),
939            Err(Error::AuthenticationFailed)
940        ));
941        // Low-order sender key is rejected before authentication.
942        assert!(
943            dryocbox
944                .decrypt_to_vec(&v.nonce, &PublicKey::default(), &v.bob.secret_key)
945                .is_err()
946        );
947
948        let mut wrong_nonce = v.nonce.clone();
949        wrong_nonce[0] ^= 1;
950        assert!(matches!(
951            dryocbox.decrypt_to_vec(&wrong_nonce, &v.alice.public_key, &v.bob.secret_key),
952            Err(Error::AuthenticationFailed)
953        ));
954        assert!(matches!(
955            dryocbox.precalc_decrypt_to_vec(&wrong_nonce, &precalc),
956            Err(Error::AuthenticationFailed)
957        ));
958
959        for index in [
960            0,
961            CRYPTO_BOX_MACBYTES - 1,
962            CRYPTO_BOX_MACBYTES,
963            v.boxed.len() - 1,
964        ] {
965            let mut tampered = v.boxed.clone();
966            tampered[index] ^= 0x80;
967            let tampered = VecBox::from_bytes(&tampered).expect("parse");
968            assert!(matches!(
969                tampered.decrypt_to_vec(&v.nonce, &v.alice.public_key, &v.bob.secret_key),
970                Err(Error::AuthenticationFailed)
971            ));
972            assert!(matches!(
973                tampered.precalc_decrypt_to_vec(&v.nonce, &precalc),
974                Err(Error::AuthenticationFailed)
975            ));
976        }
977
978        let mut wrong_precalc = precalc.clone();
979        wrong_precalc.as_mut_array()[0] ^= 1;
980        assert!(matches!(
981            dryocbox.precalc_decrypt_to_vec(&v.nonce, &wrong_precalc),
982            Err(Error::AuthenticationFailed)
983        ));
984
985        // Rejections leave the box untouched and decryptable.
986        assert_eq!(dryocbox.to_vec(), v.boxed);
987        assert_eq!(
988            dryocbox
989                .decrypt_to_vec(&v.nonce, &v.alice.public_key, &v.bob.secret_key)
990                .expect("decrypt"),
991            v.message
992        );
993    }
994
995    #[test]
996    fn encrypt_rejects_low_order_recipient_key() {
997        let v = nacl_vector();
998        let mut identity = PublicKey::default();
999        identity[0] = 1;
1000        for low_order in [PublicKey::default(), identity] {
1001            assert!(
1002                DryocBox::encrypt_to_vecbox(&v.message, &v.nonce, &low_order, &v.alice.secret_key)
1003                    .is_err()
1004            );
1005            assert!(PrecalcSecretKey::precalculate(&low_order, &v.alice.secret_key).is_err());
1006        }
1007    }
1008
1009    #[test]
1010    fn from_bytes_and_from_sealed_bytes_require_their_prefixes() {
1011        for len in 0..CRYPTO_BOX_MACBYTES {
1012            assert!(matches!(
1013                VecBox::from_bytes(&vec![0u8; len]),
1014                Err(Error::InvalidLength {
1015                    context: crate::ErrorContext::Box,
1016                    actual,
1017                    ..
1018                }) if actual == len
1019            ));
1020        }
1021        for len in [0, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SEALBYTES - 1] {
1022            assert!(matches!(
1023                VecBox::from_sealed_bytes(&vec![0u8; len]),
1024                Err(Error::InvalidLength {
1025                    context: crate::ErrorContext::SealedBox,
1026                    actual,
1027                    ..
1028                }) if actual == len
1029            ));
1030        }
1031
1032        let empty_sealed =
1033            VecBox::from_sealed_bytes(&[0x5au8; CRYPTO_BOX_SEALBYTES]).expect("empty sealed box");
1034        assert!(empty_sealed.data.is_empty());
1035        assert_eq!(
1036            empty_sealed.ephemeral_pk.as_ref().map(|epk| epk.as_slice()),
1037            Some(&[0x5au8; CRYPTO_BOX_PUBLICKEYBYTES][..])
1038        );
1039    }
1040
1041    #[test]
1042    fn sealed_wire_format_is_ephemeral_key_then_tag_then_ciphertext() {
1043        let epk: PublicKey = array(ALICE_PK);
1044        let tag: Mac = array(&NONCE[..CRYPTO_BOX_MACBYTES * 2]);
1045        let data = hex::decode(MESSAGE).expect("hex");
1046
1047        let mut expected = epk.to_vec();
1048        expected.extend_from_slice(tag.as_slice());
1049        expected.extend_from_slice(&data);
1050
1051        let sealed = VecBox::from_parts(Some(epk.clone()), tag.clone(), data.clone());
1052        assert_eq!(sealed.to_vec(), expected);
1053        let reparsed = VecBox::from_sealed_bytes(&expected).expect("parse");
1054        assert_eq!(reparsed, sealed);
1055        let (parsed_epk, parsed_tag, parsed_data) = reparsed.into_parts();
1056        assert_eq!(parsed_tag, tag);
1057        assert_eq!(parsed_data, data);
1058        assert_eq!(parsed_epk.as_ref(), Some(&epk));
1059
1060        // Without an ephemeral key the same tag and data serialize as a regular
1061        // box.
1062        let regular = VecBox::from_parts(None, tag, data);
1063        assert_eq!(regular.to_vec(), &expected[CRYPTO_BOX_PUBLICKEYBYTES..]);
1064    }
1065
1066    #[test]
1067    fn sealed_box_authenticates_recipient_and_contents() {
1068        let v = nacl_vector();
1069        let sealed = DryocBox::seal_to_vecbox(&v.message, &v.bob.public_key).expect("seal");
1070        assert_eq!(sealed.open_to_vec(&v.bob).expect("open"), v.message);
1071
1072        let wrong_recipient = sealed.open_to_vec(&v.alice);
1073        assert!(matches!(wrong_recipient, Err(Error::AuthenticationFailed)));
1074
1075        let bytes = sealed.to_vec();
1076        for index in [
1077            0,
1078            CRYPTO_BOX_PUBLICKEYBYTES,
1079            CRYPTO_BOX_SEALBYTES,
1080            bytes.len() - 1,
1081        ] {
1082            let mut tampered = bytes.clone();
1083            tampered[index] ^= 0x80;
1084            let tampered = VecBox::from_sealed_bytes(&tampered).expect("parse");
1085            assert!(tampered.open_to_vec(&v.bob).is_err());
1086        }
1087        assert_eq!(
1088            VecBox::from_sealed_bytes(&bytes)
1089                .expect("parse")
1090                .open_to_vec(&v.bob)
1091                .expect("open"),
1092            v.message
1093        );
1094    }
1095
1096    #[test]
1097    fn test_precalc_encrypt_decrypt() {
1098        let keypair_sender = StackKeyPair::generate();
1099        let keypair_recipient = StackKeyPair::generate();
1100        let nonce = Nonce::generate();
1101
1102        let message = b"To be, or not to be, that is the question:";
1103        let precalc_secret_key = PrecalcSecretKey::precalculate(
1104            &keypair_recipient.public_key,
1105            &keypair_sender.secret_key,
1106        )
1107        .expect("precalculation failed");
1108
1109        let dryocbox: VecBox = DryocBox::precalc_encrypt(message, &nonce, &precalc_secret_key)
1110            .expect("unable to encrypt");
1111
1112        let decrypted: Vec<u8> = dryocbox
1113            .precalc_decrypt(&nonce, &precalc_secret_key)
1114            .expect("unable to decrypt");
1115
1116        assert_eq!(message, decrypted.as_slice());
1117    }
1118
1119    #[test]
1120    fn test_precalc_encrypt_to_vecbox_decrypt_to_vecbox() {
1121        let keypair_sender = StackKeyPair::generate();
1122        let keypair_recipient = StackKeyPair::generate();
1123        let nonce = Nonce::generate();
1124
1125        let message = b"All the world's a stage, and all the men and women merely players:";
1126        let precalc_secret_key = PrecalcSecretKey::precalculate(
1127            &keypair_recipient.public_key,
1128            &keypair_sender.secret_key,
1129        )
1130        .expect("precalculation failed");
1131
1132        let dryocbox = DryocBox::precalc_encrypt_to_vecbox(message, &nonce, &precalc_secret_key)
1133            .expect("unable to encrypt");
1134
1135        let decrypted = dryocbox
1136            .precalc_decrypt_to_vec(&nonce, &precalc_secret_key)
1137            .expect("unable to decrypt");
1138
1139        assert_eq!(message, decrypted.as_slice());
1140    }
1141
1142    #[test]
1143    fn test_precalc_encrypt_decrypt_with_different_messages() {
1144        let keypair_sender = StackKeyPair::generate();
1145        let keypair_recipient = StackKeyPair::generate();
1146        let nonce = Nonce::generate();
1147
1148        let messages: Vec<&[u8]> = vec![
1149            b"Now is the winter of our discontent, made glorious summer by this sun of York;",
1150            b"Friends, Romans, countrymen, lend me your ears; I come to bury Caesar, not to praise him.",
1151            b"A horse! a horse! my kingdom for a horse!",
1152            b"Good night, good night! parting is such sweet sorrow, that I shall say good night till it be morrow.",
1153        ];
1154
1155        let precalc_secret_key = PrecalcSecretKey::precalculate(
1156            &keypair_recipient.public_key,
1157            &keypair_sender.secret_key,
1158        )
1159        .expect("precalculation failed");
1160
1161        for message in &messages {
1162            let dryocbox: VecBox = DryocBox::precalc_encrypt(message, &nonce, &precalc_secret_key)
1163                .expect("unable to encrypt");
1164
1165            let decrypted: Vec<u8> = dryocbox
1166                .precalc_decrypt(&nonce, &precalc_secret_key)
1167                .expect("unable to decrypt");
1168
1169            assert_eq!(*message, decrypted.as_slice());
1170        }
1171    }
1172
1173    #[test]
1174    fn test_precalc_encrypt_to_vecbox_decrypt_to_vecbox_with_different_messages() {
1175        let keypair_sender = StackKeyPair::generate();
1176        let keypair_recipient = StackKeyPair::generate();
1177        let nonce = Nonce::generate();
1178
1179        let messages: Vec<&[u8]> = vec![
1180            b"Out, out brief candle! Life's but a walking shadow, a poor player that struts and frets his hour upon the stage and then is heard no more.",
1181            b"Some are born great, some achieve greatness, and some have greatness thrust upon them.",
1182            b"The lady doth protest too much, methinks.",
1183            b"What's in a name? That which we call a rose by any other name would smell as sweet.",
1184        ];
1185
1186        let precalc_secret_key = PrecalcSecretKey::precalculate(
1187            &keypair_recipient.public_key,
1188            &keypair_sender.secret_key,
1189        )
1190        .expect("precalculation failed");
1191
1192        for message in &messages {
1193            let dryocbox =
1194                DryocBox::precalc_encrypt_to_vecbox(message, &nonce, &precalc_secret_key)
1195                    .expect("unable to encrypt");
1196
1197            let decrypted = dryocbox
1198                .precalc_decrypt_to_vec(&nonce, &precalc_secret_key)
1199                .expect("unable to decrypt");
1200
1201            assert_eq!(*message, decrypted.as_slice());
1202        }
1203    }
1204
1205    #[cfg(dryoc_native_tests)]
1206    mod native_tests {
1207        use super::*;
1208        use crate::native_test_util as sodium;
1209
1210        #[test]
1211        fn nacl_vector_matches_libsodium_box_and_beforenm() {
1212            let v = nacl_vector();
1213            let so_boxed =
1214                sodium::box_easy(&v.message, &v.nonce, &v.bob.public_key, &v.alice.secret_key);
1215            assert_eq!(so_boxed, v.boxed);
1216
1217            let precalc = v.alice.precalculate(&v.bob.public_key).expect("precalc");
1218            let so_precalc = sodium::box_beforenm(&v.bob.public_key, &v.alice.secret_key)
1219                .expect("libsodium beforenm");
1220            assert_eq!(precalc.as_slice(), so_precalc.as_slice());
1221        }
1222
1223        #[test]
1224        fn libsodium_regular_and_precomputed_boxes_decrypt_with_rustaceous() {
1225            let v = nacl_vector();
1226            let so_precalc = sodium::box_beforenm(&v.bob.public_key, &v.alice.secret_key)
1227                .expect("libsodium beforenm");
1228            let precalc = v.bob.precalculate(&v.alice.public_key).expect("precalc");
1229
1230            for len in [0, 1, 15, 16, 17, 63, 64, 65, v.message.len()] {
1231                let plaintext = &v.message[..len];
1232
1233                let so_boxed =
1234                    sodium::box_easy(plaintext, &v.nonce, &v.bob.public_key, &v.alice.secret_key);
1235                let dryocbox = VecBox::from_bytes(&so_boxed).expect("sodium box should parse");
1236                assert_eq!(
1237                    dryocbox
1238                        .decrypt_to_vec(&v.nonce, &v.alice.public_key, &v.bob.secret_key)
1239                        .expect("decrypt failed"),
1240                    plaintext
1241                );
1242                assert_eq!(
1243                    dryocbox
1244                        .precalc_decrypt_to_vec(&v.nonce, &precalc)
1245                        .expect("precalc decrypt failed"),
1246                    plaintext
1247                );
1248
1249                let so_afternm = sodium::box_easy_afternm(plaintext, &v.nonce, &so_precalc);
1250                assert_eq!(so_afternm, so_boxed);
1251                let dryocbox =
1252                    VecBox::from_bytes(&so_afternm).expect("sodium afternm box should parse");
1253                assert_eq!(
1254                    dryocbox
1255                        .precalc_decrypt_to_vec(&v.nonce, &precalc)
1256                        .expect("precalc decrypt failed"),
1257                    plaintext
1258                );
1259
1260                let precalc_box =
1261                    DryocBox::precalc_encrypt_to_vecbox(plaintext, &v.nonce, &precalc)
1262                        .expect("precalc encrypt failed");
1263                assert_eq!(
1264                    sodium::box_open_easy_afternm(&precalc_box.to_vec(), &v.nonce, &so_precalc)
1265                        .expect("sodium open_precomputed failed"),
1266                    plaintext
1267                );
1268                assert_eq!(
1269                    sodium::box_open_easy(
1270                        &precalc_box.to_vec(),
1271                        &v.nonce,
1272                        &v.alice.public_key,
1273                        &v.bob.secret_key
1274                    )
1275                    .expect("sodium open failed"),
1276                    plaintext
1277                );
1278            }
1279        }
1280
1281        #[test]
1282        fn libsodium_sealed_box_rejects_wrong_recipient_and_modification() {
1283            let v = nacl_vector();
1284            let ciphertext = sodium::box_seal(&v.message, &v.bob.public_key);
1285            let sealed = VecBox::from_sealed_bytes(&ciphertext).expect("parse");
1286            assert_eq!(sealed.open_to_vec(&v.bob).expect("open"), v.message);
1287            assert!(matches!(
1288                sealed.open_to_vec(&v.alice),
1289                Err(Error::AuthenticationFailed)
1290            ));
1291
1292            for index in [0, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SEALBYTES] {
1293                let mut tampered = ciphertext.clone();
1294                tampered[index] ^= 0x80;
1295                assert!(
1296                    VecBox::from_sealed_bytes(&tampered)
1297                        .expect("parse")
1298                        .open_to_vec(&v.bob)
1299                        .is_err()
1300                );
1301            }
1302        }
1303
1304        #[test]
1305        fn test_dryocbox_vecbox() {
1306            for i in 0..20 {
1307                use base64::Engine as _;
1308                use base64::engine::general_purpose;
1309
1310                let keypair_sender = StackKeyPair::generate();
1311                let keypair_recipient = StackKeyPair::generate();
1312                let keypair_sender_copy = keypair_sender.clone();
1313                let keypair_recipient_copy = keypair_recipient.clone();
1314                let nonce = Nonce::generate();
1315                let words = vec!["hello1".to_string(); i];
1316                let message = words.join(" :D ");
1317                let message_copy = message.clone();
1318                let dryocbox = DryocBox::encrypt_to_vecbox(
1319                    message.as_bytes(),
1320                    &nonce,
1321                    &keypair_recipient.public_key,
1322                    &keypair_sender.secret_key,
1323                )
1324                .unwrap();
1325
1326                let ciphertext = dryocbox.to_vec();
1327
1328                let so_ciphertext = sodium::box_easy(
1329                    message_copy.as_bytes(),
1330                    &nonce,
1331                    &keypair_recipient_copy.public_key,
1332                    &keypair_sender_copy.secret_key,
1333                );
1334
1335                assert_eq!(
1336                    general_purpose::STANDARD.encode(&ciphertext),
1337                    general_purpose::STANDARD.encode(&so_ciphertext)
1338                );
1339
1340                let keypair_sender = keypair_sender_copy.clone();
1341                let keypair_recipient = keypair_recipient_copy.clone();
1342
1343                let m = dryocbox
1344                    .decrypt_to_vec(
1345                        &nonce,
1346                        &keypair_sender.public_key,
1347                        &keypair_recipient.secret_key,
1348                    )
1349                    .expect("hmm");
1350                let so_m = sodium::box_open_easy(
1351                    &ciphertext,
1352                    &nonce,
1353                    &keypair_recipient_copy.public_key,
1354                    &keypair_sender_copy.secret_key,
1355                )
1356                .expect("HMMM");
1357
1358                assert_eq!(m, message_copy.as_bytes());
1359                assert_eq!(m, so_m);
1360            }
1361        }
1362
1363        #[test]
1364        fn test_decrypt_failure() {
1365            for i in 0..20 {
1366                use base64::Engine as _;
1367                use base64::engine::general_purpose;
1368
1369                let keypair_sender = StackKeyPair::generate();
1370                let keypair_recipient = StackKeyPair::generate();
1371                let keypair_sender_copy = keypair_sender.clone();
1372                let keypair_recipient_copy = keypair_recipient.clone();
1373                let nonce = Nonce::generate();
1374                let words = vec!["hello1".to_string(); i];
1375                let message = words.join(" :D ");
1376                let message_copy = message.clone();
1377                let dryocbox = DryocBox::encrypt_to_vecbox(
1378                    message.as_bytes(),
1379                    &nonce,
1380                    &keypair_recipient.public_key,
1381                    &keypair_sender.secret_key,
1382                )
1383                .unwrap();
1384
1385                let ciphertext = dryocbox.to_vec();
1386
1387                let so_ciphertext = sodium::box_easy(
1388                    message_copy.as_bytes(),
1389                    &nonce,
1390                    &keypair_recipient_copy.public_key,
1391                    &keypair_sender_copy.secret_key,
1392                );
1393
1394                assert_eq!(
1395                    general_purpose::STANDARD.encode(&ciphertext),
1396                    general_purpose::STANDARD.encode(&so_ciphertext)
1397                );
1398
1399                let invalid_key = StackKeyPair::generate();
1400                let invalid_key_copy_1 = invalid_key.clone();
1401                let invalid_key_copy_2 = invalid_key.clone();
1402
1403                DryocBox::decrypt::<Vec<u8>, Nonce, PublicKey, SecretKey>(
1404                    &dryocbox,
1405                    &nonce,
1406                    &invalid_key_copy_1.public_key,
1407                    &invalid_key_copy_2.secret_key,
1408                )
1409                .expect_err("hmm");
1410                sodium::box_open_easy(
1411                    &ciphertext,
1412                    &nonce,
1413                    &invalid_key.public_key,
1414                    &invalid_key.secret_key,
1415                )
1416                .expect_err("HMMM");
1417            }
1418        }
1419
1420        #[test]
1421        fn test_dryocbox_seal_vecbox() {
1422            for i in 0..20 {
1423                let keypair_recipient = StackKeyPair::generate();
1424                let words = vec!["hello1".to_string(); i];
1425                let message = words.join(" :D ");
1426                let message_copy = message.clone();
1427                let dryocbox =
1428                    DryocBox::seal_to_vecbox(message.as_bytes(), &keypair_recipient.public_key)
1429                        .unwrap();
1430
1431                let ciphertext = dryocbox.to_vec();
1432
1433                let m = dryocbox.open_to_vec(&keypair_recipient).expect("hmm");
1434                let so_m = sodium::box_seal_open(
1435                    ciphertext.as_slice(),
1436                    keypair_recipient.public_key.as_slice(),
1437                    keypair_recipient.secret_key.as_slice(),
1438                )
1439                .unwrap();
1440
1441                assert_eq!(m, message_copy.as_bytes());
1442                assert_eq!(m, so_m);
1443            }
1444        }
1445
1446        #[test]
1447        fn test_dryocbox_open_vecbox() {
1448            for i in 0..20 {
1449                let keypair_recipient = StackKeyPair::generate();
1450                let words = vec!["hello1".to_string(); i];
1451                let message = words.join(" :D ");
1452
1453                let ciphertext =
1454                    sodium::box_seal(message.as_bytes(), keypair_recipient.public_key.as_slice());
1455
1456                let dryocbox =
1457                    DryocBox::from_sealed_bytes(&ciphertext).expect("from sealed bytes failed");
1458
1459                let m = dryocbox.open_to_vec(&keypair_recipient).expect("hmm");
1460
1461                assert_eq!(m, message.as_bytes());
1462            }
1463        }
1464    }
1465}