libsodium_rs/crypto_box/mod.rs
1//! # Public-Key Cryptography
2//!
3//! This module provides functions for authenticated encryption using public-key cryptography.
4//! It implements the X25519-XSalsa20-Poly1305 construction, which combines the X25519
5//! key exchange with the XSalsa20 stream cipher and the Poly1305 message authentication code.
6//!
7//! This construction is also known as NaCl's crypto_box.
8//!
9//! ## Features
10//!
11//! - Authenticated encryption with public-key cryptography
12//! - Protection against tampering and forgery
13//! - Secure key exchange using X25519 elliptic curve Diffie-Hellman
14//! - Strong encryption using XSalsa20 stream cipher
15//! - Message authentication using Poly1305 MAC
16//! - Forward secrecy when using ephemeral keys
17//!
18//! ## Usage
19//!
20//! ```rust
21//! use libsodium_rs as sodium;
22//! use sodium::crypto_box;
23//! use sodium::random;
24//! use sodium::ensure_init;
25//!
26//! // Initialize libsodium
27//! ensure_init().expect("Failed to initialize libsodium");
28//!
29//! // Generate key pairs for Alice and Bob
30//! let alice_keypair = crypto_box::KeyPair::generate();
31//! let alice_pk = alice_keypair.public_key;
32//! let alice_sk = alice_keypair.secret_key;
33//! let bob_keypair = crypto_box::KeyPair::generate();
34//! let bob_pk = bob_keypair.public_key;
35//! let bob_sk = bob_keypair.secret_key;
36//!
37//! // Generate a random nonce
38//! let nonce = crypto_box::Nonce::generate();
39//!
40//! // Alice encrypts a message for Bob
41//! let message = b"Hello, Bob! This is a secret message.";
42//! let ciphertext = crypto_box::seal(message, &nonce, &bob_pk, &alice_sk).unwrap();
43//!
44//! // Bob decrypts the message from Alice
45//! let decrypted = crypto_box::open(&ciphertext, &nonce, &alice_pk, &bob_sk).unwrap();
46//! assert_eq!(message, &decrypted[..]);
47//! ```
48//!
49//! ## Security Considerations
50//!
51//! - Always use a unique nonce for each encryption operation with the same key pair
52//! - For maximum security, use ephemeral keys for each communication session
53//! - The secret key should be kept confidential
54//! - The public key can be shared freely
55//! - This implementation uses constant-time operations to prevent timing attacks
56//! - For long-term security, consider using the XChaCha20-Poly1305 variant in the
57//! `curve25519xchacha20poly1305` submodule
58//! - Be aware that X25519 is based on Curve25519, which has a cofactor of 8
59//! - The shared secret established through X25519 is automatically hashed before being used
60//! as an encryption key
61//! - The encryption provides authenticated encryption with associated data (AEAD)
62//! which means it protects both confidentiality and integrity
63
64use crate::{Result, SodiumError};
65use ct_codecs;
66use ct_codecs::Encoder;
67use libsodium_sys;
68
69/// Number of bytes in a public key (32)
70///
71/// The public key is used for encryption and can be shared publicly.
72/// It is based on the X25519 elliptic curve cryptography.
73pub const PUBLICKEYBYTES: usize = libsodium_sys::crypto_box_PUBLICKEYBYTES as usize;
74
75/// Number of bytes in a secret key (32)
76///
77/// The secret key is used for decryption and should be kept private.
78/// It is based on the X25519 elliptic curve cryptography.
79pub const SECRETKEYBYTES: usize = libsodium_sys::crypto_box_SECRETKEYBYTES as usize;
80
81/// Number of bytes in a nonce (24)
82///
83/// The nonce is a unique value used for each encryption operation.
84/// It should never be reused with the same key pair.
85pub const NONCEBYTES: usize = libsodium_sys::crypto_box_NONCEBYTES as usize;
86
87/// A nonce for use with crypto_box functions
88///
89/// This struct represents a nonce of the appropriate size (NONCEBYTES)
90/// for use with the encryption and decryption functions in this module.
91///
92/// A nonce must be unique for each encryption operation with the same key pair.
93#[derive(Clone, Eq, PartialEq)]
94pub struct Nonce([u8; NONCEBYTES]);
95
96impl Nonce {
97 /// Generate a new random nonce
98 ///
99 /// ## Returns
100 ///
101 /// * `Nonce` - A random nonce for use with crypto_box functions
102 ///
103 /// ## Example
104 ///
105 /// ```rust
106 /// use libsodium_rs as sodium;
107 /// use sodium::crypto_box;
108 /// use sodium::ensure_init;
109 ///
110 /// // Initialize libsodium
111 /// ensure_init().expect("Failed to initialize libsodium");
112 ///
113 /// // Generate a random nonce
114 /// let nonce = crypto_box::Nonce::generate();
115 /// ```
116 pub fn generate() -> Self {
117 let mut bytes = [0u8; NONCEBYTES];
118 crate::random::fill_bytes(&mut bytes);
119 Self(bytes)
120 }
121
122 /// Create a nonce from raw bytes
123 ///
124 /// ## Arguments
125 ///
126 /// * `bytes` - Byte array of exactly NONCEBYTES length
127 ///
128 /// ## Returns
129 ///
130 /// * `Nonce` - A nonce initialized with the provided bytes
131 pub const fn from_bytes_exact(bytes: [u8; NONCEBYTES]) -> Self {
132 Self(bytes)
133 }
134
135 /// Get a reference to the underlying bytes
136 ///
137 /// ## Returns
138 ///
139 /// * `&[u8; NONCEBYTES]` - Reference to the nonce bytes
140 pub fn as_bytes(&self) -> &[u8; NONCEBYTES] {
141 &self.0
142 }
143
144 /// Get a mutable reference to the underlying bytes
145 ///
146 /// ## Returns
147 ///
148 /// * `&mut [u8; NONCEBYTES]` - Mutable reference to the nonce bytes
149 pub fn as_bytes_mut(&mut self) -> &mut [u8; NONCEBYTES] {
150 &mut self.0
151 }
152}
153
154impl AsRef<[u8]> for Nonce {
155 fn as_ref(&self) -> &[u8] {
156 &self.0
157 }
158}
159
160impl TryFrom<&[u8]> for Nonce {
161 type Error = crate::SodiumError;
162
163 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
164 if slice.len() != NONCEBYTES {
165 return Err(crate::SodiumError::InvalidNonce(format!(
166 "nonce must be exactly {NONCEBYTES} bytes"
167 )));
168 }
169
170 let mut bytes = [0u8; NONCEBYTES];
171 bytes.copy_from_slice(slice);
172 Ok(Self(bytes))
173 }
174}
175
176impl std::fmt::Debug for Nonce {
177 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178 let hex = ct_codecs::Hex::encode_to_string(&self.0[..4]).unwrap_or_default();
179 write!(f, "Nonce({hex})...")
180 }
181}
182
183/// Generate a random nonce for use with crypto_box functions (legacy function)
184///
185/// This function generates a random nonce of the appropriate size (NONCEBYTES)
186/// for use with the encryption and decryption functions in this module.
187///
188/// ## Returns
189///
190/// * `Vec<u8>` - A random nonce of length NONCEBYTES
191///
192/// ## Example
193///
194/// ```rust
195/// use libsodium_rs as sodium;
196/// use sodium::crypto_box;
197/// use sodium::ensure_init;
198///
199/// // Initialize libsodium
200/// ensure_init().expect("Failed to initialize libsodium");
201///
202/// // Generate a random nonce
203/// let nonce = crypto_box::Nonce::generate();
204/// assert_eq!(nonce.as_ref().len(), crypto_box::NONCEBYTES);
205/// ```
206///
207/// Number of bytes in a MAC (message authentication code) (16)
208///
209/// The MAC is used to verify the authenticity and integrity of the message.
210/// It is added to the ciphertext during encryption.
211pub const MACBYTES: usize = libsodium_sys::crypto_box_MACBYTES as usize;
212
213/// Number of bytes in a precomputed key (32)
214///
215/// The precomputed key is the result of the Diffie-Hellman key exchange,
216/// which can be reused for multiple encryption/decryption operations.
217pub const BEFORENMBYTES: usize = libsodium_sys::crypto_box_BEFORENMBYTES as usize;
218
219/// Number of zero bytes required for NaCl compatibility (32)
220///
221/// This is used only with the NaCl compatibility API.
222pub const ZEROBYTES: usize = libsodium_sys::crypto_box_ZEROBYTES as usize;
223
224/// Number of zero bytes required in ciphertext for NaCl compatibility (16)
225///
226/// This is used only with the NaCl compatibility API.
227pub const BOXZEROBYTES: usize = libsodium_sys::crypto_box_BOXZEROBYTES as usize;
228
229/// Number of bytes in a sealed box (48)
230///
231/// A sealed box is used for anonymous encryption, where the sender's identity is not revealed.
232pub const SEALBYTES: usize = libsodium_sys::crypto_box_SEALBYTES as usize;
233
234/// A public key for asymmetric encryption using X25519
235///
236/// This key is used for encrypting messages and can be shared publicly.
237/// It is derived from the corresponding secret key using the X25519 elliptic curve.
238///
239/// ## Properties
240///
241/// - Size: 32 bytes (256 bits)
242/// - Based on the X25519 elliptic curve
243/// - Can be safely shared with anyone
244/// - Used to encrypt messages that can only be decrypted with the corresponding secret key
245#[derive(Clone, Eq, PartialEq)]
246pub struct PublicKey([u8; PUBLICKEYBYTES]);
247
248/// A secret key for asymmetric encryption using X25519
249///
250/// This key is used for decrypting messages and must be kept private.
251/// It is randomly generated and used to derive the corresponding public key.
252///
253/// ## Properties
254///
255/// - Size: 32 bytes (256 bits)
256/// - Based on the X25519 elliptic curve
257/// - Must be kept confidential
258/// - Used to decrypt messages that were encrypted with the corresponding public key
259#[derive(Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
260pub struct SecretKey([u8; SECRETKEYBYTES]);
261
262/// A key pair for public-key encryption
263///
264/// Contains both a public key and a secret key for use with crypto_box functions.
265pub struct KeyPair {
266 /// Public key
267 pub public_key: PublicKey,
268 /// Secret key
269 pub secret_key: SecretKey,
270}
271
272impl PublicKey {
273 /// Generate a new public key from bytes
274 ///
275 /// # Arguments
276 /// * `bytes` - Byte slice of exactly PUBLICKEYBYTES length
277 ///
278 /// # Returns
279 /// * `Result<Self>` - A new public key or an error if the input is invalid
280 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
281 if bytes.len() != libsodium_sys::crypto_box_PUBLICKEYBYTES as usize {
282 return Err(SodiumError::InvalidKey(format!(
283 "public key must be exactly {} bytes",
284 libsodium_sys::crypto_box_PUBLICKEYBYTES
285 )));
286 }
287
288 let mut key = [0u8; libsodium_sys::crypto_box_PUBLICKEYBYTES as usize];
289 key.copy_from_slice(bytes);
290 Ok(PublicKey(key))
291 }
292
293 /// Create a public key from a fixed-size bytes
294 ///
295 /// # Arguments
296 /// * `bytes` - Byte array of exactly PUBLICKEYBYTES length
297 ///
298 /// # Returns
299 /// * `Self` - A new public key
300 pub const fn from_bytes_exact(
301 bytes: [u8; libsodium_sys::crypto_box_PUBLICKEYBYTES as usize],
302 ) -> Self {
303 Self(bytes)
304 }
305
306 /// Get a reference to the underlying bytes
307 ///
308 /// # Returns
309 /// * `&[u8; PUBLICKEYBYTES]` - Reference to the public key bytes
310 pub fn as_bytes(&self) -> &[u8; libsodium_sys::crypto_box_PUBLICKEYBYTES as usize] {
311 &self.0
312 }
313}
314
315impl AsRef<[u8]> for PublicKey {
316 fn as_ref(&self) -> &[u8] {
317 &self.0
318 }
319}
320
321impl TryFrom<&[u8]> for PublicKey {
322 type Error = SodiumError;
323
324 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
325 Self::from_bytes(slice)
326 }
327}
328
329impl From<[u8; libsodium_sys::crypto_box_PUBLICKEYBYTES as usize]> for PublicKey {
330 fn from(bytes: [u8; libsodium_sys::crypto_box_PUBLICKEYBYTES as usize]) -> Self {
331 Self(bytes)
332 }
333}
334
335impl From<PublicKey> for [u8; PUBLICKEYBYTES] {
336 fn from(key: PublicKey) -> [u8; PUBLICKEYBYTES] {
337 key.0
338 }
339}
340
341impl std::fmt::Debug for PublicKey {
342 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
343 let hex = ct_codecs::Hex::encode_to_string(&self.0[..4]).unwrap_or_default();
344 write!(f, "PublicKey({hex})...")
345 }
346}
347
348impl std::fmt::Display for PublicKey {
349 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
350 let hex = ct_codecs::Hex::encode_to_string(&self.0[..4]).unwrap_or_default();
351 write!(f, "PublicKey({hex})...")
352 }
353}
354
355impl SecretKey {
356 /// Generate a new secret key from bytes
357 ///
358 /// # Arguments
359 /// * `bytes` - Byte slice of exactly SECRETKEYBYTES length
360 ///
361 /// # Returns
362 /// * `Result<Self>` - A new secret key or an error if the input is invalid
363 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
364 if bytes.len() != libsodium_sys::crypto_box_SECRETKEYBYTES as usize {
365 return Err(SodiumError::InvalidKey(format!(
366 "secret key must be exactly {} bytes",
367 libsodium_sys::crypto_box_SECRETKEYBYTES
368 )));
369 }
370
371 let mut key = [0u8; libsodium_sys::crypto_box_SECRETKEYBYTES as usize];
372 key.copy_from_slice(bytes);
373 Ok(SecretKey(key))
374 }
375
376 /// Create a secret key from a fixed-size bytes
377 ///
378 /// # Arguments
379 /// * `bytes` - Byte array of exactly SECRETKEYBYTES length
380 ///
381 /// # Returns
382 /// * `Self` - A new secret key
383 pub const fn from_bytes_exact(
384 bytes: [u8; libsodium_sys::crypto_box_SECRETKEYBYTES as usize],
385 ) -> Self {
386 Self(bytes)
387 }
388
389 /// Get a reference to the underlying bytes
390 ///
391 /// # Returns
392 /// * `&[u8; SECRETKEYBYTES]` - Reference to the secret key bytes
393 pub fn as_bytes(&self) -> &[u8; libsodium_sys::crypto_box_SECRETKEYBYTES as usize] {
394 &self.0
395 }
396}
397
398impl AsRef<[u8]> for SecretKey {
399 fn as_ref(&self) -> &[u8] {
400 &self.0
401 }
402}
403
404impl TryFrom<&[u8]> for SecretKey {
405 type Error = SodiumError;
406
407 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
408 Self::from_bytes(slice)
409 }
410}
411
412impl From<[u8; libsodium_sys::crypto_box_SECRETKEYBYTES as usize]> for SecretKey {
413 fn from(bytes: [u8; libsodium_sys::crypto_box_SECRETKEYBYTES as usize]) -> Self {
414 Self(bytes)
415 }
416}
417
418impl From<SecretKey> for [u8; SECRETKEYBYTES] {
419 fn from(key: SecretKey) -> [u8; SECRETKEYBYTES] {
420 key.0
421 }
422}
423
424impl std::fmt::Debug for SecretKey {
425 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
426 f.write_str("SecretKey(*****)")
427 }
428}
429
430impl std::fmt::Display for SecretKey {
431 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
432 f.write_str("SecretKey(*****)")
433 }
434}
435
436/// A precomputed shared key for public-key cryptography
437///
438/// This key is the result of the Diffie-Hellman key exchange between a public key
439/// and a secret key. It can be reused for multiple encryption/decryption operations
440/// with the same pair of keys, improving performance.
441///
442/// ## Properties
443///
444/// - Size: 32 bytes (256 bits)
445/// - Derived from a public key and a secret key
446/// - Must be kept confidential
447/// - Can be used for multiple encryption/decryption operations
448#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
449pub struct PrecomputedKey([u8; BEFORENMBYTES]);
450
451impl PrecomputedKey {
452 /// Generate a new precomputed key from bytes
453 ///
454 /// # Arguments
455 /// * `bytes` - Byte slice of exactly BEFORENMBYTES length
456 ///
457 /// # Returns
458 /// * `Result<Self>` - A new precomputed key or an error if the input is invalid
459 pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
460 if bytes.len() != BEFORENMBYTES {
461 return Err(SodiumError::InvalidInput(format!(
462 "precomputed key must be exactly {BEFORENMBYTES} bytes"
463 )));
464 }
465
466 let mut k = [0u8; BEFORENMBYTES];
467 k.copy_from_slice(bytes);
468 Ok(PrecomputedKey(k))
469 }
470
471 /// Get a reference to the underlying bytes
472 ///
473 /// # Returns
474 /// * `&[u8; BEFORENMBYTES]` - Reference to the precomputed key bytes
475 pub fn as_bytes(&self) -> &[u8; BEFORENMBYTES] {
476 &self.0
477 }
478
479 /// Create a precomputed key from a fixed-size bytes
480 ///
481 /// # Arguments
482 /// * `bytes` - Byte array of exactly BEFORENMBYTES length
483 ///
484 /// # Returns
485 /// * `Self` - A new precomputed key
486 pub const fn from_bytes_exact(bytes: [u8; BEFORENMBYTES]) -> Self {
487 Self(bytes)
488 }
489}
490
491impl AsRef<[u8]> for PrecomputedKey {
492 fn as_ref(&self) -> &[u8] {
493 &self.0
494 }
495}
496
497impl TryFrom<&[u8]> for PrecomputedKey {
498 type Error = SodiumError;
499
500 fn try_from(slice: &[u8]) -> std::result::Result<Self, Self::Error> {
501 Self::from_bytes(slice)
502 }
503}
504
505impl From<[u8; BEFORENMBYTES]> for PrecomputedKey {
506 fn from(bytes: [u8; BEFORENMBYTES]) -> Self {
507 Self(bytes)
508 }
509}
510
511impl From<PrecomputedKey> for [u8; BEFORENMBYTES] {
512 fn from(key: PrecomputedKey) -> [u8; BEFORENMBYTES] {
513 key.0
514 }
515}
516
517impl KeyPair {
518 /// Generate a new key pair for public-key encryption
519 ///
520 /// This function generates a new random X25519 key pair suitable for public-key encryption
521 /// and decryption. The key generation process uses libsodium's secure random number generator.
522 ///
523 /// ## Algorithm Details
524 ///
525 /// The key pair generation works as follows:
526 /// 1. Generate 32 random bytes for the secret key
527 /// 2. Derive the public key from the secret key using the X25519 elliptic curve
528 ///
529 /// ## Example
530 ///
531 /// ```rust
532 /// use libsodium_rs as sodium;
533 /// use sodium::crypto_box;
534 /// use sodium::ensure_init;
535 ///
536 /// // Initialize libsodium
537 /// ensure_init().expect("Failed to initialize libsodium");
538 ///
539 /// // Generate a key pair
540 /// let keypair = crypto_box::KeyPair::generate();
541 ///
542 /// // The keys can now be used for encryption and decryption
543 /// assert_eq!(keypair.public_key.as_bytes().len(), crypto_box::PUBLICKEYBYTES);
544 /// assert_eq!(keypair.secret_key.as_bytes().len(), crypto_box::SECRETKEYBYTES);
545 /// ```
546 ///
547 /// # Returns
548 /// * `KeyPair` - A new key pair
549 ///
550 /// # Note
551 /// This function should not fail under normal circumstances. In the extremely unlikely event
552 /// of a system-level failure, this function might panic.
553 pub fn generate() -> Self {
554 let mut pk = [0u8; PUBLICKEYBYTES];
555 let mut sk = [0u8; SECRETKEYBYTES];
556
557 let result = unsafe { libsodium_sys::crypto_box_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
558
559 // This should never happen in practice, but we check anyway for safety
560 assert_eq!(result, 0, "Failed to generate keypair");
561
562 Self {
563 public_key: PublicKey(pk),
564 secret_key: SecretKey(sk),
565 }
566 }
567
568 /// Generate a new key pair from a seed
569 ///
570 /// This function generates a deterministic X25519 key pair from a seed. Given the same seed,
571 /// it will always produce the same key pair. This is useful for applications that need
572 /// deterministic key generation, such as when deriving keys from a master key.
573 ///
574 /// ## Security Considerations
575 ///
576 /// - The seed must be kept as secret as the secret key itself
577 /// - The seed should be high-entropy (ideally from a CSPRNG)
578 /// - If you need non-deterministic key generation, use `generate()` instead
579 ///
580 /// ## Example
581 ///
582 /// ```rust
583 /// use libsodium_rs as sodium;
584 /// use sodium::crypto_box;
585 /// use sodium::random;
586 /// use sodium::ensure_init;
587 ///
588 /// // Initialize libsodium
589 /// ensure_init().expect("Failed to initialize libsodium");
590 ///
591 /// // Generate a random seed
592 /// let mut seed = [0u8; 32];
593 /// random::fill_bytes(&mut seed);
594 ///
595 /// // Generate a keypair from the seed
596 /// let keypair1 = crypto_box::KeyPair::from_seed(&seed).unwrap();
597 ///
598 /// // The same seed will always produce the same keypair
599 /// let keypair2 = crypto_box::KeyPair::from_seed(&seed).unwrap();
600 /// assert_eq!(keypair1.public_key, keypair2.public_key);
601 /// assert_eq!(keypair1.secret_key, keypair2.secret_key);
602 /// ```
603 ///
604 /// # Arguments
605 /// * `seed` - The seed to generate the keypair from (must be exactly 32 bytes)
606 ///
607 /// # Returns
608 /// * `Result<KeyPair>` - A deterministically generated key pair or an error
609 ///
610 /// # Errors
611 /// Returns an error if:
612 /// * The seed has an invalid length
613 /// * Key generation fails (extremely rare, typically only due to system issues)
614 pub fn from_seed(seed: &[u8]) -> Result<Self> {
615 if seed.len() != SECRETKEYBYTES {
616 return Err(SodiumError::InvalidInput(format!(
617 "invalid seed length: expected {}, got {}",
618 SECRETKEYBYTES,
619 seed.len()
620 )));
621 }
622
623 let mut pk = [0u8; PUBLICKEYBYTES];
624 let mut sk = [0u8; SECRETKEYBYTES];
625
626 let result = unsafe {
627 libsodium_sys::crypto_box_seed_keypair(pk.as_mut_ptr(), sk.as_mut_ptr(), seed.as_ptr())
628 };
629
630 if result != 0 {
631 return Err(SodiumError::OperationError(
632 "failed to generate keypair from seed".into(),
633 ));
634 }
635
636 Ok(Self {
637 public_key: PublicKey(pk),
638 secret_key: SecretKey(sk),
639 })
640 }
641
642 /// Convert the KeyPair into a tuple of (PublicKey, SecretKey)
643 ///
644 /// This function consumes the KeyPair and returns its components as a tuple.
645 ///
646 /// ## Example
647 ///
648 /// ```rust
649 /// use libsodium_rs as sodium;
650 /// use sodium::crypto_box;
651 /// use sodium::ensure_init;
652 ///
653 /// // Initialize libsodium
654 /// ensure_init().expect("Failed to initialize libsodium");
655 ///
656 /// // Generate a key pair
657 /// let keypair = crypto_box::KeyPair::generate();
658 ///
659 /// // Convert to tuple
660 /// let (public_key, secret_key) = keypair.into_tuple();
661 /// ```
662 pub fn into_tuple(self) -> (PublicKey, SecretKey) {
663 (self.public_key, self.secret_key)
664 }
665}
666
667/// ## Algorithm Details
668///
669/// 1. A shared secret is computed using X25519 key exchange
670/// 2. The shared secret is hashed using the HSalsa20 function
671/// 3. The resulting key is used with XSalsa20 for encryption
672/// 4. Poly1305 is used to authenticate the ciphertext, ensuring integrity
673///
674/// ## Security Considerations
675///
676/// - Always use a unique nonce for each encryption operation with the same key pair
677/// - The nonce can be public, but must never be reused with the same key pair
678/// - For maximum security, use `Nonce::generate()` to create random nonces
679///
680/// ## Example
681///
682/// ```rust
683/// use libsodium_rs as sodium;
684/// use sodium::crypto_box;
685/// use sodium::ensure_init;
686///
687/// // Initialize libsodium
688/// ensure_init().expect("Failed to initialize libsodium");
689///
690/// // Generate key pairs for Alice and Bob
691/// let alice_keypair = crypto_box::KeyPair::generate();
692/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
693/// let bob_keypair = crypto_box::KeyPair::generate();
694/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
695///
696/// // Generate a random nonce
697/// let nonce = crypto_box::Nonce::generate();
698///
699/// // Alice encrypts a message for Bob
700/// let message = b"Hello, Bob! This is a secret message.";
701/// let ciphertext = crypto_box::seal(message, &nonce, &bob_pk, &alice_sk).unwrap();
702///
703/// // The ciphertext is longer than the message due to the added MAC
704/// assert_eq!(ciphertext.len(), message.len() + crypto_box::MACBYTES);
705/// ```
706///
707/// # Arguments
708/// * `message` - Message to encrypt
709/// * `nonce` - Nonce for encryption
710/// * `recipient_pk` - Recipient's public key
711/// * `sender_sk` - Sender's secret key
712///
713/// # Returns
714/// * `Result<Vec<u8>>` - Encrypted message (ciphertext) or an error
715///
716/// # Errors
717/// Returns an error if the encryption operation fails (extremely rare)
718pub fn seal(
719 message: &[u8],
720 nonce: &Nonce,
721 recipient_pk: &PublicKey,
722 sender_sk: &SecretKey,
723) -> Result<Vec<u8>> {
724 let mut ciphertext = vec![0u8; message.len() + MACBYTES];
725
726 let result = unsafe {
727 libsodium_sys::crypto_box_easy(
728 ciphertext.as_mut_ptr(),
729 message.as_ptr(),
730 message.len() as u64,
731 nonce.as_ref().as_ptr(),
732 recipient_pk.as_bytes().as_ptr(),
733 sender_sk.as_bytes().as_ptr(),
734 )
735 };
736
737 if result != 0 {
738 return Err(SodiumError::EncryptionError(
739 "crypto_box encryption failed".into(),
740 ));
741 }
742
743 Ok(ciphertext)
744}
745
746/// Encrypts a message using public-key cryptography with a precomputed shared key
747///
748/// This function is similar to `seal`, but it uses a precomputed shared key
749/// instead of computing it from the public and secret keys. This can improve
750/// performance when encrypting multiple messages with the same key pair.
751///
752/// ## Algorithm Details
753///
754/// 1. The precomputed shared key (already derived from X25519 and hashed with HSalsa20)
755/// is used directly for XSalsa20 encryption
756/// 2. Poly1305 is used to authenticate the ciphertext, ensuring integrity
757///
758/// ## Security Considerations
759///
760/// - Always use a unique nonce for each encryption operation with the same key pair
761/// - The nonce can be public, but must never be reused with the same key pair
762/// - For maximum security, use `Nonce::generate()` to create random nonces
763///
764/// ## Example
765///
766/// ```rust
767/// use libsodium_rs as sodium;
768/// use sodium::crypto_box;
769/// use sodium::random;
770/// use sodium::ensure_init;
771///
772/// // Initialize libsodium
773/// ensure_init().expect("Failed to initialize libsodium");
774///
775/// // Generate key pairs for Alice and Bob
776/// let alice_keypair = crypto_box::KeyPair::generate();
777/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
778/// let bob_keypair = crypto_box::KeyPair::generate();
779/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
780///
781/// // Alice precomputes a shared key with Bob
782/// let alice_precomputed = crypto_box::beforenm(&bob_pk, &alice_sk).unwrap();
783///
784/// // Generate a random nonce
785/// let nonce = crypto_box::Nonce::generate();
786///
787/// // Alice encrypts a message for Bob using the precomputed key
788/// let message = b"Hello, Bob! This is a secret message.";
789/// let ciphertext = crypto_box::seal_afternm(message, &nonce, &alice_precomputed).unwrap();
790/// ```
791///
792/// # Arguments
793/// * `message` - Message to encrypt
794/// * `nonce` - Nonce for encryption
795/// * `precomputed_key` - Precomputed shared key from beforenm()
796///
797/// # Returns
798/// * `Result<Vec<u8>>` - Encrypted message or an error
799///
800/// # Errors
801/// Returns an error if the encryption operation fails (extremely rare)
802pub fn seal_afternm(
803 message: &[u8],
804 nonce: &Nonce,
805 precomputed_key: &PrecomputedKey,
806) -> Result<Vec<u8>> {
807 let ciphertext_len = message.len() + MACBYTES;
808 let mut ciphertext = vec![0u8; ciphertext_len];
809
810 let result = unsafe {
811 libsodium_sys::crypto_box_easy_afternm(
812 ciphertext.as_mut_ptr(),
813 message.as_ptr(),
814 message.len() as u64,
815 nonce.as_ref().as_ptr(),
816 precomputed_key.as_bytes().as_ptr(),
817 )
818 };
819
820 if result != 0 {
821 return Err(SodiumError::OperationError("encryption failed".into()));
822 }
823
824 Ok(ciphertext)
825}
826
827/// Decrypts a message using public-key cryptography
828///
829/// This function decrypts a message using the X25519-XSalsa20-Poly1305 construction.
830/// The recipient uses their secret key and the sender's public key to decrypt
831/// the message.
832///
833/// ## Algorithm Details
834///
835/// 1. A shared secret is computed using X25519 key exchange
836/// 2. The shared secret is hashed using the HSalsa20 function
837/// 3. The resulting key is used with XSalsa20 for decryption
838/// 4. Poly1305 is used to authenticate the ciphertext, ensuring integrity
839///
840/// ## Security Considerations
841///
842/// - If decryption fails, it could be due to tampering, using the wrong keys, or using the wrong nonce
843/// - The decryption operation is performed in constant time to prevent timing attacks
844/// - If verification fails, no part of the message is considered authentic
845///
846/// ## Example
847///
848/// ```rust
849/// use libsodium_rs as sodium;
850/// use sodium::crypto_box;
851/// use sodium::ensure_init;
852///
853/// // Initialize libsodium
854/// ensure_init().expect("Failed to initialize libsodium");
855///
856/// // Generate key pairs for Alice and Bob
857/// let alice_keypair = crypto_box::KeyPair::generate();
858/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
859/// let bob_keypair = crypto_box::KeyPair::generate();
860/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
861///
862/// // Generate a random nonce
863/// let nonce = crypto_box::Nonce::generate();
864///
865/// // Alice encrypts a message for Bob
866/// let message = b"Hello, Bob! This is a secret message.";
867/// let ciphertext = crypto_box::seal(message, &nonce, &bob_pk, &alice_sk).unwrap();
868///
869/// // Bob decrypts the message from Alice
870/// let decrypted = crypto_box::open(&ciphertext, &nonce, &alice_pk, &bob_sk).unwrap();
871/// assert_eq!(message, &decrypted[..]);
872///
873/// // Tamper with the ciphertext - decryption should fail
874/// let mut tampered = ciphertext.clone();
875/// tampered[0] ^= 1; // Flip a bit in the ciphertext
876/// assert!(crypto_box::open(&tampered, &nonce, &alice_pk, &bob_sk).is_err());
877/// ```
878///
879/// # Arguments
880/// * `ciphertext` - Ciphertext to decrypt
881/// * `nonce` - Nonce used for encryption
882/// * `sender_pk` - Sender's public key
883/// * `recipient_sk` - Recipient's secret key
884///
885/// # Returns
886/// * `Result<Vec<u8>>` - Decrypted message or an error
887///
888/// # Errors
889/// Returns an error if:
890/// - The ciphertext is too short (less than MACBYTES)
891/// - The MAC verification fails (indicating tampering or incorrect keys/nonce)
892/// - The decryption operation fails
893pub fn open(ciphertext: &[u8], nonce: &Nonce, pk: &PublicKey, sk: &SecretKey) -> Result<Vec<u8>> {
894 if ciphertext.len() < MACBYTES {
895 return Err(SodiumError::InvalidInput("ciphertext too short".into()));
896 }
897
898 let mut message = vec![0u8; ciphertext.len() - MACBYTES];
899
900 let result = unsafe {
901 libsodium_sys::crypto_box_open_easy(
902 message.as_mut_ptr(),
903 ciphertext.as_ptr(),
904 ciphertext.len() as u64,
905 nonce.as_ref().as_ptr(),
906 pk.as_bytes().as_ptr(),
907 sk.as_bytes().as_ptr(),
908 )
909 };
910
911 if result != 0 {
912 return Err(SodiumError::AuthenticationError);
913 }
914
915 Ok(message)
916}
917
918/// Decrypts a message using public-key cryptography with a precomputed shared key
919///
920/// This function is similar to `open`, but it uses a precomputed shared key
921/// instead of computing it from the public and secret keys. This can improve
922/// performance when decrypting multiple messages with the same key pair.
923///
924/// ## Algorithm Details
925///
926/// 1. The precomputed shared key (already derived from X25519 and hashed with HSalsa20)
927/// is used directly for XSalsa20 decryption
928/// 2. Poly1305 is used to authenticate the ciphertext, ensuring integrity
929///
930/// ## Security Considerations
931///
932/// - If decryption fails, it could be due to tampering, using the wrong keys, or using the wrong nonce
933/// - The decryption operation is performed in constant time to prevent timing attacks
934/// - If verification fails, no part of the message is considered authentic
935///
936/// ## Example
937///
938/// ```rust
939/// use libsodium_rs as sodium;
940/// use sodium::crypto_box;
941/// use sodium::random;
942/// use sodium::ensure_init;
943///
944/// // Initialize libsodium
945/// ensure_init().expect("Failed to initialize libsodium");
946///
947/// // Generate key pairs for Alice and Bob
948/// let alice_keypair = crypto_box::KeyPair::generate();
949/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
950/// let bob_keypair = crypto_box::KeyPair::generate();
951/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
952///
953/// // Bob precomputes a shared key with Alice
954/// let bob_precomputed = crypto_box::beforenm(&alice_pk, &bob_sk).unwrap();
955///
956/// // Generate a random nonce
957/// let nonce = crypto_box::Nonce::generate();
958///
959/// // Alice encrypts a message for Bob using the precomputed key
960/// let message = b"Hello, Bob! This is a secret message.";
961/// let alice_precomputed = crypto_box::beforenm(&bob_pk, &alice_sk).unwrap();
962/// let ciphertext = crypto_box::seal_afternm(message, &nonce, &alice_precomputed).unwrap();
963///
964/// // Bob decrypts the message from Alice using the precomputed key
965/// let decrypted = crypto_box::open_afternm(&ciphertext, &nonce, &bob_precomputed).unwrap();
966/// assert_eq!(message, &decrypted[..]);
967/// ```
968///
969/// # Arguments
970/// * `ciphertext` - Ciphertext to decrypt
971/// * `nonce` - Nonce used for encryption
972/// * `precomputed_key` - Precomputed shared key from beforenm()
973///
974/// # Returns
975/// * `Result<Vec<u8>>` - Decrypted message or an error
976///
977/// # Errors
978/// Returns an error if:
979/// - The ciphertext is too short (less than MACBYTES)
980/// - The MAC verification fails (indicating tampering or incorrect keys/nonce)
981/// - The decryption operation fails
982pub fn open_afternm(
983 ciphertext: &[u8],
984 nonce: &Nonce,
985 precomputed_key: &PrecomputedKey,
986) -> Result<Vec<u8>> {
987 if ciphertext.len() < MACBYTES {
988 return Err(SodiumError::InvalidInput("ciphertext too short".into()));
989 }
990
991 let mut message = vec![0u8; ciphertext.len() - MACBYTES];
992
993 let result = unsafe {
994 libsodium_sys::crypto_box_open_easy_afternm(
995 message.as_mut_ptr(),
996 ciphertext.as_ptr(),
997 ciphertext.len() as u64,
998 nonce.as_ref().as_ptr(),
999 precomputed_key.as_bytes().as_ptr(),
1000 )
1001 };
1002
1003 if result != 0 {
1004 return Err(SodiumError::OperationError("decryption failed".into()));
1005 }
1006
1007 Ok(message)
1008}
1009
1010/// Computes a shared secret key from a public key and a secret key
1011///
1012/// This function performs the X25519 key exchange to compute a shared secret
1013/// that can be used for symmetric encryption. It's useful when you need to
1014/// perform multiple encryption operations with the same key pair.
1015///
1016/// ## Algorithm Details
1017///
1018/// The shared secret is computed using the X25519 function, which is an
1019/// implementation of the elliptic curve Diffie-Hellman key exchange using
1020/// Curve25519. The raw output of X25519 is then hashed using the HSalsa20
1021/// function to derive the final symmetric key.
1022///
1023/// ## Security Considerations
1024///
1025/// - The shared secret should be kept confidential
1026/// - The shared secret should be used with a secure symmetric encryption algorithm
1027///
1028/// ## Example
1029///
1030/// ```rust
1031/// use libsodium_rs as sodium;
1032/// use sodium::crypto_box;
1033/// use sodium::random;
1034/// use sodium::ensure_init;
1035///
1036/// // Initialize libsodium
1037/// ensure_init().expect("Failed to initialize libsodium");
1038///
1039/// // Generate key pairs for Alice and Bob
1040/// let alice_keypair = crypto_box::KeyPair::generate();
1041/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1042/// let bob_keypair = crypto_box::KeyPair::generate();
1043/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1044///
1045/// // Alice precomputes a shared key with Bob
1046/// let alice_precomputed = crypto_box::beforenm(&bob_pk, &alice_sk).unwrap();
1047///
1048/// // Bob precomputes a shared key with Alice
1049/// let bob_precomputed = crypto_box::beforenm(&alice_pk, &bob_sk).unwrap();
1050///
1051/// // These precomputed keys can now be used for faster encryption/decryption
1052/// ```
1053///
1054/// # Arguments
1055/// * `public_key` - The public key of the other party
1056/// * `secret_key` - Your secret key
1057///
1058/// # Returns
1059/// * `Result<PrecomputedKey>` - The precomputed shared key or an error
1060///
1061/// # Example
1062/// ```rust
1063/// use libsodium_rs as sodium;
1064/// use sodium::crypto_box;
1065/// use sodium::random;
1066/// use sodium::ensure_init;
1067///
1068/// // Initialize libsodium
1069/// ensure_init().expect("Failed to initialize libsodium");
1070///
1071/// // Generate key pairs for Alice and Bob
1072/// let alice_keypair = crypto_box::KeyPair::generate();
1073/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1074/// let bob_keypair = crypto_box::KeyPair::generate();
1075/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1076///
1077/// // Alice precomputes a shared key with Bob
1078/// let alice_precomputed = crypto_box::beforenm(&bob_pk, &alice_sk).unwrap();
1079///
1080/// // Bob precomputes a shared key with Alice
1081/// let bob_precomputed = crypto_box::beforenm(&alice_pk, &bob_sk).unwrap();
1082///
1083/// // These precomputed keys can now be used for faster encryption/decryption
1084/// ```
1085pub fn beforenm(public_key: &PublicKey, secret_key: &SecretKey) -> Result<PrecomputedKey> {
1086 let mut k = [0u8; BEFORENMBYTES];
1087
1088 let result = unsafe {
1089 libsodium_sys::crypto_box_beforenm(
1090 k.as_mut_ptr(),
1091 public_key.as_bytes().as_ptr(),
1092 secret_key.as_bytes().as_ptr(),
1093 )
1094 };
1095
1096 if result != 0 {
1097 return Err(SodiumError::OperationError(
1098 "precomputed key generation failed".into(),
1099 ));
1100 }
1101
1102 Ok(PrecomputedKey(k))
1103}
1104
1105/// Encrypt a message using a precomputed key
1106///
1107/// This function is more efficient when encrypting multiple messages for the same recipient.
1108///
1109/// # Arguments
1110/// * `message` - Message to encrypt
1111/// * `nonce` - Nonce for encryption (must be NONCEBYTES bytes)
1112/// * `precomputed_key` - Precomputed shared key from beforenm()
1113///
1114/// # Returns
1115/// * `Result<Vec<u8>>` - Encrypted message or an error
1116///
1117/// # Example
1118///
1119/// ```rust
1120/// use libsodium_rs as sodium;
1121/// use sodium::crypto_box;
1122/// use sodium::random;
1123/// use sodium::ensure_init;
1124///
1125/// // Initialize libsodium
1126/// ensure_init().expect("Failed to initialize libsodium");
1127///
1128/// // Generate key pairs for Alice and Bob
1129/// let alice_keypair = crypto_box::KeyPair::generate();
1130/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1131/// let bob_keypair = crypto_box::KeyPair::generate();
1132/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1133///
1134/// // Alice precomputes a shared key with Bob
1135/// let alice_precomputed = crypto_box::beforenm(&bob_pk, &alice_sk).unwrap();
1136///
1137/// // Generate a random nonce
1138/// let nonce = crypto_box::Nonce::generate();
1139///
1140/// // Alice encrypts a message for Bob using the precomputed key
1141/// let message = b"Hello, Bob! This is a secret message.";
1142/// let ciphertext = crypto_box::seal_afternm(message, &nonce, &alice_precomputed).unwrap();
1143/// ```
1144/// Decrypt a message using a precomputed key
1145///
1146/// This function is more efficient when decrypting multiple messages from the same sender.
1147///
1148/// # Arguments
1149/// * `ciphertext` - Ciphertext to decrypt
1150/// * `nonce` - Nonce used for encryption (must be NONCEBYTES bytes)
1151/// * `precomputed_key` - Precomputed shared key from beforenm()
1152///
1153/// # Returns
1154/// * `Result<Vec<u8>>` - Decrypted message or an error
1155///
1156/// # Example
1157///
1158/// ```rust
1159/// use libsodium_rs as sodium;
1160/// use sodium::crypto_box;
1161/// use sodium::random;
1162/// use sodium::ensure_init;
1163///
1164/// // Initialize libsodium
1165/// ensure_init().expect("Failed to initialize libsodium");
1166///
1167/// // Generate key pairs for Alice and Bob
1168/// let alice_keypair = crypto_box::KeyPair::generate();
1169/// let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1170/// let bob_keypair = crypto_box::KeyPair::generate();
1171/// let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1172///
1173/// // Bob precomputes a shared key with Alice
1174/// let bob_precomputed = crypto_box::beforenm(&alice_pk, &bob_sk).unwrap();
1175///
1176/// // Generate a random nonce
1177/// let nonce = crypto_box::Nonce::generate();
1178///
1179/// // Alice encrypts a message for Bob using the precomputed key
1180/// let message = b"Hello, Bob! This is a secret message.";
1181/// let alice_precomputed = crypto_box::beforenm(&bob_pk, &alice_sk).unwrap();
1182/// let ciphertext = crypto_box::seal_afternm(message, &nonce, &alice_precomputed).unwrap();
1183///
1184/// // Bob decrypts the message from Alice using the precomputed key
1185/// let decrypted = crypto_box::open_afternm(&ciphertext, &nonce, &bob_precomputed).unwrap();
1186/// assert_eq!(message, &decrypted[..]);
1187/// ```
1188/// # Arguments
1189/// * `message` - Message to encrypt
1190/// * `nonce` - Nonce for encryption
1191/// * `recipient_pk` - Recipient's public key
1192/// * `sender_sk` - Sender's secret key
1193///
1194/// # Returns
1195/// * `Result<(Vec<u8>, [u8; MACBYTES])>` - Tuple of (ciphertext, authentication tag) or an error
1196pub fn seal_detached(
1197 message: &[u8],
1198 nonce: &Nonce,
1199 recipient_pk: &PublicKey,
1200 sender_sk: &SecretKey,
1201) -> Result<(Vec<u8>, [u8; MACBYTES])> {
1202 let mut ciphertext = vec![0u8; message.len()];
1203 let mut mac = [0u8; MACBYTES];
1204
1205 let result = unsafe {
1206 libsodium_sys::crypto_box_detached(
1207 ciphertext.as_mut_ptr(),
1208 mac.as_mut_ptr(),
1209 message.as_ptr(),
1210 message.len() as u64,
1211 nonce.as_ref().as_ptr(),
1212 recipient_pk.as_bytes().as_ptr(),
1213 sender_sk.as_bytes().as_ptr(),
1214 )
1215 };
1216
1217 if result != 0 {
1218 return Err(SodiumError::EncryptionError(
1219 "crypto_box detached encryption failed".into(),
1220 ));
1221 }
1222
1223 Ok((ciphertext, mac))
1224}
1225
1226/// Encrypt a message with detached authentication tag (legacy version)
1227///
1228/// This function is a legacy version that accepts a raw byte slice for the nonce.
1229/// It's recommended to use the version that accepts a `Nonce` type instead.
1230///
1231/// # Arguments
1232/// * `message` - Message to encrypt
1233/// * `nonce` - Nonce for encryption (must be NONCEBYTES bytes)
1234/// * `recipient_pk` - Recipient's public key
1235/// * `sender_sk` - Sender's secret key
1236///
1237/// # Returns
1238/// * `Result<(Vec<u8>, [u8; MACBYTES])>` - Tuple of (ciphertext, authentication tag) or an error
1239///
1240/// Decrypt a message with detached authentication tag
1241///
1242/// This function decrypts a message using a separately provided authentication tag.
1243///
1244/// # Arguments
1245/// * `ciphertext` - Ciphertext to decrypt
1246/// * `mac` - Authentication tag (must be MACBYTES bytes)
1247/// * `nonce` - Nonce used for encryption
1248/// * `sender_pk` - Sender's public key
1249/// * `recipient_sk` - Recipient's secret key
1250///
1251/// # Returns
1252/// * `Result<Vec<u8>>` - Decrypted message or an error
1253pub fn open_detached(
1254 ciphertext: &[u8],
1255 mac: &[u8],
1256 nonce: &Nonce,
1257 pk: &PublicKey,
1258 sk: &SecretKey,
1259) -> Result<Vec<u8>> {
1260 let mut message = vec![0u8; ciphertext.len()];
1261
1262 let result = unsafe {
1263 libsodium_sys::crypto_box_open_detached(
1264 message.as_mut_ptr(),
1265 ciphertext.as_ptr(),
1266 mac.as_ptr(),
1267 ciphertext.len() as u64,
1268 nonce.as_ref().as_ptr(),
1269 pk.as_bytes().as_ptr(),
1270 sk.as_bytes().as_ptr(),
1271 )
1272 };
1273
1274 if result != 0 {
1275 return Err(SodiumError::AuthenticationError);
1276 }
1277
1278 Ok(message)
1279}
1280
1281/// Decrypt a message with detached authentication tag (legacy version)
1282///
1283/// This function is a legacy version that accepts a raw byte slice for the nonce.
1284/// It's recommended to use the version that accepts a `Nonce` type instead.
1285///
1286/// # Arguments
1287/// * `ciphertext` - Ciphertext to decrypt
1288/// * `mac` - Authentication tag (must be MACBYTES bytes)
1289/// * `nonce` - Nonce used for encryption (must be NONCEBYTES bytes)
1290/// * `sender_pk` - Sender's public key
1291/// * `recipient_sk` - Recipient's secret key
1292///
1293/// # Returns
1294/// * `Result<Vec<u8>>` - Decrypted message or an error
1295///
1296/// Encrypt a message with detached authentication tag using a precomputed key
1297///
1298/// This function encrypts a message and returns the ciphertext and authentication tag separately.
1299///
1300/// # Arguments
1301/// * `message` - Message to encrypt
1302/// * `nonce` - Nonce for encryption (must be NONCEBYTES bytes)
1303/// * `precomputed_key` - Precomputed shared key from beforenm()
1304///
1305/// # Returns
1306/// * `Result<(Vec<u8>, Vec<u8>)>` - Tuple of (ciphertext, authentication tag) or an error
1307pub fn seal_detached_afternm(
1308 message: &[u8],
1309 nonce: &Nonce,
1310 precomputed_key: &PrecomputedKey,
1311) -> Result<(Vec<u8>, Vec<u8>)> {
1312 let mut ciphertext = vec![0u8; message.len()];
1313 let mut mac = vec![0u8; MACBYTES];
1314
1315 let result = unsafe {
1316 libsodium_sys::crypto_box_detached_afternm(
1317 ciphertext.as_mut_ptr(),
1318 mac.as_mut_ptr(),
1319 message.as_ptr(),
1320 message.len() as u64,
1321 nonce.as_ref().as_ptr(),
1322 precomputed_key.as_bytes().as_ptr(),
1323 )
1324 };
1325
1326 if result != 0 {
1327 return Err(SodiumError::OperationError("encryption failed".into()));
1328 }
1329
1330 Ok((ciphertext, mac))
1331}
1332
1333/// Decrypt a message with detached authentication tag using a precomputed key
1334///
1335/// This function decrypts a message using a separately provided authentication tag.
1336///
1337/// # Arguments
1338/// * `ciphertext` - Ciphertext to decrypt
1339/// * `mac` - Authentication tag
1340/// * `nonce` - Nonce used for encryption
1341/// * `precomputed_key` - Precomputed shared key from beforenm()
1342///
1343/// # Returns
1344/// * `Result<Vec<u8>>` - Decrypted message or an error
1345pub fn open_detached_afternm(
1346 ciphertext: &[u8],
1347 mac: &[u8],
1348 nonce: &Nonce,
1349 precomputed_key: &PrecomputedKey,
1350) -> Result<Vec<u8>> {
1351 let mut message = vec![0u8; ciphertext.len()];
1352
1353 let result = unsafe {
1354 libsodium_sys::crypto_box_open_detached_afternm(
1355 message.as_mut_ptr(),
1356 ciphertext.as_ptr(),
1357 mac.as_ptr(),
1358 ciphertext.len() as u64,
1359 nonce.as_ref().as_ptr(),
1360 precomputed_key.as_bytes().as_ptr(),
1361 )
1362 };
1363
1364 if result != 0 {
1365 return Err(SodiumError::OperationError("decryption failed".into()));
1366 }
1367
1368 Ok(message)
1369}
1370
1371/// Encrypt a message for a recipient without revealing the sender's identity
1372///
1373/// This function generates an ephemeral key pair, performs a key exchange with the recipient's
1374/// public key, and then encrypts the message. The ephemeral public key is included in the output.
1375///
1376/// ## Algorithm Details
1377///
1378/// The sealed box construction works as follows:
1379/// 1. Generate an ephemeral key pair
1380/// 2. Perform a key exchange with the recipient's public key to create a shared secret
1381/// 3. Use the shared secret to encrypt the message
1382/// 4. Combine the ephemeral public key with the ciphertext
1383///
1384/// This provides anonymity for the sender while ensuring only the intended recipient can decrypt
1385/// the message.
1386///
1387/// ## Security Considerations
1388///
1389/// - The recipient cannot authenticate the sender's identity
1390/// - Each message uses a unique ephemeral key pair, providing forward secrecy
1391/// - The output is deterministic for the same input message and recipient key
1392///
1393/// # Arguments
1394/// * `message` - Message to encrypt
1395/// * `recipient_pk` - Recipient's public key
1396///
1397/// # Returns
1398/// * `Result<Vec<u8>>` - Sealed box (ephemeral public key + ciphertext) or an error
1399pub fn seal_box(message: &[u8], recipient_pk: &PublicKey) -> Result<Vec<u8>> {
1400 let mut sealed_box = vec![0u8; message.len() + SEALBYTES];
1401
1402 let result = unsafe {
1403 libsodium_sys::crypto_box_seal(
1404 sealed_box.as_mut_ptr(),
1405 message.as_ptr(),
1406 message.len() as u64,
1407 recipient_pk.as_bytes().as_ptr(),
1408 )
1409 };
1410
1411 if result != 0 {
1412 return Err(SodiumError::EncryptionError(
1413 "sealed box encryption failed".into(),
1414 ));
1415 }
1416
1417 Ok(sealed_box)
1418}
1419
1420/// Decrypt a message from an anonymous sender
1421///
1422/// This function decrypts a message that was encrypted using seal_box().
1423/// It extracts the ephemeral public key from the sealed box and performs the key exchange.
1424///
1425/// # Arguments
1426/// * `sealed_box` - Sealed box (ephemeral public key + ciphertext)
1427/// * `recipient_pk` - Recipient's public key
1428/// * `recipient_sk` - Recipient's secret key
1429///
1430/// # Returns
1431/// * `Result<Vec<u8>>` - Decrypted message or an error
1432pub fn open_sealed_box(
1433 sealed_box: &[u8],
1434 recipient_pk: &PublicKey,
1435 recipient_sk: &SecretKey,
1436) -> Result<Vec<u8>> {
1437 if sealed_box.len() < SEALBYTES {
1438 return Err(SodiumError::InvalidInput("sealed box too short".into()));
1439 }
1440
1441 let mut message = vec![0u8; sealed_box.len() - SEALBYTES];
1442
1443 let result = unsafe {
1444 libsodium_sys::crypto_box_seal_open(
1445 message.as_mut_ptr(),
1446 sealed_box.as_ptr(),
1447 sealed_box.len() as u64,
1448 recipient_pk.as_bytes().as_ptr(),
1449 recipient_sk.as_bytes().as_ptr(),
1450 )
1451 };
1452
1453 if result != 0 {
1454 return Err(SodiumError::OperationError(
1455 "sealed box decryption failed".into(),
1456 ));
1457 }
1458
1459 Ok(message)
1460}
1461
1462/// NaCl compatibility: Encrypt a message using XSalsa20-Poly1305 with zero padding
1463///
1464/// This function is provided for compatibility with the NaCl API.
1465/// It requires the message to be padded with ZEROBYTES zero bytes at the beginning.
1466///
1467/// # Arguments
1468/// * `padded_message` - Message to encrypt, with ZEROBYTES zero bytes at the beginning
1469/// * `nonce` - Nonce for encryption (must be NONCEBYTES bytes)
1470/// * `recipient_pk` - Recipient's public key
1471/// * `sender_sk` - Sender's secret key
1472///
1473/// # Returns
1474/// * `Result<Vec<u8>>` - Encrypted message (with BOXZEROBYTES zero bytes at the beginning) or an error
1475pub fn seal_nacl(
1476 padded_message: &[u8],
1477 nonce: &Nonce,
1478 pk: &PublicKey,
1479 sk: &SecretKey,
1480) -> Result<Vec<u8>> {
1481 if padded_message.len() < ZEROBYTES {
1482 return Err(SodiumError::InvalidInput(format!(
1483 "padded message must be at least {ZEROBYTES} bytes"
1484 )));
1485 }
1486
1487 // Verify that the first ZEROBYTES bytes of the message are all 0
1488 if padded_message.iter().take(ZEROBYTES).any(|&byte| byte != 0) {
1489 return Err(SodiumError::InvalidInput(format!(
1490 "first {ZEROBYTES} bytes of padded message must be zero"
1491 )));
1492 }
1493
1494 let mut ciphertext = vec![0u8; padded_message.len()];
1495
1496 let result = unsafe {
1497 libsodium_sys::crypto_box(
1498 ciphertext.as_mut_ptr(),
1499 padded_message.as_ptr(),
1500 padded_message.len() as u64,
1501 nonce.as_ref().as_ptr(),
1502 pk.as_bytes().as_ptr(),
1503 sk.as_bytes().as_ptr(),
1504 )
1505 };
1506
1507 if result != 0 {
1508 return Err(SodiumError::OperationError("encryption failed".into()));
1509 }
1510
1511 Ok(ciphertext)
1512}
1513
1514/// NaCl compatibility: Decrypt a message using XSalsa20-Poly1305 with zero padding
1515///
1516/// This function is provided for compatibility with the NaCl API.
1517/// The ciphertext must have BOXZEROBYTES zero bytes at the beginning.
1518///
1519/// # Arguments
1520/// * `padded_ciphertext` - Ciphertext to decrypt, with BOXZEROBYTES zero bytes at the beginning
1521/// * `nonce` - Nonce used for encryption
1522/// * `pk` - Sender's public key
1523/// * `sk` - Recipient's secret key
1524///
1525/// # Returns
1526/// * `Result<Vec<u8>>` - Decrypted message (with ZEROBYTES zero bytes at the beginning) or an error
1527pub fn open_nacl(
1528 padded_ciphertext: &[u8],
1529 nonce: &Nonce,
1530 pk: &PublicKey,
1531 sk: &SecretKey,
1532) -> Result<Vec<u8>> {
1533 if padded_ciphertext.len() < BOXZEROBYTES {
1534 return Err(SodiumError::InvalidInput(format!(
1535 "padded ciphertext must be at least {BOXZEROBYTES} bytes"
1536 )));
1537 }
1538
1539 // Verify that the first BOXZEROBYTES bytes of the ciphertext are all 0
1540 if padded_ciphertext
1541 .iter()
1542 .take(BOXZEROBYTES)
1543 .any(|&byte| byte != 0)
1544 {
1545 return Err(SodiumError::InvalidInput(format!(
1546 "first {BOXZEROBYTES} bytes of padded ciphertext must be zero"
1547 )));
1548 }
1549
1550 let mut message = vec![0u8; padded_ciphertext.len()];
1551
1552 let result = unsafe {
1553 libsodium_sys::crypto_box_open(
1554 message.as_mut_ptr(),
1555 padded_ciphertext.as_ptr(),
1556 padded_ciphertext.len() as u64,
1557 nonce.as_ref().as_ptr(),
1558 pk.as_bytes().as_ptr(),
1559 sk.as_bytes().as_ptr(),
1560 )
1561 };
1562
1563 if result != 0 {
1564 return Err(SodiumError::OperationError("decryption failed".into()));
1565 }
1566
1567 Ok(message)
1568}
1569
1570/// NaCl compatibility: Encrypt a message using XSalsa20-Poly1305 with zero padding and a precomputed key
1571///
1572/// This function is provided for compatibility with the NaCl API.
1573/// It requires the message to be padded with ZEROBYTES zero bytes at the beginning.
1574///
1575/// # Arguments
1576/// * `padded_message` - Message to encrypt, with ZEROBYTES zero bytes at the beginning
1577/// * `nonce` - Nonce for encryption (must be NONCEBYTES bytes)
1578/// * `precomputed_key` - Precomputed shared key from beforenm()
1579///
1580/// # Returns
1581/// * `Result<Vec<u8>>` - Encrypted message (with BOXZEROBYTES zero bytes at the beginning) or an error
1582pub fn seal_nacl_afternm(
1583 padded_message: &[u8],
1584 nonce: &Nonce,
1585 precomputed_key: &PrecomputedKey,
1586) -> Result<Vec<u8>> {
1587 if padded_message.len() < ZEROBYTES {
1588 return Err(SodiumError::InvalidInput(format!(
1589 "padded message must be at least {ZEROBYTES} bytes"
1590 )));
1591 }
1592
1593 // Verify that the first ZEROBYTES bytes of the message are all 0
1594 if padded_message.iter().take(ZEROBYTES).any(|&byte| byte != 0) {
1595 return Err(SodiumError::InvalidInput(format!(
1596 "first {ZEROBYTES} bytes of padded message must be zero"
1597 )));
1598 }
1599
1600 let mut ciphertext = vec![0u8; padded_message.len()];
1601
1602 let result = unsafe {
1603 libsodium_sys::crypto_box_afternm(
1604 ciphertext.as_mut_ptr(),
1605 padded_message.as_ptr(),
1606 padded_message.len() as u64,
1607 nonce.as_ref().as_ptr(),
1608 precomputed_key.as_bytes().as_ptr(),
1609 )
1610 };
1611
1612 if result != 0 {
1613 return Err(SodiumError::OperationError("encryption failed".into()));
1614 }
1615
1616 Ok(ciphertext)
1617}
1618
1619/// NaCl compatibility: Decrypt a message using XSalsa20-Poly1305 with zero padding and a precomputed key
1620///
1621/// This function is provided for compatibility with the NaCl API.
1622/// The ciphertext must have BOXZEROBYTES zero bytes at the beginning.
1623///
1624/// # Arguments
1625/// * `padded_ciphertext` - Ciphertext to decrypt, with BOXZEROBYTES zero bytes at the beginning
1626/// * `nonce` - Nonce used for encryption (must be NONCEBYTES bytes)
1627/// * `precomputed_key` - Precomputed shared key from beforenm()
1628///
1629/// # Returns
1630/// * `Result<Vec<u8>>` - Decrypted message (with ZEROBYTES zero bytes at the beginning) or an error
1631pub fn open_nacl_afternm(
1632 padded_ciphertext: &[u8],
1633 nonce: &Nonce,
1634 precomputed_key: &PrecomputedKey,
1635) -> Result<Vec<u8>> {
1636 if padded_ciphertext.len() < BOXZEROBYTES {
1637 return Err(SodiumError::InvalidInput(format!(
1638 "padded ciphertext must be at least {BOXZEROBYTES} bytes"
1639 )));
1640 }
1641
1642 // Verify that the first BOXZEROBYTES bytes of the ciphertext are all 0
1643 if padded_ciphertext
1644 .iter()
1645 .take(BOXZEROBYTES)
1646 .any(|&byte| byte != 0)
1647 {
1648 return Err(SodiumError::InvalidInput(format!(
1649 "first {BOXZEROBYTES} bytes of padded ciphertext must be zero"
1650 )));
1651 }
1652
1653 let mut message = vec![0u8; padded_ciphertext.len()];
1654
1655 let result = unsafe {
1656 libsodium_sys::crypto_box_open_afternm(
1657 message.as_mut_ptr(),
1658 padded_ciphertext.as_ptr(),
1659 padded_ciphertext.len() as u64,
1660 nonce.as_ref().as_ptr(),
1661 precomputed_key.as_bytes().as_ptr(),
1662 )
1663 };
1664
1665 if result != 0 {
1666 return Err(SodiumError::OperationError("decryption failed".into()));
1667 }
1668
1669 Ok(message)
1670}
1671
1672// Export submodules
1673
1674/// Original variant of crypto_box using XSalsa20-Poly1305
1675///
1676/// This submodule provides the same functionality as the parent module, but with
1677/// explicit naming to indicate the use of XSalsa20-Poly1305.
1678pub mod curve25519xsalsa20poly1305;
1679
1680/// Extended variant of crypto_box using XChaCha20-Poly1305
1681///
1682/// This submodule provides the same functionality as the parent module, but uses
1683/// XChaCha20-Poly1305 instead of XSalsa20-Poly1305. The main advantage is the
1684/// extended nonce size (24 bytes), which makes it safer for random nonce generation.
1685pub mod curve25519xchacha20poly1305;
1686
1687#[cfg(test)]
1688mod tests {
1689 use super::*;
1690 // Random is used in other test functions
1691
1692 #[test]
1693 fn test_keypair_generation() {
1694 let keypair = KeyPair::generate();
1695 let (pk, sk) = (keypair.public_key, keypair.secret_key);
1696 assert_eq!(pk.as_bytes().len(), PUBLICKEYBYTES);
1697 assert_eq!(sk.as_bytes().len(), SECRETKEYBYTES);
1698 }
1699
1700 #[test]
1701 fn test_seed_keypair() {
1702 // Generate a random seed
1703 let mut seed = [0u8; SECRETKEYBYTES];
1704 crate::random::fill_bytes(&mut seed);
1705
1706 // Use the from_seed method of KeyPair
1707 let keypair1 = KeyPair::from_seed(&seed).unwrap();
1708 let keypair2 = KeyPair::from_seed(&seed).unwrap();
1709
1710 // Both keypairs should be identical
1711 assert_eq!(keypair1.public_key, keypair2.public_key);
1712 assert_eq!(keypair1.secret_key, keypair2.secret_key);
1713
1714 // Test with invalid seed length
1715 let invalid_seed = [0u8; SECRETKEYBYTES - 1];
1716 assert!(KeyPair::from_seed(&invalid_seed).is_err());
1717 }
1718
1719 #[test]
1720 fn test_seal_open() {
1721 let alice_keypair = KeyPair::generate();
1722 let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1723 let bob_keypair = KeyPair::generate();
1724 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1725 let nonce = Nonce::generate();
1726 let message = b"Hello, world!";
1727
1728 // Alice encrypts a message for Bob
1729 let ciphertext = seal(message, &nonce, &bob_pk, &alice_sk).unwrap();
1730
1731 // Bob decrypts the message from Alice
1732 let decrypted = open(&ciphertext, &nonce, &alice_pk, &bob_sk).unwrap();
1733
1734 assert_eq!(decrypted, message);
1735 }
1736
1737 #[test]
1738 fn test_beforenm_afternm() {
1739 let alice_keypair = KeyPair::generate();
1740 let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1741 let bob_keypair = KeyPair::generate();
1742 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1743 let nonce = Nonce::generate();
1744 let message = b"Hello, precomputed key!";
1745
1746 // Alice precomputes a shared key with Bob
1747 let alice_precomputed = beforenm(&bob_pk, &alice_sk).unwrap();
1748
1749 // Bob precomputes a shared key with Alice
1750 let bob_precomputed = beforenm(&alice_pk, &bob_sk).unwrap();
1751
1752 // Alice encrypts a message for Bob using the precomputed key
1753 let ciphertext = seal_afternm(message, &nonce, &alice_precomputed).unwrap();
1754
1755 // Bob decrypts the message from Alice using the precomputed key
1756 let decrypted = open_afternm(&ciphertext, &nonce, &bob_precomputed).unwrap();
1757
1758 assert_eq!(decrypted, message);
1759 }
1760
1761 #[test]
1762 fn test_seal_detached_open_detached() {
1763 let alice_keypair = KeyPair::generate();
1764 let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1765 let bob_keypair = KeyPair::generate();
1766 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1767 let nonce = Nonce::generate();
1768 let message = b"Hello, detached authentication!";
1769
1770 // Alice encrypts a message for Bob with detached authentication
1771 let (ciphertext, mac) = seal_detached(message, &nonce, &bob_pk, &alice_sk).unwrap();
1772
1773 // Bob decrypts the message from Alice with detached authentication
1774 let decrypted = open_detached(&ciphertext, &mac, &nonce, &alice_pk, &bob_sk).unwrap();
1775
1776 assert_eq!(decrypted, message);
1777 }
1778
1779 #[test]
1780 fn test_seal_detached_open_detached_afternm() {
1781 let alice_keypair = KeyPair::generate();
1782 let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1783 let bob_keypair = KeyPair::generate();
1784 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1785 let nonce = Nonce::generate();
1786 let message = b"Hello, detached authentication with precomputed key!";
1787
1788 // Alice precomputes a shared key with Bob
1789 let alice_precomputed = beforenm(&bob_pk, &alice_sk).unwrap();
1790
1791 // Bob precomputes a shared key with Alice
1792 let bob_precomputed = beforenm(&alice_pk, &bob_sk).unwrap();
1793
1794 // Alice encrypts a message for Bob with detached authentication using precomputed key
1795 let (ciphertext, mac) = seal_detached_afternm(message, &nonce, &alice_precomputed).unwrap();
1796
1797 // Bob decrypts the message from Alice with detached authentication using precomputed key
1798 let decrypted = open_detached_afternm(&ciphertext, &mac, &nonce, &bob_precomputed).unwrap();
1799
1800 assert_eq!(decrypted, message);
1801 }
1802
1803 #[test]
1804 fn test_seal_box_open_sealed_box() {
1805 let bob_keypair = KeyPair::generate();
1806 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1807 let message = b"Hello, anonymous sender!";
1808
1809 // Anonymous sender encrypts a message for Bob
1810 let sealed_box = seal_box(message, &bob_pk).unwrap();
1811
1812 // Bob decrypts the message from the anonymous sender
1813 let decrypted = open_sealed_box(&sealed_box, &bob_pk, &bob_sk).unwrap();
1814
1815 assert_eq!(decrypted, message);
1816 }
1817
1818 #[test]
1819 fn test_nacl_compatibility() {
1820 let alice_keypair = KeyPair::generate();
1821 let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1822 let bob_keypair = KeyPair::generate();
1823 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1824 let nonce = Nonce::generate();
1825
1826 // Create a message with ZEROBYTES zero bytes at the beginning
1827 let message = b"Hello, NaCl!";
1828 let mut padded_message = vec![0u8; ZEROBYTES + message.len()];
1829 padded_message[ZEROBYTES..].copy_from_slice(message);
1830
1831 // Alice encrypts a message for Bob using NaCl compatibility mode
1832 let padded_ciphertext = seal_nacl(&padded_message, &nonce, &bob_pk, &alice_sk).unwrap();
1833
1834 // Bob decrypts the message from Alice using NaCl compatibility mode
1835 let decrypted_padded = open_nacl(&padded_ciphertext, &nonce, &alice_pk, &bob_sk).unwrap();
1836
1837 assert_eq!(decrypted_padded, padded_message);
1838 }
1839
1840 #[test]
1841 fn test_nacl_compatibility_afternm() {
1842 let alice_keypair = KeyPair::generate();
1843 let (alice_pk, alice_sk) = (alice_keypair.public_key, alice_keypair.secret_key);
1844 let bob_keypair = KeyPair::generate();
1845 let (bob_pk, bob_sk) = (bob_keypair.public_key, bob_keypair.secret_key);
1846 let nonce = Nonce::generate();
1847
1848 // Create a message with ZEROBYTES zero bytes at the beginning
1849 let message = b"Hello, NaCl afternm!";
1850 let mut padded_message = vec![0u8; ZEROBYTES + message.len()];
1851 padded_message[ZEROBYTES..].copy_from_slice(message);
1852
1853 // Alice precomputes a shared key with Bob
1854 let alice_precomputed = beforenm(&bob_pk, &alice_sk).unwrap();
1855
1856 // Bob precomputes a shared key with Alice
1857 let bob_precomputed = beforenm(&alice_pk, &bob_sk).unwrap();
1858
1859 // Alice encrypts a message for Bob using NaCl compatibility mode with precomputed key
1860 let padded_ciphertext =
1861 seal_nacl_afternm(&padded_message, &nonce, &alice_precomputed).unwrap();
1862
1863 // Bob decrypts the message from Alice using NaCl compatibility mode with precomputed key
1864 let decrypted_padded =
1865 open_nacl_afternm(&padded_ciphertext, &nonce, &bob_precomputed).unwrap();
1866
1867 assert_eq!(decrypted_padded, padded_message);
1868 }
1869
1870 #[test]
1871 fn test_publickey_traits() {
1872 let keypair = KeyPair::generate();
1873 let pk = keypair.public_key;
1874
1875 // Test From<[u8; N]> for PublicKey
1876 let bytes: [u8; PUBLICKEYBYTES] = pk.clone().into();
1877 let pk2 = PublicKey::from(bytes);
1878 assert_eq!(pk.as_bytes(), pk2.as_bytes());
1879
1880 // Test From<PublicKey> for [u8; N]
1881 let extracted: [u8; PUBLICKEYBYTES] = pk.into();
1882 assert_eq!(extracted, bytes);
1883 }
1884
1885 #[test]
1886 fn test_secretkey_traits() {
1887 let keypair = KeyPair::generate();
1888 let sk = keypair.secret_key;
1889
1890 // Test From<[u8; N]> for SecretKey
1891 let bytes: [u8; SECRETKEYBYTES] = sk.clone().into();
1892 let sk2 = SecretKey::from(bytes);
1893 assert_eq!(sk.as_bytes(), sk2.as_bytes());
1894
1895 // Test From<SecretKey> for [u8; N]
1896 let extracted: [u8; SECRETKEYBYTES] = sk.into();
1897 assert_eq!(extracted, bytes);
1898 }
1899
1900 #[test]
1901 fn test_precomputedkey_traits() {
1902 let alice_keypair = KeyPair::generate();
1903 let bob_keypair = KeyPair::generate();
1904
1905 let precomputed = beforenm(&bob_keypair.public_key, &alice_keypair.secret_key).unwrap();
1906
1907 // Test TryFrom<&[u8]>
1908 let bytes = precomputed.as_bytes();
1909 let pk2 = PrecomputedKey::try_from(&bytes[..]).unwrap();
1910 assert_eq!(precomputed.as_bytes(), pk2.as_bytes());
1911
1912 // Test invalid length
1913 let invalid_bytes = [0u8; BEFORENMBYTES - 1];
1914 assert!(PrecomputedKey::try_from(&invalid_bytes[..]).is_err());
1915
1916 // Test From<[u8; N]>
1917 let bytes: [u8; BEFORENMBYTES] = precomputed.clone().into();
1918 let pk3 = PrecomputedKey::from(bytes);
1919 assert_eq!(precomputed.as_bytes(), pk3.as_bytes());
1920
1921 // Test From<PrecomputedKey> for [u8; N]
1922 let extracted: [u8; BEFORENMBYTES] = precomputed.into();
1923 assert_eq!(extracted, bytes);
1924
1925 // Test AsRef<[u8]>
1926 let precomputed2 = beforenm(&alice_keypair.public_key, &bob_keypair.secret_key).unwrap();
1927 let slice_ref: &[u8] = precomputed2.as_ref();
1928 assert_eq!(slice_ref.len(), BEFORENMBYTES);
1929 }
1930}