dryoc/classic/
crypto_box.rs

1//! # Authenticated public-key cryptography functions
2//!
3//! Implements libsodium's public-key authenticated crypto boxes.
4//!
5//! For details, refer to [libsodium docs](https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption).
6//!
7//! ## Classic API example
8//!
9//! ```
10//! use dryoc::classic::crypto_box::*;
11//! use dryoc::constants::CRYPTO_BOX_MACBYTES;
12//! use dryoc::types::*;
13//!
14//! // Create a random sender keypair
15//! let (sender_pk, sender_sk) = crypto_box_keypair();
16//!
17//! // Create a random recipient keypair
18//! let (recipient_pk, recipient_sk) = crypto_box_keypair();
19//!
20//! // Generate a random nonce
21//! let nonce = Nonce::gen();
22//!
23//! let message = "hello".as_bytes();
24//! // Encrypt message
25//! let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_MACBYTES];
26//! crypto_box_easy(&mut ciphertext, message, &nonce, &recipient_pk, &sender_sk)
27//!     .expect("encrypt failed");
28//!
29//! // Decrypt message
30//! let mut decrypted_message = vec![0u8; ciphertext.len() - CRYPTO_BOX_MACBYTES];
31//! crypto_box_open_easy(
32//!     &mut decrypted_message,
33//!     &ciphertext,
34//!     &nonce,
35//!     &sender_pk,
36//!     &recipient_sk,
37//! )
38//! .expect("decrypt failed");
39//!
40//! assert_eq!(message, decrypted_message);
41//! ```
42
43use zeroize::Zeroize;
44
45use super::crypto_generichash::{
46    crypto_generichash_final, crypto_generichash_init, crypto_generichash_update,
47};
48use crate::classic::crypto_box_impl::*;
49use crate::classic::crypto_secretbox::*;
50use crate::classic::crypto_secretbox_impl::*;
51use crate::constants::*;
52use crate::error::Error;
53use crate::types::*;
54
55/// Crypto box message authentication code.
56pub type Mac = [u8; CRYPTO_BOX_MACBYTES];
57
58/// Nonce for crypto boxes.
59pub type Nonce = [u8; CRYPTO_BOX_NONCEBYTES];
60/// Public key for public key authenticated crypto boxes.
61pub type PublicKey = [u8; CRYPTO_BOX_PUBLICKEYBYTES];
62/// Secret key for public key authenticated crypto boxes.
63pub type SecretKey = [u8; CRYPTO_BOX_SECRETKEYBYTES];
64
65/// In-place variant of [`crypto_box_keypair`]
66pub fn crypto_box_keypair_inplace(public_key: &mut PublicKey, secret_key: &mut SecretKey) {
67    crypto_box_curve25519xsalsa20poly1305_keypair_inplace(public_key, secret_key)
68}
69
70/// In-place variant of [`crypto_box_seed_keypair`]
71pub fn crypto_box_seed_keypair_inplace(
72    public_key: &mut PublicKey,
73    secret_key: &mut SecretKey,
74    seed: &[u8],
75) {
76    crypto_box_curve25519xsalsa20poly1305_seed_keypair_inplace(public_key, secret_key, seed)
77}
78
79/// Generates a public/secret key pair using OS provided data using
80/// [`rand_core::OsRng`].
81pub fn crypto_box_keypair() -> (PublicKey, SecretKey) {
82    crypto_box_curve25519xsalsa20poly1305_keypair()
83}
84
85/// Deterministically derives a keypair from `seed`, which can be of arbitrary
86/// length.
87///
88/// Compatible with libsodium's `crypto_box_seed_keypair`.
89pub fn crypto_box_seed_keypair(seed: &[u8]) -> (PublicKey, SecretKey) {
90    crypto_box_curve25519xsalsa20poly1305_seed_keypair(seed)
91}
92
93/// Computes a shared secret for the given `public_key` and `private_key`.
94/// Resulting shared secret can be used with the precalculation interface.
95///
96/// Compatible with libsodium's `crypto_box_beforenm`.
97pub fn crypto_box_beforenm(public_key: &PublicKey, secret_key: &SecretKey) -> Key {
98    crypto_box_curve25519xsalsa20poly1305_beforenm(public_key, secret_key)
99}
100
101/// Precalculation variant of
102/// [`crypto_box_easy`].
103///
104/// Compatible with libsodium's `crypto_box_detached_afternm`.
105pub fn crypto_box_detached_afternm(
106    ciphertext: &mut [u8],
107    mac: &mut Mac,
108    message: &[u8],
109    nonce: &Nonce,
110    key: &Key,
111) {
112    crypto_secretbox_detached(ciphertext, mac, message, nonce, key)
113}
114
115/// In-place variant of [`crypto_box_detached_afternm`].
116pub fn crypto_box_detached_afternm_inplace(
117    ciphertext: &mut [u8],
118    mac: &mut Mac,
119    nonce: &Nonce,
120    key: &Key,
121) {
122    crypto_secretbox_detached_inplace(ciphertext, mac, nonce, key)
123}
124
125/// Detached variant of [`crypto_box_easy`].
126///
127/// Compatible with libsodium's `crypto_box_detached`.
128pub fn crypto_box_detached(
129    ciphertext: &mut [u8],
130    mac: &mut Mac,
131    message: &[u8],
132    nonce: &Nonce,
133    recipient_public_key: &PublicKey,
134    sender_secret_key: &SecretKey,
135) {
136    let mut key = crypto_box_beforenm(recipient_public_key, sender_secret_key);
137
138    crypto_box_detached_afternm(ciphertext, mac, message, nonce, &key);
139
140    key.zeroize();
141}
142
143/// In-place variant of [`crypto_box_detached`].
144pub fn crypto_box_detached_inplace(
145    message: &mut [u8],
146    mac: &mut Mac,
147    nonce: &Nonce,
148    recipient_public_key: &PublicKey,
149    sender_secret_key: &SecretKey,
150) -> Result<(), Error> {
151    let mut key = crypto_box_beforenm(recipient_public_key, sender_secret_key);
152
153    crypto_box_detached_afternm_inplace(message, mac, nonce, &key);
154
155    key.zeroize();
156
157    Ok(())
158}
159/// Encrypts a message in a box.
160///
161/// Encrypts `message` with recipient's public key `recipient_public_key`,
162/// sender's secret key `sender_secret_key`, and `nonce`. The result is placed
163/// into `ciphertext` which must be the length of the message plus
164/// [`CRYPTO_BOX_MACBYTES`] bytes, for the message tag.
165///
166/// Compatible with libsodium's `crypto_box_easy`.
167pub fn crypto_box_easy(
168    ciphertext: &mut [u8],
169    message: &[u8],
170    nonce: &Nonce,
171    recipient_public_key: &PublicKey,
172    sender_secret_key: &SecretKey,
173) -> Result<(), Error> {
174    if ciphertext.len() < CRYPTO_BOX_MACBYTES {
175        Err(dryoc_error!(format!(
176            "ciphertext length {} less than minimum {}",
177            ciphertext.len(),
178            CRYPTO_BOX_MACBYTES
179        )))
180    } else if message.len() > CRYPTO_BOX_MESSAGEBYTES_MAX {
181        Err(dryoc_error!(format!(
182            "message length {} exceeds max message length {}",
183            message.len(),
184            CRYPTO_BOX_MESSAGEBYTES_MAX
185        )))
186    } else {
187        let (mac, ciphertext) = ciphertext.split_at_mut(CRYPTO_BOX_MACBYTES);
188        let mac = MutByteArray::as_mut_array(mac);
189        crypto_box_detached(
190            ciphertext,
191            mac,
192            message,
193            nonce,
194            recipient_public_key,
195            sender_secret_key,
196        );
197
198        Ok(())
199    }
200}
201
202pub(crate) fn crypto_box_seal_nonce(nonce: &mut Nonce, epk: &PublicKey, rpk: &SecretKey) {
203    let mut state = crypto_generichash_init(None, CRYPTO_BOX_NONCEBYTES).expect("state");
204    crypto_generichash_update(&mut state, epk);
205    crypto_generichash_update(&mut state, rpk);
206    crypto_generichash_final(state, nonce).expect("hash error");
207}
208
209/// Encrypts and seals a message in a box.
210///
211/// Encrypts `message` with recipient's public key `recipient_public_key`, using
212/// an ephemeral keypair and nonce. The length of `ciphertext` must be the
213/// length of the message plus [`CRYPTO_BOX_SEALBYTES`] bytes for the message
214/// tag and ephemeral public key.
215///
216/// Compatible with libsodium's `crypto_box_seal`.
217pub fn crypto_box_seal(
218    ciphertext: &mut [u8],
219    message: &[u8],
220    recipient_public_key: &PublicKey,
221) -> Result<(), Error> {
222    if ciphertext.len() < message.len() + CRYPTO_BOX_SEALBYTES {
223        Err(dryoc_error!(format!(
224            "ciphertext length invalid ({} != {}",
225            ciphertext.len(),
226            message.len() + CRYPTO_BOX_SEALBYTES,
227        )))
228    } else {
229        let mut nonce = Nonce::new_byte_array();
230        let (mut epk, mut esk) = crypto_box_keypair();
231        crypto_box_seal_nonce(&mut nonce, &epk, recipient_public_key);
232
233        crypto_box_easy(
234            &mut ciphertext[CRYPTO_BOX_PUBLICKEYBYTES..],
235            message,
236            &nonce,
237            recipient_public_key,
238            &esk,
239        )?;
240
241        ciphertext[..CRYPTO_BOX_PUBLICKEYBYTES].copy_from_slice(&epk);
242
243        epk.zeroize();
244        esk.zeroize();
245        nonce.zeroize();
246
247        Ok(())
248    }
249}
250
251/// Encrypts a message in-place in a box.
252///
253/// Encrypts `message` with recipient's public key `recipient_public_key` and
254/// sender's secret key `sender_secret_key` using `nonce` in-place in `data`,
255/// without allocated additional memory for the message.
256///
257/// The caller of this function is responsible for allocating `data` such that
258/// there's enough capacity for the message plus the additional
259/// [`CRYPTO_BOX_MACBYTES`] bytes for the authentication tag.
260///
261/// For this reason, the last [`CRYPTO_BOX_MACBYTES`] bytes from the input
262/// is ignored. The length of `data` should be the length of your message plus
263/// [`CRYPTO_BOX_MACBYTES`] bytes.
264pub fn crypto_box_easy_inplace(
265    data: &mut [u8],
266    nonce: &Nonce,
267    recipient_public_key: &PublicKey,
268    sender_secret_key: &SecretKey,
269) -> Result<(), Error> {
270    if data.len() < CRYPTO_BOX_MACBYTES {
271        Err(dryoc_error!(format!(
272            "Message length {} less than {}, impossibly small",
273            data.len(),
274            CRYPTO_BOX_MACBYTES
275        )))
276    } else if data.len() > CRYPTO_BOX_MESSAGEBYTES_MAX {
277        Err(dryoc_error!(format!(
278            "Message length {} exceeds max message length {}",
279            data.len(),
280            CRYPTO_BOX_MESSAGEBYTES_MAX
281        )))
282    } else {
283        data.rotate_right(CRYPTO_BOX_MACBYTES);
284
285        let (mac, data) = data.split_at_mut(CRYPTO_BOX_MACBYTES);
286        let mac = MutByteArray::as_mut_array(mac);
287
288        crypto_box_detached_inplace(data, mac, nonce, recipient_public_key, sender_secret_key)?;
289
290        Ok(())
291    }
292}
293
294/// Precalculation variant of [`crypto_box_open_easy`].
295///
296/// Compatible with libsodium's `crypto_box_open_detached_afternm`.
297pub fn crypto_box_open_detached_afternm(
298    message: &mut [u8],
299    mac: &Mac,
300    ciphertext: &[u8],
301    nonce: &Nonce,
302    key: &Key,
303) -> Result<(), Error> {
304    crypto_secretbox_open_detached(message, mac, ciphertext, nonce, key)
305}
306
307/// In-place variant of [`crypto_box_open_detached_afternm`].
308pub fn crypto_box_open_detached_afternm_inplace(
309    data: &mut [u8],
310    mac: &Mac,
311    nonce: &Nonce,
312    key: &Key,
313) -> Result<(), Error> {
314    crypto_secretbox_open_detached_inplace(data, mac, nonce, key)
315}
316
317/// Detached variant of [`crypto_box_open_easy`].
318///
319/// Compatible with libsodium's `crypto_box_open_detached`.
320pub fn crypto_box_open_detached(
321    message: &mut [u8],
322    mac: &Mac,
323    ciphertext: &[u8],
324    nonce: &Nonce,
325    recipient_public_key: &PublicKey,
326    sender_secret_key: &SecretKey,
327) -> Result<(), Error> {
328    let mut key = crypto_box_beforenm(recipient_public_key, sender_secret_key);
329
330    crypto_box_open_detached_afternm(message, mac, ciphertext, nonce, &key)?;
331
332    key.zeroize();
333
334    Ok(())
335}
336
337/// In-place variant of [`crypto_box_open_detached`].
338pub fn crypto_box_open_detached_inplace(
339    data: &mut [u8],
340    mac: &Mac,
341    nonce: &Nonce,
342    recipient_public_key: &PublicKey,
343    sender_secret_key: &SecretKey,
344) -> Result<(), Error> {
345    let mut key = crypto_box_beforenm(recipient_public_key, sender_secret_key);
346
347    crypto_box_open_detached_afternm_inplace(data, mac, nonce, &key)?;
348
349    key.zeroize();
350
351    Ok(())
352}
353
354/// Decrypts `ciphertext` with recipient's secret key `recipient_secret_key` and
355/// sender's public key `sender_public_key` using `nonce`.
356///
357/// Compatible with libsodium's `crypto_box_open_easy`.
358pub fn crypto_box_open_easy(
359    message: &mut [u8],
360    ciphertext: &[u8],
361    nonce: &Nonce,
362    sender_public_key: &PublicKey,
363    recipient_secret_key: &SecretKey,
364) -> Result<(), Error> {
365    if ciphertext.len() < CRYPTO_BOX_MACBYTES {
366        Err(dryoc_error!(format!(
367            "Impossibly small box ({} < {}",
368            ciphertext.len(),
369            CRYPTO_BOX_MACBYTES
370        )))
371    } else {
372        let (mac, ciphertext) = ciphertext.split_at(CRYPTO_BOX_MACBYTES);
373        let mac = ByteArray::as_array(mac);
374
375        crypto_box_open_detached(
376            message,
377            mac,
378            ciphertext,
379            nonce,
380            sender_public_key,
381            recipient_secret_key,
382        )
383    }
384}
385
386/// Decrypts a sealed box.
387///
388/// Decrypts a sealed box from `ciphertext` with recipient's secret key
389/// `recipient_secret_key`, placing the result into `message`. The nonce and
390/// public are derived from `ciphertext`. `message` length should be equal to
391/// the length of `ciphertext` minus [`CRYPTO_BOX_SEALBYTES`] bytes for the
392/// message tag and ephemeral public key.
393///
394/// Compatible with libsodium's `crypto_box_seal_open`.
395pub fn crypto_box_seal_open(
396    message: &mut [u8],
397    ciphertext: &[u8],
398    recipient_public_key: &PublicKey,
399    recipient_secret_key: &SecretKey,
400) -> Result<(), Error> {
401    if ciphertext.len() < CRYPTO_BOX_SEALBYTES {
402        Err(dryoc_error!(format!(
403            "Impossibly small box ({} < {}",
404            ciphertext.len(),
405            CRYPTO_BOX_SEALBYTES,
406        )))
407    } else if message.len() != ciphertext.len() - CRYPTO_BOX_SEALBYTES {
408        Err(dryoc_error!(format!(
409            "message length invalid ({} != {}",
410            message.len(),
411            ciphertext.len() - CRYPTO_BOX_SEALBYTES,
412        )))
413    } else {
414        let mut nonce = Nonce::new_byte_array();
415        let mut epk = PublicKey::new_byte_array();
416        epk.copy_from_slice(&ciphertext[..CRYPTO_BOX_PUBLICKEYBYTES]);
417
418        crypto_box_seal_nonce(&mut nonce, &epk, recipient_public_key);
419
420        crypto_box_open_easy(
421            message,
422            &ciphertext[CRYPTO_BOX_PUBLICKEYBYTES..],
423            &nonce,
424            &epk,
425            recipient_secret_key,
426        )
427    }
428}
429
430/// Decrypts a sealed box in-place.
431///
432/// Decrypts `ciphertext` with recipient's secret key `recipient_secret_key` and
433/// sender's public key `sender_public_key` with `nonce` in-place in `data`,
434/// without allocated additional memory for the message.
435///
436/// The caller of this function is responsible for allocating `data` such that
437/// there's enough capacity for the message plus the additional
438/// [`CRYPTO_BOX_MACBYTES`] bytes for the authentication tag.
439///
440/// After opening the box, the last [`CRYPTO_BOX_MACBYTES`] bytes can be
441/// discarded or ignored at the caller's preference.
442pub fn crypto_box_open_easy_inplace(
443    data: &mut [u8],
444    nonce: &Nonce,
445    sender_public_key: &PublicKey,
446    recipient_secret_key: &SecretKey,
447) -> Result<(), Error> {
448    if data.len() < CRYPTO_BOX_MACBYTES {
449        Err(dryoc_error!(format!(
450            "Impossibly small box ({} < {}",
451            data.len(),
452            CRYPTO_BOX_MACBYTES
453        )))
454    } else {
455        let (mac, d) = data.split_at_mut(CRYPTO_BOX_MACBYTES);
456        let mac = ByteArray::as_array(mac);
457
458        crypto_box_open_detached_inplace(d, mac, nonce, sender_public_key, recipient_secret_key)?;
459
460        data.rotate_left(CRYPTO_BOX_MACBYTES);
461
462        Ok(())
463    }
464}
465
466#[cfg(test)]
467mod tests {
468    use super::*;
469    use crate::rng::*;
470
471    #[test]
472    fn test_crypto_box_easy() {
473        for i in 0..20 {
474            use base64::Engine as _;
475            use base64::engine::general_purpose;
476            use sodiumoxide::crypto::box_;
477            use sodiumoxide::crypto::box_::{Nonce as SONonce, PublicKey, SecretKey};
478
479            let (sender_pk, sender_sk) = crypto_box_keypair();
480            let (recipient_pk, recipient_sk) = crypto_box_keypair();
481            let nonce = Nonce::gen();
482            let words = vec!["hello1".to_string(); i];
483            let message = words.join(" :D ");
484            let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_MACBYTES];
485            crypto_box_easy(
486                &mut ciphertext,
487                message.as_bytes(),
488                &nonce,
489                &recipient_pk,
490                &sender_sk,
491            )
492            .expect("encrypt failed");
493
494            let so_ciphertext = box_::seal(
495                message.as_bytes(),
496                &SONonce::from_slice(&nonce).unwrap(),
497                &PublicKey::from_slice(&recipient_pk).unwrap(),
498                &SecretKey::from_slice(&sender_sk).unwrap(),
499            );
500
501            assert_eq!(
502                general_purpose::STANDARD_NO_PAD.encode(&ciphertext),
503                general_purpose::STANDARD_NO_PAD.encode(&so_ciphertext)
504            );
505
506            let mut m = vec![0u8; ciphertext.len() - CRYPTO_BOX_MACBYTES];
507            crypto_box_open_easy(
508                &mut m,
509                ciphertext.as_slice(),
510                &nonce,
511                &sender_pk,
512                &recipient_sk,
513            )
514            .expect("decrypt failed");
515            let so_m = box_::open(
516                ciphertext.as_slice(),
517                &SONonce::from_slice(&nonce).unwrap(),
518                &PublicKey::from_slice(&recipient_pk).unwrap(),
519                &SecretKey::from_slice(&sender_sk).unwrap(),
520            )
521            .unwrap();
522
523            assert_eq!(m, message.as_bytes());
524            assert_eq!(m, so_m);
525        }
526    }
527
528    #[test]
529    fn test_crypto_box_easy_inplace() {
530        for i in 0..20 {
531            use base64::Engine as _;
532            use base64::engine::general_purpose;
533            use sodiumoxide::crypto::box_;
534            use sodiumoxide::crypto::box_::{Nonce as SONonce, PublicKey, SecretKey};
535
536            let (sender_pk, sender_sk) = crypto_box_keypair();
537            let (recipient_pk, recipient_sk) = crypto_box_keypair();
538            let nonce = Nonce::gen();
539            let words = vec!["hello1".to_string(); i];
540            let message: Vec<u8> = words.join(" :D ").as_bytes().to_vec();
541            let message_copy = message.clone();
542
543            let mut ciphertext = message.clone();
544            ciphertext.resize(message.len() + CRYPTO_BOX_MACBYTES, 0);
545            crypto_box_easy_inplace(&mut ciphertext, &nonce, &recipient_pk, &sender_sk)
546                .expect("encrypt failed");
547            let so_ciphertext = box_::seal(
548                message_copy.as_slice(),
549                &SONonce::from_slice(&nonce).unwrap(),
550                &PublicKey::from_slice(&recipient_pk).unwrap(),
551                &SecretKey::from_slice(&sender_sk).unwrap(),
552            );
553
554            assert_eq!(
555                general_purpose::STANDARD_NO_PAD.encode(&ciphertext),
556                general_purpose::STANDARD_NO_PAD.encode(&so_ciphertext)
557            );
558
559            let mut ciphertext_clone = ciphertext.clone();
560            crypto_box_open_easy_inplace(&mut ciphertext_clone, &nonce, &sender_pk, &recipient_sk)
561                .expect("decrypt failed");
562            ciphertext_clone.resize(message.len(), 0);
563
564            let so_m = box_::open(
565                ciphertext.as_slice(),
566                &SONonce::from_slice(&nonce).unwrap(),
567                &PublicKey::from_slice(&recipient_pk).unwrap(),
568                &SecretKey::from_slice(&sender_sk).unwrap(),
569            )
570            .expect("decrypt failed");
571
572            assert_eq!(
573                general_purpose::STANDARD_NO_PAD.encode(&ciphertext_clone),
574                general_purpose::STANDARD_NO_PAD.encode(&message_copy)
575            );
576            assert_eq!(
577                general_purpose::STANDARD_NO_PAD.encode(&so_m),
578                general_purpose::STANDARD_NO_PAD.encode(&message_copy)
579            );
580        }
581    }
582
583    #[test]
584    fn test_crypto_box_easy_invalid() {
585        for _ in 0..20 {
586            let (sender_pk, _sender_sk) = crypto_box_keypair();
587            let (_recipient_pk, recipient_sk) = crypto_box_keypair();
588            let nonce = Nonce::gen();
589
590            let mut ciphertext: Vec<u8> = vec![];
591            let message: Vec<u8> = vec![];
592
593            crypto_box_open_easy(&mut ciphertext, &message, &nonce, &sender_pk, &recipient_sk)
594                .expect_err("expected an error");
595        }
596    }
597    #[test]
598    fn test_crypto_box_easy_inplace_invalid() {
599        for _ in 0..20 {
600            use base64::Engine as _;
601            use base64::engine::general_purpose;
602
603            let (sender_pk, _sender_sk) = crypto_box_keypair();
604            let (_recipient_pk, recipient_sk) = crypto_box_keypair();
605            let nonce = Nonce::gen();
606
607            let mut ciphertext: Vec<u8> = vec![];
608
609            crypto_box_open_easy_inplace(&mut ciphertext, &nonce, &sender_pk, &recipient_sk)
610                .expect_err("expected an error");
611
612            ciphertext.resize(1024, 0);
613            copy_randombytes(ciphertext.as_mut_slice());
614            let ciphertext_copy = ciphertext.clone();
615
616            crypto_box_open_easy_inplace(&mut ciphertext, &nonce, &sender_pk, &recipient_sk)
617                .expect_err("expected an error");
618
619            assert_eq!(ciphertext.len(), ciphertext_copy.len());
620            assert_eq!(
621                general_purpose::STANDARD_NO_PAD.encode(&ciphertext[0..CRYPTO_BOX_MACBYTES]),
622                general_purpose::STANDARD_NO_PAD.encode(&ciphertext_copy[0..CRYPTO_BOX_MACBYTES])
623            );
624        }
625    }
626
627    #[test]
628    fn test_crypto_box_seed_keypair() {
629        use base64::Engine as _;
630        use base64::engine::general_purpose;
631        use sodiumoxide::crypto::box_::{Seed, keypair_from_seed};
632
633        for _ in 0..10 {
634            let seed = randombytes_buf(CRYPTO_BOX_SEEDBYTES);
635
636            let (pk, sk) = crypto_box_seed_keypair(&seed);
637            let (so_pk, so_sk) = keypair_from_seed(&Seed::from_slice(&seed).unwrap());
638
639            assert_eq!(
640                general_purpose::STANDARD_NO_PAD.encode(pk),
641                general_purpose::STANDARD_NO_PAD.encode(so_pk.as_ref())
642            );
643            assert_eq!(
644                general_purpose::STANDARD_NO_PAD.encode(sk),
645                general_purpose::STANDARD_NO_PAD.encode(so_sk.as_ref())
646            );
647        }
648    }
649
650    #[test]
651    fn test_crypto_box_seal() {
652        for i in 0..20 {
653            use sodiumoxide::crypto::box_::{PublicKey, SecretKey};
654            use sodiumoxide::crypto::sealedbox::curve25519blake2bxsalsa20poly1305;
655
656            let (recipient_pk, recipient_sk) = crypto_box_keypair();
657            let words = vec!["hello1".to_string(); i];
658            let message = words.join(" :D ");
659            let mut ciphertext = vec![0u8; message.len() + CRYPTO_BOX_SEALBYTES];
660            crypto_box_seal(&mut ciphertext, message.as_bytes(), &recipient_pk)
661                .expect("encrypt failed");
662
663            let mut m = vec![0u8; ciphertext.len() - CRYPTO_BOX_SEALBYTES];
664            crypto_box_seal_open(&mut m, ciphertext.as_slice(), &recipient_pk, &recipient_sk)
665                .expect("decrypt failed");
666            let so_m = curve25519blake2bxsalsa20poly1305::open(
667                ciphertext.as_slice(),
668                &PublicKey::from_slice(&recipient_pk).unwrap(),
669                &SecretKey::from_slice(&recipient_sk).unwrap(),
670            )
671            .unwrap();
672
673            assert_eq!(m, message.as_bytes());
674            assert_eq!(m, so_m);
675        }
676    }
677
678    #[test]
679    fn test_crypto_box_seal_open() {
680        for i in 0..20 {
681            use sodiumoxide::crypto::box_::{PublicKey, SecretKey};
682            use sodiumoxide::crypto::sealedbox::curve25519blake2bxsalsa20poly1305;
683
684            let (recipient_pk, recipient_sk) = crypto_box_keypair();
685            let words = vec!["hello1".to_string(); i];
686            let message = words.join(" :D ");
687            let so_ciphertext = curve25519blake2bxsalsa20poly1305::seal(
688                message.as_bytes(),
689                &PublicKey::from_slice(&recipient_pk).unwrap(),
690            );
691
692            let mut m = vec![0u8; so_ciphertext.len() - CRYPTO_BOX_SEALBYTES];
693            crypto_box_seal_open(
694                &mut m,
695                so_ciphertext.as_slice(),
696                &recipient_pk,
697                &recipient_sk,
698            )
699            .expect("decrypt failed");
700            let so_m = curve25519blake2bxsalsa20poly1305::open(
701                so_ciphertext.as_slice(),
702                &PublicKey::from_slice(&recipient_pk).unwrap(),
703                &SecretKey::from_slice(&recipient_sk).unwrap(),
704            )
705            .unwrap();
706
707            assert_eq!(m, message.as_bytes());
708            assert_eq!(m, so_m);
709        }
710    }
711}