Skip to main content

dryoc/
dryocbox.rs

1//! # Public-key authenticated encryption
2//!
3//! [`DryocBox`] implements libsodium's public-key authenticated encryption,
4//! also known as a _box_. This implementation uses X25519 for key derivation,
5//! the XSalsa20 stream cipher, and Poly1305 for message authentication.
6//!
7//! You should use a [`DryocBox`] when you want to:
8//!
9//! * exchange messages between two parties
10//! * authenticate the messages with public keys, rather than a pre-shared
11//!   secret
12//! * avoid secret sharing between parties
13//!
14//! [`DryocBox::encrypt`] authenticates the sender, so the sender and recipient
15//! public keys must be known ahead of time. [`DryocBox::seal`] instead sends an
16//! anonymous sealed box: it generates a one-time ephemeral keypair and stores
17//! the ephemeral public key with the ciphertext. Sealed boxes authenticate the
18//! ciphertext for the recipient, but not the sender's identity.
19//!
20//! Box nonces are public, but a nonce must never repeat for the same pair of
21//! keypairs. The two parties share one nonce space across both communication
22//! directions unless they use direction-specific keys. Callers of
23//! [`DryocBox::encrypt`] must coordinate this uniqueness. [`DryocBox::seal`]
24//! derives its nonce from a newly generated ephemeral public key and the
25//! recipient public key.
26//!
27//! With the `serde` feature,
28//! [`serde::Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) and
29//! [`serde::Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) are implemented
30//! for [`DryocBox`]. With `wincode`,
31//! [`wincode::SchemaRead`](https://docs.rs/wincode/latest/wincode/trait.SchemaRead.html) and
32//! [`wincode::SchemaWrite`](https://docs.rs/wincode/latest/wincode/trait.SchemaWrite.html) are
33//! implemented.
34//!
35//! ## Rustaceous API example
36//!
37//! ```
38//! use dryoc::dryocbox::*;
39//!
40//! // Randomly generate sender/recipient keypairs. Under normal circumstances, the
41//! // sender would only know the recipient's public key, and the recipient would
42//! // only know the sender's public key.
43//! let sender_keypair = KeyPair::generate();
44//! let recipient_keypair = KeyPair::generate();
45//!
46//! // Randomly generate a nonce
47//! let nonce = Nonce::generate();
48//!
49//! let message = b"All that glitters is not gold";
50//!
51//! // Encrypt the message into a Vec<u8>-based box.
52//! let dryocbox = DryocBox::encrypt_to_vecbox(
53//!     message,
54//!     &nonce,
55//!     &recipient_keypair.public_key,
56//!     &sender_keypair.secret_key,
57//! )
58//! .expect("unable to encrypt");
59//!
60//! // Convert into a libsodium compatible box as a Vec<u8>
61//! let sodium_box = dryocbox.to_vec();
62//!
63//! // Load the libsodium box into a DryocBox
64//! let dryocbox = DryocBox::from_bytes(&sodium_box).expect("failed to read box");
65//!
66//! // Decrypt the same box back to the original message, with the sender/recipient
67//! // keypairs flipped.
68//! let decrypted = dryocbox
69//!     .decrypt_to_vec(
70//!         &nonce,
71//!         &sender_keypair.public_key,
72//!         &recipient_keypair.secret_key,
73//!     )
74//!     .expect("unable to decrypt");
75//!
76//! assert_eq!(message, decrypted.as_slice());
77//! ```
78//!
79//! ## Sealed box example
80//!
81//! ```
82//! use dryoc::dryocbox::*;
83//!
84//! let recipient_keypair = KeyPair::generate();
85//! let message = b"Now is the winter of our discontent.";
86//!
87//! let dryocbox = DryocBox::seal_to_vecbox(message, &recipient_keypair.public_key.clone())
88//!     .expect("unable to seal");
89//!
90//! let decrypted = dryocbox
91//!     .unseal_to_vec(&recipient_keypair)
92//!     .expect("unable to unseal");
93//!
94//! assert_eq!(message, decrypted.as_slice());
95//! ```
96//!
97//! ## Additional resources
98//!
99//! * See <https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption>
100//!   for additional details on crypto boxes
101//! * For secret-key based encryption, see
102//!   [`DryocSecretBox`](crate::dryocsecretbox)
103//! * For stream encryption, see [`DryocStream`](crate::dryocstream)
104//! * See the [protected] mod for an example using the protected memory features
105//!   with [`DryocBox`]
106
107#[cfg(feature = "serde")]
108use serde::{Deserialize, Serialize};
109use subtle::ConstantTimeEq;
110use zeroize::{Zeroize, Zeroizing};
111
112use crate::constants::{
113    CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_MACBYTES, CRYPTO_BOX_NONCEBYTES,
114    CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SEALBYTES, CRYPTO_BOX_SECRETKEYBYTES,
115};
116use crate::error::*;
117pub use crate::types::*;
118
119/// Stack-allocated public key for authenticated public-key boxes.
120pub type PublicKey = StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
121/// Stack-allocated secret key for authenticated public-key boxes.
122pub type SecretKey = StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
123/// Stack-allocated nonce for authenticated public-key boxes.
124pub type Nonce = StackByteArray<CRYPTO_BOX_NONCEBYTES>;
125/// Stack-allocated message authentication code for authenticated public-key
126/// boxes.
127pub type Mac = StackByteArray<CRYPTO_BOX_MACBYTES>;
128/// Stack-allocated public/secret keypair for authenticated public-key
129/// boxes.
130pub type KeyPair = crate::keypair::KeyPair<PublicKey, SecretKey>;
131
132#[cfg(any(all(feature = "protected", any(unix, windows)), all(doc, not(doctest))))]
133#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "protected")))]
134pub mod protected {
135    //! # Protected memory type aliases for [`DryocBox`]
136    //!
137    //! Type aliases for using [`DryocBox`] with protected memory.
138    //!
139    //! ## Example
140    //!
141    //! ```
142    //! use dryoc::dryocbox::DryocBox;
143    //! use dryoc::dryocbox::protected::*;
144    //!
145    //! // Generate a random sender and recipient keypair, into locked, readonly
146    //! // memory.
147    //! let sender_keypair = LockedROKeyPair::generate_readonly_locked_keypair().expect("keypair");
148    //! let recipient_keypair = LockedROKeyPair::generate_readonly_locked_keypair().expect("keypair");
149    //!
150    //! // Generate a random nonce, into locked, readonly memory.
151    //! let nonce = Nonce::generate_readonly_locked().expect("nonce failed");
152    //!
153    //! // Read message into locked, readonly memory.
154    //! let message = HeapBytes::from_slice_into_readonly_locked(b"Secret message from Santa Claus")
155    //!     .expect("message failed");
156    //!
157    //! // Encrypt message into a locked box.
158    //! let dryocbox: LockedBox = DryocBox::encrypt(
159    //!     &message,
160    //!     &nonce,
161    //!     &recipient_keypair.public_key,
162    //!     &sender_keypair.secret_key,
163    //! )
164    //! .expect("encrypt failed");
165    //!
166    //! // Decrypt message into locked bytes.
167    //! let decrypted: LockedBytes = dryocbox
168    //!     .decrypt(
169    //!         &nonce,
170    //!         &sender_keypair.public_key,
171    //!         &recipient_keypair.secret_key,
172    //!     )
173    //!     .expect("decrypt failed");
174    //!
175    //! assert_eq!(message.as_slice(), decrypted.as_slice());
176    //! ```
177    use super::*;
178    pub use crate::protected::*;
179
180    /// Heap-allocated, page-aligned public key for authenticated public-key
181    /// boxes, for use with protected memory.
182    pub type PublicKey = HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
183    /// Heap-allocated, page-aligned secret key for authenticated public-key
184    /// boxes, for use with protected memory.
185    pub type SecretKey = HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
186    /// Heap-allocated, page-aligned nonce for authenticated public-key
187    /// boxes, for use with protected memory.
188    pub type Nonce = HeapByteArray<CRYPTO_BOX_NONCEBYTES>;
189    /// Heap-allocated, page-aligned message authentication code for
190    /// authenticated public-key boxes, for use with protected memory.
191    pub type Mac = HeapByteArray<CRYPTO_BOX_MACBYTES>;
192
193    /// Heap-allocated, page-aligned public/secret keypair for
194    /// authenticated public-key boxes, for use with protected memory.
195    pub type LockedKeyPair = crate::keypair::KeyPair<Locked<PublicKey>, Locked<SecretKey>>;
196    /// Heap-allocated, page-aligned public/secret keypair for
197    /// authenticated public-key boxes, for use with protected memory.
198    pub type LockedROKeyPair = crate::keypair::KeyPair<LockedRO<PublicKey>, LockedRO<SecretKey>>;
199    /// Locked [DryocBox], provided as a type alias for convenience.
200    pub type LockedBox = DryocBox<Locked<PublicKey>, Locked<Mac>, LockedBytes>;
201}
202
203#[cfg_attr(
204    feature = "serde",
205    derive(Zeroize, Clone, Debug, Serialize, Deserialize)
206)]
207#[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))]
208/// A libsodium public-key authenticated encrypted box.
209///
210/// Refer to [crate::dryocbox] for sample usage.
211pub struct DryocBox<
212    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
213    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
214    Data: Bytes + Zeroize,
215> {
216    ephemeral_pk: Option<EphemeralPublicKey>,
217    tag: Mac,
218    data: Data,
219}
220
221/// [Vec]-based authenticated public-key box.
222pub type VecBox = DryocBox<PublicKey, Mac, Vec<u8>>;
223
224#[cfg(feature = "wincode")]
225// SAFETY: The implementation writes exactly the fields used to reconstruct
226// `VecBox` below, using `wincode` schema implementations for each initialized
227// field and preserving their order.
228unsafe impl<C: wincode::config::Config> wincode::SchemaWrite<C> for VecBox {
229    type Src = Self;
230
231    fn size_of(src: &Self::Src) -> wincode::WriteResult<usize> {
232        Ok(
233            <Option<[u8; CRYPTO_BOX_PUBLICKEYBYTES]> as wincode::SchemaWrite<C>>::size_of(
234                &src.ephemeral_pk.as_ref().map(|epk| *epk.as_array()),
235            )? + <[u8; CRYPTO_BOX_MACBYTES] as wincode::SchemaWrite<C>>::size_of(
236                src.tag.as_array(),
237            )? + <Vec<u8> as wincode::SchemaWrite<C>>::size_of(&src.data)?,
238        )
239    }
240
241    fn write(mut writer: impl wincode::io::Writer, src: &Self::Src) -> wincode::WriteResult<()> {
242        <Option<[u8; CRYPTO_BOX_PUBLICKEYBYTES]> as wincode::SchemaWrite<C>>::write(
243            writer.by_ref(),
244            &src.ephemeral_pk.as_ref().map(|epk| *epk.as_array()),
245        )?;
246        <[u8; CRYPTO_BOX_MACBYTES] as wincode::SchemaWrite<C>>::write(
247            writer.by_ref(),
248            src.tag.as_array(),
249        )?;
250        <Vec<u8> as wincode::SchemaWrite<C>>::write(writer, &src.data)
251    }
252}
253
254#[cfg(feature = "wincode")]
255// SAFETY: The implementation fully initializes `dst` with a valid `VecBox`
256// after successfully reading each field in the same order as `SchemaWrite`.
257unsafe impl<'de, C: wincode::config::Config> wincode::SchemaRead<'de, C> for VecBox {
258    type Dst = Self;
259
260    fn read(
261        mut reader: impl wincode::io::Reader<'de>,
262        dst: &mut std::mem::MaybeUninit<Self::Dst>,
263    ) -> wincode::ReadResult<()> {
264        let ephemeral_pk = <Option<[u8; CRYPTO_BOX_PUBLICKEYBYTES]> as wincode::SchemaRead<
265            'de,
266            C,
267        >>::get(reader.by_ref())?
268        .map(Into::into);
269        let tag = <[u8; CRYPTO_BOX_MACBYTES] as wincode::SchemaRead<'de, C>>::get(reader.by_ref())?;
270        let data = <Vec<u8> as wincode::SchemaRead<'de, C>>::get(reader)?;
271        dst.write(Self {
272            ephemeral_pk,
273            tag: tag.into(),
274            data,
275        });
276        Ok(())
277    }
278}
279
280impl<
281    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
282    Mac: NewByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
283    Data: NewBytes + ResizableBytes + Zeroize,
284> DryocBox<EphemeralPublicKey, Mac, Data>
285{
286    /// Encrypts a message using `sender_secret_key` for `recipient_public_key`,
287    /// and returns a new [`DryocBox`] with ciphertext and tag.
288    ///
289    /// # Errors
290    ///
291    /// Returns an error if the message is too long, `recipient_public_key` is
292    /// an unacceptable low-order key, or the output storage does not resize to
293    /// the message length.
294    pub fn encrypt<
295        Message: Bytes + ?Sized,
296        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
297        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
298        SenderSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
299    >(
300        message: &Message,
301        nonce: &Nonce,
302        recipient_public_key: &RecipientPublicKey,
303        sender_secret_key: &SenderSecretKey,
304    ) -> Result<Self, Error> {
305        use crate::classic::crypto_box::crypto_box_detached;
306
307        let mut dryocbox = Self {
308            ephemeral_pk: None,
309            tag: Mac::new_byte_array(),
310            data: Data::new_bytes(),
311        };
312
313        dryocbox.data.resize(message.as_slice().len(), 0);
314
315        crypto_box_detached(
316            dryocbox.data.as_mut_slice(),
317            dryocbox.tag.as_mut_array(),
318            message.as_slice(),
319            nonce.as_array(),
320            recipient_public_key.as_array(),
321            sender_secret_key.as_array(),
322        )?;
323
324        Ok(dryocbox)
325    }
326
327    /// Encrypts a message using `precalc_secret_key`, and returns a new
328    /// [`DryocBox`] with ciphertext and tag.
329    ///
330    /// # Errors
331    ///
332    /// Returns an error if the message is too long or the output storage does
333    /// not resize to the message length.
334    pub fn precalc_encrypt<
335        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
336        Message: Bytes + ?Sized,
337        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
338    >(
339        message: &Message,
340        nonce: &Nonce,
341        precalc_secret_key: &PrecalcSecretKey,
342    ) -> Result<Self, Error> {
343        use crate::classic::crypto_box::crypto_box_detached_afternm;
344
345        let mut dryocbox = Self {
346            ephemeral_pk: None,
347            tag: Mac::new_byte_array(),
348            data: Data::new_bytes(),
349        };
350
351        dryocbox.data.resize(message.as_slice().len(), 0);
352
353        crypto_box_detached_afternm(
354            dryocbox.data.as_mut_slice(),
355            dryocbox.tag.as_mut_array(),
356            message.as_slice(),
357            nonce.as_array(),
358            precalc_secret_key.as_array(),
359        )?;
360
361        Ok(dryocbox)
362    }
363}
364
365impl<
366    EphemeralPublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
367    Mac: NewByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
368    Data: NewBytes + ResizableBytes + Zeroize,
369> DryocBox<EphemeralPublicKey, Mac, Data>
370{
371    /// Encrypts a message for `recipient_public_key`, using an ephemeral secret
372    /// key and nonce. Returns a new [`DryocBox`] with ciphertext, tag, and
373    /// ephemeral public key.
374    ///
375    /// # Errors
376    ///
377    /// Returns an error if the message is too long, `recipient_public_key` is
378    /// an unacceptable low-order key, or the output storage does not resize to
379    /// the message length.
380    ///
381    /// # Panics
382    ///
383    /// Panics if the operating system's random number generator fails while
384    /// creating the ephemeral keypair.
385    pub fn seal<
386        Message: Bytes + ?Sized,
387        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
388    >(
389        message: &Message,
390        recipient_public_key: &RecipientPublicKey,
391    ) -> Result<Self, Error> {
392        use crate::classic::crypto_box::{
393            crypto_box_detached, crypto_box_keypair, crypto_box_seal_nonce,
394        };
395
396        let mut nonce = Nonce::new_byte_array();
397        let (epk, esk) = crypto_box_keypair();
398        let esk = Zeroizing::new(esk);
399        crypto_box_seal_nonce(nonce.as_mut_array(), &epk, recipient_public_key.as_array());
400
401        let mut pk = EphemeralPublicKey::new_byte_array();
402        pk.copy_from_slice(&epk);
403
404        let mut dryocbox = Self {
405            ephemeral_pk: Some(pk),
406            tag: Mac::new_byte_array(),
407            data: Data::new_bytes(),
408        };
409
410        dryocbox.data.resize(message.as_slice().len(), 0);
411
412        crypto_box_detached(
413            dryocbox.data.as_mut_slice(),
414            dryocbox.tag.as_mut_array(),
415            message.as_slice(),
416            nonce.as_array(),
417            recipient_public_key.as_array(),
418            &esk,
419        )?;
420
421        Ok(dryocbox)
422    }
423}
424
425impl<
426    'a,
427    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
428    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
429    Data: Bytes + From<&'a [u8]> + Zeroize,
430> DryocBox<EphemeralPublicKey, Mac, Data>
431{
432    /// Initializes a [`DryocBox`] from a slice. Expects the first
433    /// [`CRYPTO_BOX_MACBYTES`] bytes to contain the message authentication tag,
434    /// with the remaining bytes containing the encrypted message.
435    ///
436    /// # Errors
437    ///
438    /// Returns an error if `bytes` is shorter than one authentication tag or
439    /// the tag cannot be converted to `Mac`.
440    pub fn from_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
441        if bytes.len() < CRYPTO_BOX_MACBYTES {
442            Err(length_error!(crate::ErrorContext::Box, bytes.len(), min CRYPTO_BOX_MACBYTES))
443        } else {
444            let (tag, data) = bytes.split_at(CRYPTO_BOX_MACBYTES);
445            Ok(Self {
446                ephemeral_pk: None,
447                tag: Mac::try_from(tag)
448                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
449                data: Data::from(data),
450            })
451        }
452    }
453
454    /// Initializes a sealed [`DryocBox`] from a slice. Expects the first
455    /// [`CRYPTO_BOX_PUBLICKEYBYTES`] bytes to contain the ephemeral public key,
456    /// the next [`CRYPTO_BOX_MACBYTES`] bytes to be the message authentication
457    /// tag, with the remaining bytes containing the encrypted message.
458    ///
459    /// # Errors
460    ///
461    /// Returns an error if `bytes` is shorter than one ephemeral public key
462    /// plus one authentication tag, or if either field cannot be converted to
463    /// its target type.
464    pub fn from_sealed_bytes(bytes: &'a [u8]) -> Result<Self, Error> {
465        if bytes.len() < CRYPTO_BOX_SEALBYTES {
466            Err(
467                length_error!(crate::ErrorContext::SealedBox, bytes.len(), min CRYPTO_BOX_SEALBYTES),
468            )
469        } else {
470            let (seal, data) = bytes.split_at(CRYPTO_BOX_SEALBYTES);
471            let (epk, tag) = seal.split_at(CRYPTO_BOX_PUBLICKEYBYTES);
472            Ok(Self {
473                ephemeral_pk: Some(
474                    EphemeralPublicKey::try_from(epk)
475                        .map_err(|_| Error::invalid_key(crate::ErrorContext::EphemeralPublicKey))?,
476                ),
477                tag: Mac::try_from(tag)
478                    .map_err(|_| Error::invalid_encoding(crate::ErrorContext::AuthenticationTag))?,
479                data: Data::from(data),
480            })
481        }
482    }
483}
484
485impl<
486    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
487    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
488    Data: Bytes + Zeroize,
489> DryocBox<EphemeralPublicKey, Mac, Data>
490{
491    /// Returns a new box with `tag`, `data` and (optional) `ephemeral_pk`,
492    /// consuming each.
493    pub fn from_parts(tag: Mac, data: Data, ephemeral_pk: Option<EphemeralPublicKey>) -> Self {
494        Self {
495            ephemeral_pk,
496            tag,
497            data,
498        }
499    }
500
501    /// Copies `self` into a new [`Vec`]
502    pub fn to_vec(&self) -> Vec<u8> {
503        self.to_bytes()
504    }
505
506    /// Moves the tag, data, and (optional) ephemeral public key out of this
507    /// instance, returning them as a tuple.
508    pub fn into_parts(self) -> (Mac, Data, Option<EphemeralPublicKey>) {
509        (self.tag, self.data, self.ephemeral_pk)
510    }
511
512    /// Decrypts this box using `nonce`, `recipient_secret_key`, and
513    /// `sender_public_key`, returning the decrypted message upon success.
514    ///
515    /// # Errors
516    ///
517    /// Returns an error if the ciphertext is too long, `sender_public_key` is
518    /// an unacceptable low-order key, the output storage has the wrong length,
519    /// or authentication fails. Authentication fails for a wrong key, nonce,
520    /// tag, or ciphertext.
521    pub fn decrypt<
522        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
523        SenderPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
524        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
525        Output: ResizableBytes + NewBytes,
526    >(
527        &self,
528        nonce: &Nonce,
529        sender_public_key: &SenderPublicKey,
530        recipient_secret_key: &RecipientSecretKey,
531    ) -> Result<Output, Error> {
532        use crate::classic::crypto_box::*;
533
534        let mut message = Output::new_bytes();
535        message.resize(self.data.as_slice().len(), 0);
536
537        crypto_box_open_detached(
538            message.as_mut_slice(),
539            self.tag.as_array(),
540            self.data.as_slice(),
541            nonce.as_array(),
542            sender_public_key.as_array(),
543            recipient_secret_key.as_array(),
544        )?;
545
546        Ok(message)
547    }
548
549    /// Decrypts this box using `nonce` and `precalc_secret_key`, returning the
550    /// decrypted message upon success.
551    ///
552    /// # Errors
553    ///
554    /// Returns an error if the ciphertext is too long, the output storage has
555    /// the wrong length, or authentication fails because the precomputed key,
556    /// nonce, tag, or ciphertext does not match.
557    pub fn precalc_decrypt<
558        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
559        Nonce: ByteArray<CRYPTO_BOX_NONCEBYTES>,
560        Output: ResizableBytes + NewBytes,
561    >(
562        &self,
563        nonce: &Nonce,
564        precalc_secret_key: &PrecalcSecretKey,
565    ) -> Result<Output, Error> {
566        use crate::classic::crypto_box::crypto_box_open_detached_afternm;
567
568        let mut message = Output::new_bytes();
569        message.resize(self.data.as_slice().len(), 0);
570
571        crypto_box_open_detached_afternm(
572            message.as_mut_slice(),
573            self.tag.as_array(),
574            self.data.as_slice(),
575            nonce.as_array(),
576            precalc_secret_key.as_array(),
577        )?;
578
579        Ok(message)
580    }
581
582    /// Decrypts this sealed box using `recipient_keypair`.
583    ///
584    /// # Errors
585    ///
586    /// Returns an error if the ciphertext is too long, the box has no
587    /// ephemeral public key, that key is an unacceptable low-order key, the
588    /// output storage has the wrong length, or authentication fails.
589    /// Authentication fails for the wrong recipient key pair or modified box
590    /// data.
591    pub fn unseal<
592        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
593        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
594        Output: ResizableBytes + NewBytes + Zeroize,
595    >(
596        &self,
597        recipient_keypair: &crate::keypair::KeyPair<RecipientPublicKey, RecipientSecretKey>,
598    ) -> Result<Output, Error> {
599        use crate::classic::crypto_box::*;
600
601        match &self.ephemeral_pk {
602            Some(epk) => {
603                let mut nonce = Nonce::new_byte_array();
604                crypto_box_seal_nonce(
605                    nonce.as_mut_array(),
606                    epk.as_array(),
607                    recipient_keypair.public_key.as_array(),
608                );
609
610                let mut message = Output::new_bytes();
611                message.resize(self.data.as_slice().len(), 0);
612
613                crypto_box_open_detached(
614                    message.as_mut_slice(),
615                    self.tag.as_array(),
616                    self.data.as_slice(),
617                    nonce.as_array(),
618                    epk.as_array(),
619                    recipient_keypair.secret_key.as_array(),
620                )?;
621
622                Ok(message)
623            }
624            None => Err(Error::missing_data(crate::ErrorContext::EphemeralPublicKey)),
625        }
626    }
627
628    /// Copies `self` into the target. Can be used with protected memory.
629    pub fn to_bytes<Bytes: NewBytes + ResizableBytes>(&self) -> Bytes {
630        let mut data = Bytes::new_bytes();
631        match &self.ephemeral_pk {
632            Some(epk) => {
633                data.resize(epk.len() + self.tag.len() + self.data.len(), 0);
634                let s = data.as_mut_slice();
635                s[..CRYPTO_BOX_PUBLICKEYBYTES].copy_from_slice(epk.as_slice());
636                s[CRYPTO_BOX_PUBLICKEYBYTES..CRYPTO_BOX_SEALBYTES]
637                    .copy_from_slice(self.tag.as_slice());
638                s[CRYPTO_BOX_SEALBYTES..].copy_from_slice(self.data.as_slice());
639            }
640            None => {
641                data.resize(self.tag.len() + self.data.len(), 0);
642                let s = data.as_mut_slice();
643                s[..CRYPTO_BOX_MACBYTES].copy_from_slice(self.tag.as_slice());
644                s[CRYPTO_BOX_MACBYTES..].copy_from_slice(self.data.as_slice());
645            }
646        }
647        data
648    }
649}
650
651impl DryocBox<PublicKey, Mac, Vec<u8>> {
652    /// Encrypts a message using `sender_secret_key` for `recipient_public_key`,
653    /// and returns a new [`DryocBox`] with ciphertext and tag.
654    ///
655    /// # Errors
656    ///
657    /// Returns an error if the message is too long or `recipient_public_key` is
658    /// an unacceptable low-order key.
659    pub fn encrypt_to_vecbox<
660        Message: Bytes + ?Sized,
661        SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
662    >(
663        message: &Message,
664        nonce: &Nonce,
665        recipient_public_key: &PublicKey,
666        sender_secret_key: &SecretKey,
667    ) -> Result<Self, Error> {
668        Self::encrypt(message, nonce, recipient_public_key, sender_secret_key)
669    }
670
671    /// Encrypts a message using `precalc_secret_key`, and returns a new
672    /// [`DryocBox`] with ciphertext and tag.
673    ///
674    /// # Errors
675    ///
676    /// Returns an error if the message is too long or the output storage cannot
677    /// hold the ciphertext.
678    pub fn precalc_encrypt_to_vecbox<
679        Message: Bytes + ?Sized,
680        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
681    >(
682        message: &Message,
683        nonce: &Nonce,
684        precalc_secret_key: &PrecalcSecretKey,
685    ) -> Result<Self, Error> {
686        Self::precalc_encrypt(message, nonce, precalc_secret_key)
687    }
688
689    /// Encrypts a message for `recipient_public_key`, using an ephemeral secret
690    /// key and nonce, and returns a new [`DryocBox`] with the ciphertext,
691    /// ephemeral public key, and tag.
692    ///
693    /// # Errors
694    ///
695    /// Returns an error if the message is too long or `recipient_public_key` is
696    /// an unacceptable low-order key.
697    ///
698    /// # Panics
699    ///
700    /// Panics if the operating system's random number generator fails while
701    /// creating the ephemeral keypair.
702    pub fn seal_to_vecbox<Message: Bytes + ?Sized>(
703        message: &Message,
704        recipient_public_key: &PublicKey,
705    ) -> Result<Self, Error> {
706        Self::seal(message, recipient_public_key)
707    }
708
709    /// Decrypts this box using `nonce`, `recipient_secret_key` and
710    /// `sender_public_key`, returning the decrypted message upon success.
711    ///
712    /// # Errors
713    ///
714    /// Returns an error if the ciphertext is too long, `sender_public_key` is
715    /// an unacceptable low-order key, or authentication fails because a key,
716    /// nonce, tag, or ciphertext is wrong.
717    pub fn decrypt_to_vec<SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES>>(
718        &self,
719        nonce: &Nonce,
720        sender_public_key: &PublicKey,
721        recipient_secret_key: &SecretKey,
722    ) -> Result<Vec<u8>, Error> {
723        self.decrypt(nonce, sender_public_key, recipient_secret_key)
724    }
725
726    /// Decrypts this box using `nonce` and
727    /// `precalc_secret_key`, returning the decrypted message upon
728    /// success.
729    ///
730    /// # Errors
731    ///
732    /// Returns an error if the ciphertext is too long or authentication fails
733    /// because the precomputed key, nonce, tag, or ciphertext does not match.
734    pub fn precalc_decrypt_to_vec<
735        PrecalcSecretKey: ByteArray<CRYPTO_BOX_BEFORENMBYTES> + Zeroize,
736    >(
737        &self,
738        nonce: &Nonce,
739        precalc_secret_key: &PrecalcSecretKey,
740    ) -> Result<Vec<u8>, Error> {
741        self.precalc_decrypt(nonce, precalc_secret_key)
742    }
743
744    /// Decrypts this sealed box using `recipient_keypair`.
745    ///
746    /// # Errors
747    ///
748    /// Returns an error if the ciphertext is too long, the box has no
749    /// ephemeral public key, that key is an unacceptable low-order key, or
750    /// authentication fails because the recipient key pair or box data is
751    /// wrong.
752    pub fn unseal_to_vec<
753        RecipientPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
754        RecipientSecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
755    >(
756        &self,
757        recipient_keypair: &crate::keypair::KeyPair<RecipientPublicKey, RecipientSecretKey>,
758    ) -> Result<Vec<u8>, Error> {
759        self.unseal(recipient_keypair)
760    }
761}
762
763impl<
764    'a,
765    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
766    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
767    Data: Bytes + ResizableBytes + From<&'a [u8]> + Zeroize,
768> DryocBox<EphemeralPublicKey, Mac, Data>
769{
770    /// Returns a new box with ciphertext copied from `input` and the supplied
771    /// `tag`. The box has no ephemeral public key.
772    pub fn new_with_data_and_mac(tag: Mac, input: &'a [u8]) -> Self {
773        Self {
774            ephemeral_pk: None,
775            tag,
776            data: input.into(),
777        }
778    }
779
780    /// Returns a new sealed box with ciphertext copied from `input` and the
781    /// supplied `ephemeral_pk` and `tag`.
782    pub fn new_with_epk_data_and_mac(
783        ephemeral_pk: EphemeralPublicKey,
784        tag: Mac,
785        input: &'a [u8],
786    ) -> Self {
787        Self {
788            ephemeral_pk: Some(ephemeral_pk),
789            tag,
790            data: input.into(),
791        }
792    }
793}
794
795impl<
796    EphemeralPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
797    Mac: ByteArray<CRYPTO_BOX_MACBYTES> + Zeroize,
798    Data: Bytes + Zeroize,
799> PartialEq<DryocBox<EphemeralPublicKey, Mac, Data>> for DryocBox<EphemeralPublicKey, Mac, Data>
800{
801    fn eq(&self, other: &Self) -> bool {
802        if let Some(our_epk) = &self.ephemeral_pk {
803            if let Some(their_epk) = &other.ephemeral_pk {
804                self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
805                    && self
806                        .data
807                        .as_slice()
808                        .ct_eq(other.data.as_slice())
809                        .unwrap_u8()
810                        == 1
811                    && our_epk.as_slice().ct_eq(their_epk.as_slice()).unwrap_u8() == 1
812            } else {
813                false
814            }
815        } else if other.ephemeral_pk.is_none() {
816            self.tag.as_slice().ct_eq(other.tag.as_slice()).unwrap_u8() == 1
817                && self
818                    .data
819                    .as_slice()
820                    .ct_eq(other.data.as_slice())
821                    .unwrap_u8()
822                    == 1
823        } else {
824            false
825        }
826    }
827}
828
829#[cfg(test)]
830mod tests {
831    use super::*;
832    use crate::precalc::PrecalcSecretKey;
833
834    #[test]
835    fn unseal_requires_an_ephemeral_public_key() {
836        let box_without_ephemeral_key =
837            VecBox::from_bytes(&[0u8; CRYPTO_BOX_MACBYTES]).expect("a regular box should parse");
838        let recipient_keypair = KeyPair::generate();
839
840        let error = box_without_ephemeral_key
841            .unseal::<_, _, Vec<u8>>(&recipient_keypair)
842            .expect_err("a regular box cannot be unsealed");
843        assert!(matches!(
844            error,
845            Error::MissingData {
846                context: crate::ErrorContext::EphemeralPublicKey,
847            }
848        ));
849    }
850
851    #[test]
852    fn test_decrypt_failure_empty() {
853        for _ in 0..20 {
854            use crate::keypair::*;
855
856            let invalid_key = KeyPair::generate();
857            let invalid_key_copy_1 = invalid_key.clone();
858            let invalid_key_copy_2 = invalid_key.clone();
859            let nonce = Nonce::generate();
860
861            let dryocbox: VecBox =
862                DryocBox::from_bytes(b"trollolllololololollollolololololol").expect("ok");
863            DryocBox::decrypt::<
864                Nonce,
865                crate::classic::crypto_box::PublicKey,
866                crate::classic::crypto_box::SecretKey,
867                Vec<u8>,
868            >(
869                &dryocbox,
870                &nonce,
871                &invalid_key_copy_1.public_key,
872                &invalid_key_copy_2.secret_key,
873            )
874            .expect_err("hmm");
875        }
876    }
877
878    #[test]
879    fn test_copy() {
880        for _ in 0..20 {
881            use std::convert::TryFrom;
882
883            use crate::rng::*;
884
885            let mut data1: Vec<u8> = vec![0u8; 1024];
886            copy_randombytes(data1.as_mut_slice());
887            let data1_copy = data1.clone();
888
889            let dryocbox: VecBox = DryocBox::from_bytes(&data1).expect("ok");
890            assert_eq!(dryocbox.data.as_slice(), &data1_copy[CRYPTO_BOX_MACBYTES..]);
891            assert_eq!(dryocbox.tag.as_slice(), &data1_copy[..CRYPTO_BOX_MACBYTES]);
892
893            let data1 = data1_copy.clone();
894            let (tag, data) = data1.split_at(CRYPTO_BOX_MACBYTES);
895            let dryocbox: VecBox =
896                DryocBox::new_with_data_and_mac(Mac::try_from(tag).expect("mac"), data);
897            assert_eq!(dryocbox.data.as_slice(), &data1_copy[CRYPTO_BOX_MACBYTES..]);
898            assert_eq!(dryocbox.tag.as_array(), &data1_copy[..CRYPTO_BOX_MACBYTES]);
899        }
900    }
901
902    #[test]
903    fn test_precalc_encrypt_decrypt() {
904        let keypair_sender = KeyPair::generate();
905        let keypair_recipient = KeyPair::generate();
906        let nonce = Nonce::generate();
907
908        let message = b"To be, or not to be, that is the question:";
909        let precalc_secret_key = PrecalcSecretKey::precalculate(
910            &keypair_recipient.public_key,
911            &keypair_sender.secret_key,
912        )
913        .expect("precalculation failed");
914
915        let dryocbox: VecBox = DryocBox::precalc_encrypt(message, &nonce, &precalc_secret_key)
916            .expect("unable to encrypt");
917
918        let decrypted: Vec<u8> = dryocbox
919            .precalc_decrypt(&nonce, &precalc_secret_key)
920            .expect("unable to decrypt");
921
922        assert_eq!(message, decrypted.as_slice());
923    }
924
925    #[test]
926    fn test_precalc_encrypt_to_vecbox_decrypt_to_vecbox() {
927        let keypair_sender = KeyPair::generate();
928        let keypair_recipient = KeyPair::generate();
929        let nonce = Nonce::generate();
930
931        let message = b"All the world's a stage, and all the men and women merely players:";
932        let precalc_secret_key = PrecalcSecretKey::precalculate(
933            &keypair_recipient.public_key,
934            &keypair_sender.secret_key,
935        )
936        .expect("precalculation failed");
937
938        let dryocbox = DryocBox::precalc_encrypt_to_vecbox(message, &nonce, &precalc_secret_key)
939            .expect("unable to encrypt");
940
941        let decrypted = dryocbox
942            .precalc_decrypt_to_vec(&nonce, &precalc_secret_key)
943            .expect("unable to decrypt");
944
945        assert_eq!(message, decrypted.as_slice());
946    }
947
948    #[test]
949    fn test_precalc_encrypt_decrypt_with_different_messages() {
950        let keypair_sender = KeyPair::generate();
951        let keypair_recipient = KeyPair::generate();
952        let nonce = Nonce::generate();
953
954        let messages: Vec<&[u8]> = vec![
955            b"Now is the winter of our discontent, made glorious summer by this sun of York;",
956            b"Friends, Romans, countrymen, lend me your ears; I come to bury Caesar, not to praise him.",
957            b"A horse! a horse! my kingdom for a horse!",
958            b"Good night, good night! parting is such sweet sorrow, that I shall say good night till it be morrow.",
959        ];
960
961        let precalc_secret_key = PrecalcSecretKey::precalculate(
962            &keypair_recipient.public_key,
963            &keypair_sender.secret_key,
964        )
965        .expect("precalculation failed");
966
967        for message in &messages {
968            let dryocbox: VecBox = DryocBox::precalc_encrypt(message, &nonce, &precalc_secret_key)
969                .expect("unable to encrypt");
970
971            let decrypted: Vec<u8> = dryocbox
972                .precalc_decrypt(&nonce, &precalc_secret_key)
973                .expect("unable to decrypt");
974
975            assert_eq!(*message, decrypted.as_slice());
976        }
977    }
978
979    #[test]
980    fn test_precalc_encrypt_to_vecbox_decrypt_to_vecbox_with_different_messages() {
981        let keypair_sender = KeyPair::generate();
982        let keypair_recipient = KeyPair::generate();
983        let nonce = Nonce::generate();
984
985        let messages: Vec<&[u8]> = vec![
986            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.",
987            b"Some are born great, some achieve greatness, and some have greatness thrust upon them.",
988            b"The lady doth protest too much, methinks.",
989            b"What's in a name? That which we call a rose by any other name would smell as sweet.",
990        ];
991
992        let precalc_secret_key = PrecalcSecretKey::precalculate(
993            &keypair_recipient.public_key,
994            &keypair_sender.secret_key,
995        )
996        .expect("precalculation failed");
997
998        for message in &messages {
999            let dryocbox =
1000                DryocBox::precalc_encrypt_to_vecbox(message, &nonce, &precalc_secret_key)
1001                    .expect("unable to encrypt");
1002
1003            let decrypted = dryocbox
1004                .precalc_decrypt_to_vec(&nonce, &precalc_secret_key)
1005                .expect("unable to decrypt");
1006
1007            assert_eq!(*message, decrypted.as_slice());
1008        }
1009    }
1010
1011    #[cfg(dryoc_native_tests)]
1012    mod native_tests {
1013        use super::*;
1014
1015        #[test]
1016        fn test_dryocbox_vecbox() {
1017            for i in 0..20 {
1018                use base64::Engine as _;
1019                use base64::engine::general_purpose;
1020                use sodiumoxide::crypto::box_;
1021                use sodiumoxide::crypto::box_::{Nonce as SONonce, PublicKey, SecretKey};
1022
1023                let keypair_sender = KeyPair::generate();
1024                let keypair_recipient = KeyPair::generate();
1025                let keypair_sender_copy = keypair_sender.clone();
1026                let keypair_recipient_copy = keypair_recipient.clone();
1027                let nonce = Nonce::generate();
1028                let words = vec!["hello1".to_string(); i];
1029                let message = words.join(" :D ");
1030                let message_copy = message.clone();
1031                let dryocbox = DryocBox::encrypt_to_vecbox(
1032                    message.as_bytes(),
1033                    &nonce,
1034                    &keypair_recipient.public_key,
1035                    &keypair_sender.secret_key,
1036                )
1037                .unwrap();
1038
1039                let ciphertext = dryocbox.to_vec();
1040
1041                let so_ciphertext = box_::seal(
1042                    message_copy.as_bytes(),
1043                    &SONonce::from_slice(&nonce).unwrap(),
1044                    &PublicKey::from_slice(&keypair_recipient_copy.public_key).unwrap(),
1045                    &SecretKey::from_slice(&keypair_sender_copy.secret_key).unwrap(),
1046                );
1047
1048                assert_eq!(
1049                    general_purpose::STANDARD.encode(&ciphertext),
1050                    general_purpose::STANDARD.encode(&so_ciphertext)
1051                );
1052
1053                let keypair_sender = keypair_sender_copy.clone();
1054                let keypair_recipient = keypair_recipient_copy.clone();
1055
1056                let m = dryocbox
1057                    .decrypt_to_vec(
1058                        &nonce,
1059                        &keypair_sender.public_key,
1060                        &keypair_recipient.secret_key,
1061                    )
1062                    .expect("hmm");
1063                let so_m = box_::open(
1064                    &ciphertext,
1065                    &SONonce::from_slice(&nonce).unwrap(),
1066                    &PublicKey::from_slice(&keypair_recipient_copy.public_key).unwrap(),
1067                    &SecretKey::from_slice(&keypair_sender_copy.secret_key).unwrap(),
1068                )
1069                .expect("HMMM");
1070
1071                assert_eq!(m, message_copy.as_bytes());
1072                assert_eq!(m, so_m);
1073            }
1074        }
1075
1076        #[test]
1077        fn test_decrypt_failure() {
1078            for i in 0..20 {
1079                use base64::Engine as _;
1080                use base64::engine::general_purpose;
1081                use sodiumoxide::crypto::box_;
1082                use sodiumoxide::crypto::box_::{
1083                    Nonce as SONonce, PublicKey as SOPublicKey, SecretKey as SOSecretKey,
1084                };
1085
1086                let keypair_sender = KeyPair::generate();
1087                let keypair_recipient = KeyPair::generate();
1088                let keypair_sender_copy = keypair_sender.clone();
1089                let keypair_recipient_copy = keypair_recipient.clone();
1090                let nonce = Nonce::generate();
1091                let words = vec!["hello1".to_string(); i];
1092                let message = words.join(" :D ");
1093                let message_copy = message.clone();
1094                let dryocbox = DryocBox::encrypt_to_vecbox(
1095                    message.as_bytes(),
1096                    &nonce,
1097                    &keypair_recipient.public_key,
1098                    &keypair_sender.secret_key,
1099                )
1100                .unwrap();
1101
1102                let ciphertext = dryocbox.to_vec();
1103
1104                let so_ciphertext = box_::seal(
1105                    message_copy.as_bytes(),
1106                    &SONonce::from_slice(&nonce).unwrap(),
1107                    &SOPublicKey::from_slice(&keypair_recipient_copy.public_key).unwrap(),
1108                    &SOSecretKey::from_slice(&keypair_sender_copy.secret_key).unwrap(),
1109                );
1110
1111                assert_eq!(
1112                    general_purpose::STANDARD.encode(&ciphertext),
1113                    general_purpose::STANDARD.encode(&so_ciphertext)
1114                );
1115
1116                let invalid_key = KeyPair::generate();
1117                let invalid_key_copy_1 = invalid_key.clone();
1118                let invalid_key_copy_2 = invalid_key.clone();
1119
1120                DryocBox::decrypt::<Nonce, PublicKey, SecretKey, Vec<u8>>(
1121                    &dryocbox,
1122                    &nonce,
1123                    &invalid_key_copy_1.public_key,
1124                    &invalid_key_copy_2.secret_key,
1125                )
1126                .expect_err("hmm");
1127                box_::open(
1128                    &ciphertext,
1129                    &SONonce::from_slice(&nonce).unwrap(),
1130                    &SOPublicKey::from_slice(&invalid_key.public_key).unwrap(),
1131                    &SOSecretKey::from_slice(&invalid_key.secret_key).unwrap(),
1132                )
1133                .expect_err("HMMM");
1134            }
1135        }
1136
1137        #[test]
1138        fn test_dryocbox_seal_vecbox() {
1139            for i in 0..20 {
1140                use sodiumoxide::crypto::box_::{
1141                    PublicKey as SOPublicKey, SecretKey as SOSecretKey,
1142                };
1143                use sodiumoxide::crypto::sealedbox::curve25519blake2bxsalsa20poly1305;
1144
1145                let keypair_recipient = KeyPair::generate();
1146                let words = vec!["hello1".to_string(); i];
1147                let message = words.join(" :D ");
1148                let message_copy = message.clone();
1149                let dryocbox =
1150                    DryocBox::seal_to_vecbox(message.as_bytes(), &keypair_recipient.public_key)
1151                        .unwrap();
1152
1153                let ciphertext = dryocbox.to_vec();
1154
1155                let m = dryocbox.unseal_to_vec(&keypair_recipient).expect("hmm");
1156                let so_m = curve25519blake2bxsalsa20poly1305::open(
1157                    ciphertext.as_slice(),
1158                    &SOPublicKey::from_slice(keypair_recipient.public_key.as_slice()).unwrap(),
1159                    &SOSecretKey::from_slice(keypair_recipient.secret_key.as_slice()).unwrap(),
1160                )
1161                .unwrap();
1162
1163                assert_eq!(m, message_copy.as_bytes());
1164                assert_eq!(m, so_m);
1165            }
1166        }
1167
1168        #[test]
1169        fn test_dryocbox_unseal_vecbox() {
1170            for i in 0..20 {
1171                use sodiumoxide::crypto::box_::PublicKey as SOPublicKey;
1172                use sodiumoxide::crypto::sealedbox::curve25519blake2bxsalsa20poly1305;
1173
1174                let keypair_recipient = KeyPair::generate();
1175                let words = vec!["hello1".to_string(); i];
1176                let message = words.join(" :D ");
1177
1178                let ciphertext = curve25519blake2bxsalsa20poly1305::seal(
1179                    message.as_bytes(),
1180                    &SOPublicKey::from_slice(keypair_recipient.public_key.as_slice()).unwrap(),
1181                );
1182
1183                let dryocbox =
1184                    DryocBox::from_sealed_bytes(&ciphertext).expect("from sealed bytes failed");
1185
1186                let m = dryocbox.unseal_to_vec(&keypair_recipient).expect("hmm");
1187
1188                assert_eq!(m, message.as_bytes());
1189            }
1190        }
1191    }
1192}