Skip to main content

dryoc/classic/
crypto_box.rs

1//! # Public-key authenticated encryption
2//!
3//! Implements libsodium's `crypto_box_*` functions. A sender encrypts with the
4//! recipient's public key and the sender's secret key. The recipient decrypts
5//! with the sender's public key and the recipient's secret key.
6//!
7//! Nonces are public, but a nonce must never repeat for the same sender and
8//! recipient keypair. See the [libsodium documentation](https://doc.libsodium.org/public-key_cryptography/authenticated_encryption)
9//! for details.
10//!
11//! ## Classic API example
12//!
13//! ```
14//! use dryoc::classic::crypto_box::*;
15//! use dryoc::constants::CRYPTO_BOX_MACBYTES;
16//! use dryoc::types::*;
17//!
18//! // Create a random sender keypair
19//! let (sender_pk, sender_sk) = crypto_box_keypair();
20//!
21//! // Create a random recipient keypair
22//! let (recipient_pk, recipient_sk) = crypto_box_keypair();
23//!
24//! // Generate a random nonce
25//! let nonce = Nonce::generate();
26//!
27//! let message = "hello".as_bytes();
28//! // Encrypt message
29//! let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_MACBYTES];
30//! crypto_box_easy(&mut ciphertext, message, &nonce, &recipient_pk, &sender_sk)
31//!     .expect("encrypt failed");
32//!
33//! // Decrypt message
34//! let mut decrypted_message = vec![0u8; ciphertext.len() - CRYPTO_BOX_MACBYTES];
35//! crypto_box_open_easy(
36//!     &mut decrypted_message,
37//!     &ciphertext,
38//!     &nonce,
39//!     &sender_pk,
40//!     &recipient_sk,
41//! )
42//! .expect("decrypt failed");
43//!
44//! assert_eq!(message, decrypted_message);
45//! ```
46
47use zeroize::{Zeroize, Zeroizing};
48
49use super::crypto_generichash::{
50    crypto_generichash_final, crypto_generichash_init, crypto_generichash_update,
51};
52use crate::classic::crypto_box_impl::*;
53use crate::classic::crypto_secretbox::*;
54use crate::classic::crypto_secretbox_impl::*;
55use crate::constants::*;
56use crate::error::Error;
57use crate::types::*;
58
59/// Crypto box message authentication code.
60pub type Mac = [u8; CRYPTO_BOX_MACBYTES];
61
62/// Nonce for crypto boxes.
63pub type Nonce = [u8; CRYPTO_BOX_NONCEBYTES];
64/// Public key for public key authenticated crypto boxes.
65pub type PublicKey = [u8; CRYPTO_BOX_PUBLICKEYBYTES];
66/// Secret key for public key authenticated crypto boxes.
67pub type SecretKey = [u8; CRYPTO_BOX_SECRETKEYBYTES];
68
69/// In-place variant of [`crypto_box_keypair`]
70pub fn crypto_box_keypair_inplace(public_key: &mut PublicKey, secret_key: &mut SecretKey) {
71    crypto_box_curve25519xsalsa20poly1305_keypair_inplace(public_key, secret_key)
72}
73
74/// In-place variant of [`crypto_box_seed_keypair`]
75pub fn crypto_box_seed_keypair_inplace(
76    public_key: &mut PublicKey,
77    secret_key: &mut SecretKey,
78    seed: &[u8; CRYPTO_BOX_SEEDBYTES],
79) {
80    crypto_box_curve25519xsalsa20poly1305_seed_keypair_inplace(public_key, secret_key, seed)
81}
82
83/// Generates a public/secret key pair from OS-provided random data, using
84/// [`copy_randombytes`](crate::rng::copy_randombytes).
85#[must_use]
86pub fn crypto_box_keypair() -> (PublicKey, SecretKey) {
87    crypto_box_curve25519xsalsa20poly1305_keypair()
88}
89
90/// Deterministically derives a keypair from a 32-byte `seed`.
91///
92/// Compatible with libsodium's `crypto_box_seed_keypair`.
93#[must_use]
94pub fn crypto_box_seed_keypair(seed: &[u8; CRYPTO_BOX_SEEDBYTES]) -> (PublicKey, SecretKey) {
95    crypto_box_curve25519xsalsa20poly1305_seed_keypair(seed)
96}
97
98/// Computes a shared secret for the given `public_key` and `secret_key`.
99/// Resulting shared secret can be used with the precalculation interface.
100///
101/// Compatible with libsodium's `crypto_box_beforenm`.
102///
103/// # Errors
104///
105/// Returns an error if `public_key` is an unacceptable low-order key.
106pub fn crypto_box_beforenm(public_key: &PublicKey, secret_key: &SecretKey) -> Result<Key, Error> {
107    crypto_box_curve25519xsalsa20poly1305_beforenm(public_key, secret_key)
108}
109
110/// Precalculation variant of [`crypto_box_detached`].
111///
112/// Compatible with libsodium's `crypto_box_detached_afternm`.
113///
114/// # Errors
115///
116/// Returns an error if `message` is too long or `ciphertext` is shorter than
117/// `message`.
118pub fn crypto_box_detached_afternm(
119    ciphertext: &mut [u8],
120    mac: &mut Mac,
121    message: &[u8],
122    nonce: &Nonce,
123    key: &Key,
124) -> Result<(), Error> {
125    crypto_secretbox_detached(ciphertext, mac, message, nonce, key)
126}
127
128/// In-place variant of [`crypto_box_detached_afternm`].
129pub fn crypto_box_detached_afternm_inplace(
130    ciphertext: &mut [u8],
131    mac: &mut Mac,
132    nonce: &Nonce,
133    key: &Key,
134) {
135    crypto_secretbox_detached_inplace(ciphertext, mac, nonce, key)
136}
137
138/// Encrypts a message using a key computed by [`crypto_box_beforenm`].
139///
140/// The result is placed into `ciphertext`, which must be exactly
141/// [`CRYPTO_BOX_MACBYTES`] bytes longer than `message`.
142///
143/// Compatible with libsodium's `crypto_box_easy_afternm`.
144///
145/// # Errors
146///
147/// Returns an error if `message` is too long or `ciphertext` has the wrong
148/// length.
149pub fn crypto_box_easy_afternm(
150    ciphertext: &mut [u8],
151    message: &[u8],
152    nonce: &Nonce,
153    key: &Key,
154) -> Result<(), Error> {
155    validate_length!(max CRYPTO_BOX_MESSAGEBYTES_MAX, message.len(), crate::ErrorContext::Message);
156
157    let expected_ciphertext_len = message.len() + CRYPTO_BOX_MACBYTES;
158    validate_length!(
159        exact expected_ciphertext_len,
160        ciphertext.len(),
161        crate::ErrorContext::Ciphertext
162    );
163
164    let (mac, ciphertext) = ciphertext
165        .split_first_chunk_mut::<CRYPTO_BOX_MACBYTES>()
166        .expect("validated ciphertext length");
167    crypto_box_detached_afternm(ciphertext, mac, message, nonce, key)
168}
169
170/// Detached variant of [`crypto_box_easy`].
171///
172/// Compatible with libsodium's `crypto_box_detached`.
173///
174/// # Errors
175///
176/// Returns an error if `message` is too long, `recipient_public_key` is
177/// unacceptable, or `ciphertext` is shorter than `message`.
178pub fn crypto_box_detached(
179    ciphertext: &mut [u8],
180    mac: &mut Mac,
181    message: &[u8],
182    nonce: &Nonce,
183    recipient_public_key: &PublicKey,
184    sender_secret_key: &SecretKey,
185) -> Result<(), Error> {
186    let mut key = Zeroizing::new(Key::default());
187    crypto_box_curve25519xsalsa20poly1305_beforenm_into(
188        &mut key,
189        recipient_public_key,
190        sender_secret_key,
191    )?;
192
193    crypto_box_detached_afternm(ciphertext, mac, message, nonce, &key)
194}
195
196/// In-place variant of [`crypto_box_detached`].
197///
198/// # Errors
199///
200/// Returns an error if `recipient_public_key` is unacceptable.
201pub fn crypto_box_detached_inplace(
202    message: &mut [u8],
203    mac: &mut Mac,
204    nonce: &Nonce,
205    recipient_public_key: &PublicKey,
206    sender_secret_key: &SecretKey,
207) -> Result<(), Error> {
208    let mut key = Zeroizing::new(Key::default());
209    crypto_box_curve25519xsalsa20poly1305_beforenm_into(
210        &mut key,
211        recipient_public_key,
212        sender_secret_key,
213    )?;
214
215    crypto_box_detached_afternm_inplace(message, mac, nonce, &key);
216
217    Ok(())
218}
219/// Encrypts a message in a box.
220///
221/// Encrypts `message` with recipient's public key `recipient_public_key`,
222/// sender's secret key `sender_secret_key`, and `nonce`. The result is placed
223/// into `ciphertext` which must be the length of the message plus
224/// [`CRYPTO_BOX_MACBYTES`] bytes, for the message tag.
225///
226/// Compatible with libsodium's `crypto_box_easy`.
227///
228/// # Errors
229///
230/// Returns an error if `message` is too long, `ciphertext` has the wrong
231/// length, or `recipient_public_key` is unacceptable.
232pub fn crypto_box_easy(
233    ciphertext: &mut [u8],
234    message: &[u8],
235    nonce: &Nonce,
236    recipient_public_key: &PublicKey,
237    sender_secret_key: &SecretKey,
238) -> Result<(), Error> {
239    validate_length!(max CRYPTO_BOX_MESSAGEBYTES_MAX, message.len(), crate::ErrorContext::Message);
240    validate_length!(
241        exact message.len() + CRYPTO_BOX_MACBYTES,
242        ciphertext.len(),
243        crate::ErrorContext::Ciphertext
244    );
245
246    let (mac, ciphertext) = ciphertext
247        .split_first_chunk_mut::<CRYPTO_BOX_MACBYTES>()
248        .expect("validated ciphertext length");
249    crypto_box_detached(
250        ciphertext,
251        mac,
252        message,
253        nonce,
254        recipient_public_key,
255        sender_secret_key,
256    )?;
257
258    Ok(())
259}
260
261pub(crate) fn crypto_box_seal_nonce(nonce: &mut Nonce, epk: &PublicKey, rpk: &SecretKey) {
262    let mut state = crypto_generichash_init(None, CRYPTO_BOX_NONCEBYTES).expect("state");
263    crypto_generichash_update(&mut state, epk);
264    crypto_generichash_update(&mut state, rpk);
265    crypto_generichash_final(state, nonce).expect("hash error");
266}
267
268fn crypto_box_seal_ciphertext_len(message_len: usize) -> Result<usize, Error> {
269    message_len
270        .checked_add(CRYPTO_BOX_SEALBYTES)
271        .ok_or(Error::arithmetic_overflow(crate::ErrorContext::SealedBox))
272}
273
274/// Encrypts and seals a message in a box.
275///
276/// Encrypts `message` with recipient's public key `recipient_public_key`, using
277/// an ephemeral keypair and nonce. The length of `ciphertext` must be the
278/// length of the message plus [`CRYPTO_BOX_SEALBYTES`] bytes for the message
279/// tag and ephemeral public key.
280///
281/// Compatible with libsodium's `crypto_box_seal`.
282///
283/// # Errors
284///
285/// Returns an error if `ciphertext` has the wrong length, `message` is too
286/// long, or `recipient_public_key` is unacceptable.
287///
288/// # Panics
289///
290/// Panics if the operating system's random number generator fails while
291/// creating the ephemeral keypair.
292pub fn crypto_box_seal(
293    ciphertext: &mut [u8],
294    message: &[u8],
295    recipient_public_key: &PublicKey,
296) -> Result<(), Error> {
297    let expected_ciphertext_len = crypto_box_seal_ciphertext_len(message.len())?;
298    validate_length!(
299        exact expected_ciphertext_len,
300        ciphertext.len(),
301        crate::ErrorContext::Ciphertext
302    );
303
304    let mut nonce = Nonce::new_byte_array();
305    let (mut epk, esk) = crypto_box_keypair();
306    let esk = Zeroizing::new(esk);
307    crypto_box_seal_nonce(&mut nonce, &epk, recipient_public_key);
308
309    crypto_box_easy(
310        &mut ciphertext[CRYPTO_BOX_PUBLICKEYBYTES..],
311        message,
312        &nonce,
313        recipient_public_key,
314        &esk,
315    )?;
316
317    ciphertext[..CRYPTO_BOX_PUBLICKEYBYTES].copy_from_slice(&epk);
318
319    epk.zeroize();
320    nonce.zeroize();
321
322    Ok(())
323}
324
325/// Encrypts a message in-place in a box.
326///
327/// Encrypts `message` with recipient's public key `recipient_public_key` and
328/// sender's secret key `sender_secret_key` using `nonce` in-place in `data`,
329/// without allocating additional memory for the message.
330///
331/// The caller of this function is responsible for allocating `data` such that
332/// there's enough capacity for the message plus the additional
333/// [`CRYPTO_BOX_MACBYTES`] bytes for the authentication tag.
334///
335/// For this reason, the last [`CRYPTO_BOX_MACBYTES`] bytes from the input
336/// is ignored. The length of `data` should be the length of your message plus
337/// [`CRYPTO_BOX_MACBYTES`] bytes.
338///
339/// # Errors
340///
341/// Returns an error if `data` is too short or too long, or
342/// `recipient_public_key` is unacceptable.
343pub fn crypto_box_easy_inplace(
344    data: &mut [u8],
345    nonce: &Nonce,
346    recipient_public_key: &PublicKey,
347    sender_secret_key: &SecretKey,
348) -> Result<(), Error> {
349    validate_length!(min CRYPTO_BOX_MACBYTES, data.len(), crate::ErrorContext::Data);
350    validate_length!(
351        max CRYPTO_BOX_MESSAGEBYTES_MAX + CRYPTO_BOX_MACBYTES,
352        data.len(),
353        crate::ErrorContext::Data
354    );
355
356    let mut key = Zeroizing::new(Key::default());
357    crypto_box_curve25519xsalsa20poly1305_beforenm_into(
358        &mut key,
359        recipient_public_key,
360        sender_secret_key,
361    )?;
362
363    data.rotate_right(CRYPTO_BOX_MACBYTES);
364
365    let (mac, data) = data
366        .split_first_chunk_mut::<CRYPTO_BOX_MACBYTES>()
367        .expect("validated data length");
368
369    crypto_box_detached_afternm_inplace(data, mac, nonce, &key);
370
371    Ok(())
372}
373
374/// Precalculation variant of [`crypto_box_open_detached`].
375///
376/// Compatible with libsodium's `crypto_box_open_detached_afternm`.
377///
378/// # Errors
379///
380/// Returns an error if `ciphertext` is too long, `message` is shorter than
381/// `ciphertext`, or authentication fails.
382pub fn crypto_box_open_detached_afternm(
383    message: &mut [u8],
384    ciphertext: &[u8],
385    mac: &Mac,
386    nonce: &Nonce,
387    key: &Key,
388) -> Result<(), Error> {
389    crypto_secretbox_open_detached(message, ciphertext, mac, nonce, key)
390}
391
392/// In-place variant of [`crypto_box_open_detached_afternm`].
393///
394/// # Errors
395///
396/// Returns an error if authentication fails.
397pub fn crypto_box_open_detached_afternm_inplace(
398    data: &mut [u8],
399    mac: &Mac,
400    nonce: &Nonce,
401    key: &Key,
402) -> Result<(), Error> {
403    crypto_secretbox_open_detached_inplace(data, mac, nonce, key)
404}
405
406/// Decrypts a box using a key computed by [`crypto_box_beforenm`].
407///
408/// Compatible with libsodium's `crypto_box_open_easy_afternm`.
409///
410/// # Errors
411///
412/// Returns an error if `ciphertext` is shorter than an authentication tag,
413/// `message` has the wrong length, or authentication fails.
414pub fn crypto_box_open_easy_afternm(
415    message: &mut [u8],
416    ciphertext: &[u8],
417    nonce: &Nonce,
418    key: &Key,
419) -> Result<(), Error> {
420    validate_length!(min CRYPTO_BOX_MACBYTES, ciphertext.len(), crate::ErrorContext::Ciphertext);
421
422    let expected_message_len = ciphertext.len() - CRYPTO_BOX_MACBYTES;
423    validate_length!(
424        exact expected_message_len,
425        message.len(),
426        crate::ErrorContext::Message
427    );
428
429    let (mac, ciphertext) = ciphertext
430        .split_first_chunk::<CRYPTO_BOX_MACBYTES>()
431        .expect("validated ciphertext length");
432    crypto_box_open_detached_afternm(message, ciphertext, mac, nonce, key)
433}
434
435/// Detached variant of [`crypto_box_open_easy`]: decrypts `ciphertext` with
436/// the sender's public key `sender_public_key`, the recipient's secret key
437/// `recipient_secret_key`, `nonce`, and the detached `mac`.
438///
439/// Compatible with libsodium's `crypto_box_open_detached`.
440///
441/// # Errors
442///
443/// Returns an error if `ciphertext` is too long, `sender_public_key` is
444/// unacceptable, `message` is shorter than `ciphertext`, or authentication
445/// fails.
446pub fn crypto_box_open_detached(
447    message: &mut [u8],
448    ciphertext: &[u8],
449    mac: &Mac,
450    nonce: &Nonce,
451    sender_public_key: &PublicKey,
452    recipient_secret_key: &SecretKey,
453) -> Result<(), Error> {
454    let mut key = Zeroizing::new(Key::default());
455    crypto_box_curve25519xsalsa20poly1305_beforenm_into(
456        &mut key,
457        sender_public_key,
458        recipient_secret_key,
459    )?;
460
461    crypto_box_open_detached_afternm(message, ciphertext, mac, nonce, &key)?;
462
463    Ok(())
464}
465
466/// In-place variant of [`crypto_box_open_detached`]: decrypts `data` with the
467/// sender's public key `sender_public_key` and the recipient's secret key
468/// `recipient_secret_key`.
469///
470/// # Errors
471///
472/// Returns an error if `sender_public_key` is unacceptable or authentication
473/// fails.
474pub fn crypto_box_open_detached_inplace(
475    data: &mut [u8],
476    mac: &Mac,
477    nonce: &Nonce,
478    sender_public_key: &PublicKey,
479    recipient_secret_key: &SecretKey,
480) -> Result<(), Error> {
481    let mut key = Zeroizing::new(Key::default());
482    crypto_box_curve25519xsalsa20poly1305_beforenm_into(
483        &mut key,
484        sender_public_key,
485        recipient_secret_key,
486    )?;
487
488    crypto_box_open_detached_afternm_inplace(data, mac, nonce, &key)?;
489
490    Ok(())
491}
492
493/// Decrypts `ciphertext` with recipient's secret key `recipient_secret_key` and
494/// sender's public key `sender_public_key` using `nonce`.
495///
496/// Compatible with libsodium's `crypto_box_open_easy`.
497///
498/// # Errors
499///
500/// Returns an error if `ciphertext` is shorter than an authentication tag,
501/// `message` has the wrong length, `sender_public_key` is unacceptable, or
502/// authentication fails.
503pub fn crypto_box_open_easy(
504    message: &mut [u8],
505    ciphertext: &[u8],
506    nonce: &Nonce,
507    sender_public_key: &PublicKey,
508    recipient_secret_key: &SecretKey,
509) -> Result<(), Error> {
510    validate_length!(min CRYPTO_BOX_MACBYTES, ciphertext.len(), crate::ErrorContext::Ciphertext);
511    validate_length!(
512        exact ciphertext.len() - CRYPTO_BOX_MACBYTES,
513        message.len(),
514        crate::ErrorContext::Message
515    );
516
517    let (mac, ciphertext) = ciphertext
518        .split_first_chunk::<CRYPTO_BOX_MACBYTES>()
519        .expect("validated ciphertext length");
520
521    crypto_box_open_detached(
522        message,
523        ciphertext,
524        mac,
525        nonce,
526        sender_public_key,
527        recipient_secret_key,
528    )
529}
530
531/// Decrypts a sealed box.
532///
533/// Decrypts a sealed box from `ciphertext` with recipient's secret key
534/// `recipient_secret_key`, placing the result into `message`. The nonce and
535/// public key are derived from `ciphertext`. `message` length should equal
536/// the length of `ciphertext` minus [`CRYPTO_BOX_SEALBYTES`] bytes for the
537/// message tag and ephemeral public key.
538///
539/// Compatible with libsodium's `crypto_box_seal_open`.
540///
541/// # Errors
542///
543/// Returns an error if `ciphertext` is too short, `message` has the wrong
544/// length, the ephemeral public key is unacceptable, or authentication fails.
545pub fn crypto_box_seal_open(
546    message: &mut [u8],
547    ciphertext: &[u8],
548    recipient_public_key: &PublicKey,
549    recipient_secret_key: &SecretKey,
550) -> Result<(), Error> {
551    validate_length!(min CRYPTO_BOX_SEALBYTES, ciphertext.len(), crate::ErrorContext::Ciphertext);
552    validate_length!(
553        exact ciphertext.len() - CRYPTO_BOX_SEALBYTES,
554        message.len(),
555        crate::ErrorContext::Message
556    );
557
558    let mut nonce = Nonce::new_byte_array();
559    let mut epk = PublicKey::new_byte_array();
560    epk.copy_from_slice(&ciphertext[..CRYPTO_BOX_PUBLICKEYBYTES]);
561
562    crypto_box_seal_nonce(&mut nonce, &epk, recipient_public_key);
563
564    crypto_box_open_easy(
565        message,
566        &ciphertext[CRYPTO_BOX_PUBLICKEYBYTES..],
567        &nonce,
568        &epk,
569        recipient_secret_key,
570    )
571}
572
573/// Decrypts a box in-place.
574///
575/// Decrypts `data` (`mac || ciphertext`) with recipient's secret key
576/// `recipient_secret_key` and sender's public key `sender_public_key` with
577/// `nonce` in-place, without allocating additional memory for the message.
578///
579/// The caller of this function is responsible for allocating `data` such that
580/// there's enough capacity for the message plus the additional
581/// [`CRYPTO_BOX_MACBYTES`] bytes for the authentication tag.
582///
583/// After opening the box, the last [`CRYPTO_BOX_MACBYTES`] bytes can be
584/// discarded or ignored at the caller's preference.
585///
586/// # Errors
587///
588/// Returns an error if `data` is shorter than an authentication tag,
589/// `sender_public_key` is unacceptable, or authentication fails.
590pub fn crypto_box_open_easy_inplace(
591    data: &mut [u8],
592    nonce: &Nonce,
593    sender_public_key: &PublicKey,
594    recipient_secret_key: &SecretKey,
595) -> Result<(), Error> {
596    validate_length!(min CRYPTO_BOX_MACBYTES, data.len(), crate::ErrorContext::Data);
597
598    let (mac, d) = data
599        .split_first_chunk_mut::<CRYPTO_BOX_MACBYTES>()
600        .expect("validated data length");
601    let mac = &*mac;
602
603    crypto_box_open_detached_inplace(d, mac, nonce, sender_public_key, recipient_secret_key)?;
604
605    data.rotate_left(CRYPTO_BOX_MACBYTES);
606
607    Ok(())
608}
609
610#[cfg(test)]
611mod tests {
612    use super::*;
613    use crate::rng::*;
614    use crate::test_prelude::*;
615
616    #[test]
617    fn test_crypto_box_easy_invalid() {
618        for _ in 0..20 {
619            let (sender_pk, _sender_sk) = crypto_box_keypair();
620            let (_recipient_pk, recipient_sk) = crypto_box_keypair();
621            let nonce = Nonce::generate();
622
623            let mut ciphertext: Vec<u8> = vec![];
624            let message: Vec<u8> = vec![];
625
626            crypto_box_open_easy(&mut ciphertext, &message, &nonce, &sender_pk, &recipient_sk)
627                .expect_err("expected an error");
628        }
629    }
630
631    #[test]
632    fn test_crypto_box_rejects_mismatched_buffers_without_mutation() {
633        let (sender_pk, sender_sk) = crypto_box_keypair();
634        let (recipient_pk, recipient_sk) = crypto_box_keypair();
635        let nonce = Nonce::default();
636        let message = b"buffer length validation";
637
638        for output_len in [
639            message.len() + CRYPTO_BOX_MACBYTES - 1,
640            message.len() + CRYPTO_BOX_MACBYTES + 1,
641        ] {
642            let mut output = vec![0xa5; output_len];
643            let original = output.clone();
644            assert!(
645                crypto_box_easy(&mut output, message, &nonce, &recipient_pk, &sender_sk,).is_err()
646            );
647            assert_eq!(output, original);
648        }
649
650        let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_MACBYTES];
651        crypto_box_easy(&mut ciphertext, message, &nonce, &recipient_pk, &sender_sk)
652            .expect("encrypt failed");
653
654        for output_len in [message.len() - 1, message.len() + 1] {
655            let mut output = vec![0xa5; output_len];
656            let original = output.clone();
657            assert!(
658                crypto_box_open_easy(&mut output, &ciphertext, &nonce, &sender_pk, &recipient_sk,)
659                    .is_err()
660            );
661            assert_eq!(output, original);
662        }
663    }
664
665    #[test]
666    fn test_crypto_box_easy_afternm_roundtrip_and_failure_atomicity() {
667        let (sender_public_key, sender_secret_key) = crypto_box_keypair();
668        let (recipient_public_key, recipient_secret_key) = crypto_box_keypair();
669        let nonce = Nonce::generate();
670        let message = b"precomputed crypto box";
671        let sender_key = crypto_box_beforenm(&recipient_public_key, &sender_secret_key)
672            .expect("sender precalculation failed");
673        let recipient_key = crypto_box_beforenm(&sender_public_key, &recipient_secret_key)
674            .expect("recipient precalculation failed");
675        assert_eq!(sender_key, recipient_key);
676
677        let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_MACBYTES];
678        crypto_box_easy_afternm(&mut ciphertext, message, &nonce, &sender_key)
679            .expect("encryption failed");
680        let mut direct_ciphertext = vec![0u8; ciphertext.len()];
681        crypto_box_easy(
682            &mut direct_ciphertext,
683            message,
684            &nonce,
685            &recipient_public_key,
686            &sender_secret_key,
687        )
688        .expect("direct encryption failed");
689        assert_eq!(ciphertext, direct_ciphertext);
690
691        let mut decrypted = vec![0u8; message.len()];
692        crypto_box_open_easy_afternm(&mut decrypted, &ciphertext, &nonce, &recipient_key)
693            .expect("decryption failed");
694        assert_eq!(decrypted, message);
695
696        ciphertext[0] ^= 1;
697        decrypted.fill(0xa5);
698        let original_decrypted = decrypted.clone();
699        assert!(
700            crypto_box_open_easy_afternm(&mut decrypted, &ciphertext, &nonce, &recipient_key)
701                .is_err()
702        );
703        assert_eq!(decrypted, original_decrypted);
704    }
705
706    #[test]
707    fn test_crypto_box_seal_rejects_mismatched_buffers() {
708        let (recipient_public_key, recipient_secret_key) = crypto_box_keypair();
709        let message = b"sealed box buffer validation";
710
711        assert!(matches!(
712            crypto_box_seal_ciphertext_len(usize::MAX),
713            Err(Error::ArithmeticOverflow {
714                context: crate::ErrorContext::SealedBox,
715            })
716        ));
717
718        let mut short_ciphertext = vec![0u8; message.len() + CRYPTO_BOX_SEALBYTES - 1];
719        assert!(matches!(
720            crypto_box_seal(&mut short_ciphertext, message, &recipient_public_key),
721            Err(Error::InvalidLength {
722                context: crate::ErrorContext::Ciphertext,
723                actual,
724                constraint: crate::LengthConstraint::Exact(expected),
725            }) if actual == short_ciphertext.len()
726                && expected == message.len() + CRYPTO_BOX_SEALBYTES
727        ));
728
729        let short_sealed_box = vec![0u8; CRYPTO_BOX_SEALBYTES - 1];
730        assert!(matches!(
731            crypto_box_seal_open(
732                &mut [],
733                &short_sealed_box,
734                &recipient_public_key,
735                &recipient_secret_key,
736            ),
737            Err(Error::InvalidLength {
738                context: crate::ErrorContext::Ciphertext,
739                actual,
740                constraint: crate::LengthConstraint::AtLeast(CRYPTO_BOX_SEALBYTES),
741            }) if actual == short_sealed_box.len()
742        ));
743
744        let sealed_box = vec![0u8; CRYPTO_BOX_SEALBYTES + 1];
745        assert!(matches!(
746            crypto_box_seal_open(
747                &mut [],
748                &sealed_box,
749                &recipient_public_key,
750                &recipient_secret_key,
751            ),
752            Err(Error::InvalidLength {
753                context: crate::ErrorContext::Message,
754                actual: 0,
755                constraint: crate::LengthConstraint::Exact(1),
756            })
757        ));
758    }
759
760    #[test]
761    fn test_crypto_box_rejects_low_order_public_keys() {
762        let (_, secret_key) = crypto_box_keypair();
763        let nonce = Nonce::default();
764        let message = b"message";
765        let mut ciphertext = [0u8; 7];
766        let mut mac = Mac::default();
767        let mut one = PublicKey::default();
768        one[0] = 1;
769
770        for public_key in [PublicKey::default(), one] {
771            assert!(crypto_box_beforenm(&public_key, &secret_key).is_err());
772            assert!(
773                crypto_box_detached(
774                    &mut ciphertext,
775                    &mut mac,
776                    message,
777                    &nonce,
778                    &public_key,
779                    &secret_key,
780                )
781                .is_err()
782            );
783
784            let mut data = b"message with tag storage\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".to_vec();
785            let original_data = data.clone();
786            assert!(crypto_box_easy_inplace(&mut data, &nonce, &public_key, &secret_key).is_err());
787            assert_eq!(data, original_data);
788        }
789    }
790
791    /// A failed in-place open (bad tag, wrong sender, wrong nonce) leaves
792    /// every byte of the caller's buffer as it was, tag and body alike.
793    #[test]
794    fn test_crypto_box_easy_inplace_invalid() {
795        let (sender_pk, sender_sk) = crypto_box_keypair();
796        let (recipient_pk, recipient_sk) = crypto_box_keypair();
797        let (other_pk, _) = crypto_box_keypair();
798        let nonce = Nonce::generate();
799
800        let mut ciphertext: Vec<u8> = vec![];
801        crypto_box_open_easy_inplace(&mut ciphertext, &nonce, &sender_pk, &recipient_sk)
802            .expect_err("expected an error");
803
804        for len in [CRYPTO_BOX_MACBYTES, CRYPTO_BOX_MACBYTES + 1, 1024] {
805            let mut random = vec![0u8; len];
806            copy_randombytes(&mut random);
807            let original = random.clone();
808            crypto_box_open_easy_inplace(&mut random, &nonce, &sender_pk, &recipient_sk)
809                .expect_err("random bytes must not authenticate");
810            assert_eq!(random, original);
811
812            let mut data = original.clone();
813            crypto_box_easy_inplace(&mut data, &nonce, &recipient_pk, &sender_sk)
814                .expect("encrypt failed");
815            let sealed = data.clone();
816            let mut tampered = sealed.clone();
817            tampered[len - 1] ^= 1;
818            crypto_box_open_easy_inplace(&mut tampered, &nonce, &sender_pk, &recipient_sk)
819                .expect_err("tampered body must not authenticate");
820            assert_eq!(tampered[len - 1], sealed[len - 1] ^ 1);
821            assert_eq!(tampered[..len - 1], sealed[..len - 1]);
822
823            let mut wrong_sender = sealed.clone();
824            crypto_box_open_easy_inplace(&mut wrong_sender, &nonce, &other_pk, &recipient_sk)
825                .expect_err("wrong sender must not authenticate");
826            assert_eq!(wrong_sender, sealed);
827
828            let mut wrong_nonce = sealed.clone();
829            let mut other_nonce = nonce;
830            other_nonce[0] ^= 1;
831            crypto_box_open_easy_inplace(&mut wrong_nonce, &other_nonce, &sender_pk, &recipient_sk)
832                .expect_err("wrong nonce must not authenticate");
833            assert_eq!(wrong_nonce, sealed);
834
835            crypto_box_open_easy_inplace(&mut data, &nonce, &sender_pk, &recipient_sk)
836                .expect("decrypt failed");
837            assert_eq!(
838                data[..len - CRYPTO_BOX_MACBYTES],
839                original[..len - CRYPTO_BOX_MACBYTES]
840            );
841        }
842    }
843
844    /// Detached opens verify before writing: on a bad tag, a low-order key or
845    /// a too-short output, the message buffer (or the in-place data) is
846    /// untouched.
847    #[test]
848    fn test_crypto_box_open_detached_failure_atomicity() {
849        let (sender_pk, sender_sk) = crypto_box_seed_keypair(&[0x41; CRYPTO_BOX_SEEDBYTES]);
850        let (recipient_pk, recipient_sk) = crypto_box_seed_keypair(&[0x42; CRYPTO_BOX_SEEDBYTES]);
851        let nonce = [0x43; CRYPTO_BOX_NONCEBYTES];
852        let message = [0x44; 70];
853        let mut ciphertext = [0u8; 70];
854        let mut mac = Mac::default();
855        crypto_box_detached(
856            &mut ciphertext,
857            &mut mac,
858            &message,
859            &nonce,
860            &recipient_pk,
861            &sender_sk,
862        )
863        .unwrap();
864        let key = crypto_box_beforenm(&sender_pk, &recipient_sk).unwrap();
865        let mut bad_mac = mac;
866        bad_mac[0] ^= 1;
867        let mut low_order = PublicKey::default();
868        low_order[0] = 1;
869
870        let sentinel = [0xa5; 70];
871        let mut output = sentinel;
872        assert!(matches!(
873            crypto_box_open_detached(
874                &mut output,
875                &ciphertext,
876                &bad_mac,
877                &nonce,
878                &sender_pk,
879                &recipient_sk
880            ),
881            Err(Error::AuthenticationFailed)
882        ));
883        assert_eq!(output, sentinel);
884        assert!(matches!(
885            crypto_box_open_detached_afternm(&mut output, &ciphertext, &bad_mac, &nonce, &key),
886            Err(Error::AuthenticationFailed)
887        ));
888        assert_eq!(output, sentinel);
889        assert!(matches!(
890            crypto_box_open_detached(
891                &mut output,
892                &ciphertext,
893                &mac,
894                &nonce,
895                &low_order,
896                &recipient_sk
897            ),
898            Err(Error::InvalidKey { .. })
899        ));
900        assert_eq!(output, sentinel);
901        let mut short_output = [0xa5; 69];
902        assert!(matches!(
903            crypto_box_open_detached(
904                &mut short_output,
905                &ciphertext,
906                &mac,
907                &nonce,
908                &sender_pk,
909                &recipient_sk
910            ),
911            Err(Error::InvalidLength { .. })
912        ));
913        assert_eq!(short_output, [0xa5; 69]);
914
915        let mut data = ciphertext;
916        assert!(
917            crypto_box_open_detached_inplace(
918                &mut data,
919                &bad_mac,
920                &nonce,
921                &sender_pk,
922                &recipient_sk
923            )
924            .is_err()
925        );
926        assert_eq!(data, ciphertext);
927        assert!(
928            crypto_box_open_detached_afternm_inplace(&mut data, &bad_mac, &nonce, &key).is_err()
929        );
930        assert_eq!(data, ciphertext);
931        assert!(
932            crypto_box_open_detached_inplace(&mut data, &mac, &nonce, &low_order, &recipient_sk)
933                .is_err()
934        );
935        assert_eq!(data, ciphertext);
936
937        crypto_box_open_detached(
938            &mut output,
939            &ciphertext,
940            &mac,
941            &nonce,
942            &sender_pk,
943            &recipient_sk,
944        )
945        .unwrap();
946        assert_eq!(output, message);
947        crypto_box_open_detached_inplace(&mut data, &mac, &nonce, &sender_pk, &recipient_sk)
948            .unwrap();
949        assert_eq!(data, message);
950    }
951
952    #[cfg(dryoc_native_tests)]
953    mod native_tests {
954        use super::*;
955
956        /// Every low-order peer key (libsodium's blacklist, with and without
957        /// bit 255) is refused by the precomputation, as libsodium does.
958        #[test]
959        fn test_crypto_box_beforenm_low_order_compatibility() {
960            let (_, secret_key) = crypto_box_seed_keypair(&[0x54; CRYPTO_BOX_SEEDBYTES]);
961
962            for public_key in crate::scalarmult_curve25519::test_vectors::low_order_u_encodings() {
963                assert!(
964                    crypto_box_beforenm(&public_key, &secret_key).is_err(),
965                    "{public_key:02x?}"
966                );
967                assert!(
968                    crate::native_test_util::box_beforenm(&public_key, &secret_key).is_err(),
969                    "{public_key:02x?}"
970                );
971            }
972        }
973
974        /// Detached boxes at the empty, one-byte and Salsa20 block-boundary
975        /// lengths interoperate with libsodium in both directions: direct
976        /// and precomputed (`afternm`), out of place and in place, with the
977        /// same fixed keys and nonce.
978        #[test]
979        fn test_crypto_box_detached_matches_libsodium() {
980            crate::native_test_util::init();
981            let (sender_pk, sender_sk) = crypto_box_seed_keypair(&[0x51; CRYPTO_BOX_SEEDBYTES]);
982            let (recipient_pk, recipient_sk) =
983                crypto_box_seed_keypair(&[0x52; CRYPTO_BOX_SEEDBYTES]);
984            let nonce = [0x53; CRYPTO_BOX_NONCEBYTES];
985
986            let sender_key = crypto_box_beforenm(&recipient_pk, &sender_sk).unwrap();
987            let recipient_key = crypto_box_beforenm(&sender_pk, &recipient_sk).unwrap();
988            assert_eq!(sender_key, recipient_key);
989            let sodium_key = crate::native_test_util::box_beforenm(&recipient_pk, &sender_sk)
990                .expect("libsodium beforenm");
991            assert_eq!(sender_key, sodium_key);
992
993            let mut rng = crate::utils::test_util::XorShift64::new(0x243f_6a88_85a3_08d3);
994            let mut all = Vec::with_capacity(96);
995            while all.len() < 65 {
996                all.extend_from_slice(&rng.next_bytes32());
997            }
998
999            for len in [0usize, 1, 31, 32, 33, 63, 64, 65] {
1000                let message = &all[..len];
1001
1002                let mut ciphertext = vec![0u8; len];
1003                let mut mac = Mac::default();
1004                crypto_box_detached(
1005                    &mut ciphertext,
1006                    &mut mac,
1007                    message,
1008                    &nonce,
1009                    &recipient_pk,
1010                    &sender_sk,
1011                )
1012                .unwrap();
1013
1014                let mut sodium_ciphertext = vec![0u8; len];
1015                let mut sodium_mac = Mac::default();
1016                let result = unsafe {
1017                    libsodium_sys::crypto_box_detached(
1018                        sodium_ciphertext.as_mut_ptr(),
1019                        sodium_mac.as_mut_ptr(),
1020                        message.as_ptr(),
1021                        len as u64,
1022                        nonce.as_ptr(),
1023                        recipient_pk.as_ptr(),
1024                        sender_sk.as_ptr(),
1025                    )
1026                };
1027                assert_eq!(result, 0);
1028                assert_eq!(ciphertext, sodium_ciphertext, "len {len}");
1029                assert_eq!(mac, sodium_mac, "len {len}");
1030
1031                let mut afternm_ciphertext = vec![0u8; len];
1032                let mut afternm_mac = Mac::default();
1033                crypto_box_detached_afternm(
1034                    &mut afternm_ciphertext,
1035                    &mut afternm_mac,
1036                    message,
1037                    &nonce,
1038                    &sender_key,
1039                )
1040                .unwrap();
1041                assert_eq!(afternm_ciphertext, ciphertext, "afternm len {len}");
1042                assert_eq!(afternm_mac, mac, "afternm len {len}");
1043                let result = unsafe {
1044                    libsodium_sys::crypto_box_detached_afternm(
1045                        sodium_ciphertext.as_mut_ptr(),
1046                        sodium_mac.as_mut_ptr(),
1047                        message.as_ptr(),
1048                        len as u64,
1049                        nonce.as_ptr(),
1050                        sodium_key.as_ptr(),
1051                    )
1052                };
1053                assert_eq!(result, 0);
1054                assert_eq!(ciphertext, sodium_ciphertext, "sodium afternm len {len}");
1055                assert_eq!(mac, sodium_mac, "sodium afternm len {len}");
1056
1057                let mut data = message.to_vec();
1058                let mut inplace_mac = Mac::default();
1059                crypto_box_detached_inplace(
1060                    &mut data,
1061                    &mut inplace_mac,
1062                    &nonce,
1063                    &recipient_pk,
1064                    &sender_sk,
1065                )
1066                .unwrap();
1067                assert_eq!(data, ciphertext, "inplace len {len}");
1068                assert_eq!(inplace_mac, mac, "inplace len {len}");
1069                let mut data = message.to_vec();
1070                let mut inplace_mac = Mac::default();
1071                crypto_box_detached_afternm_inplace(
1072                    &mut data,
1073                    &mut inplace_mac,
1074                    &nonce,
1075                    &sender_key,
1076                );
1077                assert_eq!(data, ciphertext, "afternm inplace len {len}");
1078                assert_eq!(inplace_mac, mac, "afternm inplace len {len}");
1079
1080                // libsodium's box opens with every dryoc variant.
1081                let mut opened = vec![0xa5; len];
1082                crypto_box_open_detached(
1083                    &mut opened,
1084                    &sodium_ciphertext,
1085                    &sodium_mac,
1086                    &nonce,
1087                    &sender_pk,
1088                    &recipient_sk,
1089                )
1090                .unwrap();
1091                assert_eq!(opened, message, "open len {len}");
1092                opened.fill(0xa5);
1093                crypto_box_open_detached_afternm(
1094                    &mut opened,
1095                    &sodium_ciphertext,
1096                    &sodium_mac,
1097                    &nonce,
1098                    &recipient_key,
1099                )
1100                .unwrap();
1101                assert_eq!(opened, message, "open afternm len {len}");
1102                let mut data = sodium_ciphertext.clone();
1103                crypto_box_open_detached_inplace(
1104                    &mut data,
1105                    &sodium_mac,
1106                    &nonce,
1107                    &sender_pk,
1108                    &recipient_sk,
1109                )
1110                .unwrap();
1111                assert_eq!(data, message, "open inplace len {len}");
1112                let mut data = sodium_ciphertext.clone();
1113                crypto_box_open_detached_afternm_inplace(
1114                    &mut data,
1115                    &sodium_mac,
1116                    &nonce,
1117                    &recipient_key,
1118                )
1119                .unwrap();
1120                assert_eq!(data, message, "open afternm inplace len {len}");
1121
1122                // libsodium opens dryoc's box, directly and precomputed.
1123                let mut sodium_opened = vec![0xa5; len];
1124                let result = unsafe {
1125                    libsodium_sys::crypto_box_open_detached(
1126                        sodium_opened.as_mut_ptr(),
1127                        ciphertext.as_ptr(),
1128                        mac.as_ptr(),
1129                        len as u64,
1130                        nonce.as_ptr(),
1131                        sender_pk.as_ptr(),
1132                        recipient_sk.as_ptr(),
1133                    )
1134                };
1135                assert_eq!(result, 0, "sodium open len {len}");
1136                assert_eq!(sodium_opened, message, "sodium open len {len}");
1137                sodium_opened.fill(0xa5);
1138                let result = unsafe {
1139                    libsodium_sys::crypto_box_open_detached_afternm(
1140                        sodium_opened.as_mut_ptr(),
1141                        ciphertext.as_ptr(),
1142                        mac.as_ptr(),
1143                        len as u64,
1144                        nonce.as_ptr(),
1145                        sodium_key.as_ptr(),
1146                    )
1147                };
1148                assert_eq!(result, 0, "sodium open afternm len {len}");
1149                assert_eq!(sodium_opened, message, "sodium open afternm len {len}");
1150            }
1151        }
1152
1153        #[test]
1154        fn test_crypto_box_easy() {
1155            for i in 0..20 {
1156                use base64::Engine as _;
1157                use base64::engine::general_purpose;
1158
1159                use crate::native_test_util::{box_easy, box_open_easy};
1160
1161                let (sender_pk, sender_sk) = crypto_box_keypair();
1162                let (recipient_pk, recipient_sk) = crypto_box_keypair();
1163                let nonce = Nonce::generate();
1164                let words = vec!["hello1".to_string(); i];
1165                let message = words.join(" :D ");
1166                let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_MACBYTES];
1167                crypto_box_easy(
1168                    &mut ciphertext,
1169                    message.as_bytes(),
1170                    &nonce,
1171                    &recipient_pk,
1172                    &sender_sk,
1173                )
1174                .expect("encrypt failed");
1175
1176                let so_ciphertext = box_easy(message.as_bytes(), &nonce, &recipient_pk, &sender_sk);
1177
1178                assert_eq!(
1179                    general_purpose::STANDARD_NO_PAD.encode(&ciphertext),
1180                    general_purpose::STANDARD_NO_PAD.encode(&so_ciphertext)
1181                );
1182
1183                let mut m = vec![0u8; ciphertext.len() - CRYPTO_BOX_MACBYTES];
1184                crypto_box_open_easy(
1185                    &mut m,
1186                    ciphertext.as_slice(),
1187                    &nonce,
1188                    &sender_pk,
1189                    &recipient_sk,
1190                )
1191                .expect("decrypt failed");
1192                let so_m = box_open_easy(ciphertext.as_slice(), &nonce, &recipient_pk, &sender_sk)
1193                    .unwrap();
1194
1195                assert_eq!(m, message.as_bytes());
1196                assert_eq!(m, so_m);
1197            }
1198        }
1199
1200        #[test]
1201        fn test_crypto_box_easy_inplace() {
1202            for i in 0..20 {
1203                use base64::Engine as _;
1204                use base64::engine::general_purpose;
1205
1206                use crate::native_test_util::{box_easy, box_open_easy};
1207
1208                let (sender_pk, sender_sk) = crypto_box_keypair();
1209                let (recipient_pk, recipient_sk) = crypto_box_keypair();
1210                let nonce = Nonce::generate();
1211                let words = vec!["hello1".to_string(); i];
1212                let message: Vec<u8> = words.join(" :D ").as_bytes().to_vec();
1213                let message_copy = message.clone();
1214
1215                let mut ciphertext = message.clone();
1216                ciphertext.resize(message.len() + CRYPTO_BOX_MACBYTES, 0);
1217                crypto_box_easy_inplace(&mut ciphertext, &nonce, &recipient_pk, &sender_sk)
1218                    .expect("encrypt failed");
1219                let so_ciphertext =
1220                    box_easy(message_copy.as_slice(), &nonce, &recipient_pk, &sender_sk);
1221
1222                assert_eq!(
1223                    general_purpose::STANDARD_NO_PAD.encode(&ciphertext),
1224                    general_purpose::STANDARD_NO_PAD.encode(&so_ciphertext)
1225                );
1226
1227                let mut ciphertext_clone = ciphertext.clone();
1228                crypto_box_open_easy_inplace(
1229                    &mut ciphertext_clone,
1230                    &nonce,
1231                    &sender_pk,
1232                    &recipient_sk,
1233                )
1234                .expect("decrypt failed");
1235                ciphertext_clone.resize(message.len(), 0);
1236
1237                let so_m = box_open_easy(ciphertext.as_slice(), &nonce, &recipient_pk, &sender_sk)
1238                    .expect("decrypt failed");
1239
1240                assert_eq!(
1241                    general_purpose::STANDARD_NO_PAD.encode(&ciphertext_clone),
1242                    general_purpose::STANDARD_NO_PAD.encode(&message_copy)
1243                );
1244                assert_eq!(
1245                    general_purpose::STANDARD_NO_PAD.encode(&so_m),
1246                    general_purpose::STANDARD_NO_PAD.encode(&message_copy)
1247                );
1248            }
1249        }
1250
1251        #[test]
1252        #[cfg(feature = "alloc")]
1253        fn test_crypto_box_seed_keypair() {
1254            use base64::Engine as _;
1255            use base64::engine::general_purpose;
1256
1257            use crate::native_test_util::box_seed_keypair;
1258
1259            for _ in 0..10 {
1260                let seed: [u8; CRYPTO_BOX_SEEDBYTES] = randombytes_buf(CRYPTO_BOX_SEEDBYTES)
1261                    .try_into()
1262                    .expect("seed length");
1263
1264                let (pk, sk) = crypto_box_seed_keypair(&seed);
1265                let (so_pk, so_sk) = box_seed_keypair(&seed);
1266
1267                assert_eq!(
1268                    general_purpose::STANDARD_NO_PAD.encode(pk),
1269                    general_purpose::STANDARD_NO_PAD.encode(so_pk)
1270                );
1271                assert_eq!(
1272                    general_purpose::STANDARD_NO_PAD.encode(sk),
1273                    general_purpose::STANDARD_NO_PAD.encode(so_sk)
1274                );
1275            }
1276        }
1277
1278        #[test]
1279        fn test_crypto_box_seal() {
1280            for i in 0..20 {
1281                use crate::native_test_util::box_seal_open;
1282
1283                let (recipient_pk, recipient_sk) = crypto_box_keypair();
1284                let words = vec!["hello1".to_string(); i];
1285                let message = words.join(" :D ");
1286                let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_SEALBYTES];
1287                crypto_box_seal(&mut ciphertext, message.as_bytes(), &recipient_pk)
1288                    .expect("encrypt failed");
1289
1290                let mut m = vec![0u8; ciphertext.len() - CRYPTO_BOX_SEALBYTES];
1291                crypto_box_seal_open(&mut m, ciphertext.as_slice(), &recipient_pk, &recipient_sk)
1292                    .expect("decrypt failed");
1293                let so_m =
1294                    box_seal_open(ciphertext.as_slice(), &recipient_pk, &recipient_sk).unwrap();
1295
1296                assert_eq!(m, message.as_bytes());
1297                assert_eq!(m, so_m);
1298            }
1299        }
1300
1301        #[test]
1302        fn test_crypto_box_seal_open() {
1303            for i in 0..20 {
1304                use crate::native_test_util::{box_seal, box_seal_open};
1305
1306                let (recipient_pk, recipient_sk) = crypto_box_keypair();
1307                let words = vec!["hello1".to_string(); i];
1308                let message = words.join(" :D ");
1309                let so_ciphertext = box_seal(message.as_bytes(), &recipient_pk);
1310
1311                let mut m = vec![0u8; so_ciphertext.len() - CRYPTO_BOX_SEALBYTES];
1312                crypto_box_seal_open(
1313                    &mut m,
1314                    so_ciphertext.as_slice(),
1315                    &recipient_pk,
1316                    &recipient_sk,
1317                )
1318                .expect("decrypt failed");
1319                let so_m =
1320                    box_seal_open(so_ciphertext.as_slice(), &recipient_pk, &recipient_sk).unwrap();
1321
1322                assert_eq!(m, message.as_bytes());
1323                assert_eq!(m, so_m);
1324            }
1325        }
1326    }
1327}