Skip to main content

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}