Skip to main content

libsodium_rs/
crypto_kx.rs

1//! # Key Exchange
2//!
3//! This module provides a secure key exchange mechanism based on the X25519 function.
4//! It allows two parties to establish a shared secret over an insecure channel.
5//! The implementation follows the key exchange protocol defined in libsodium.
6//!
7//! ## Overview
8//!
9//! The key exchange mechanism in this module uses the X25519 function, which is an
10//! elliptic curve Diffie-Hellman key exchange using Curve25519. This allows two parties
11//! to establish a shared secret that can be used for symmetric encryption. The shared
12//! secret is automatically hashed using BLAKE2b before being used as session keys.
13//!
14//! ## Features
15//!
16//! - **High security**: Based on the X25519 function (Curve25519)
17//! - **Forward secrecy**: New session keys can be generated for each session
18//! - **Authenticated**: Both parties can verify the identity of the other party
19//! - **Bidirectional**: Generates separate keys for sending and receiving
20//!
21//! ## Usage
22//!
23//! The typical workflow is as follows:
24//!
25//! 1. Both the client and server generate their own keypairs
26//! 2. They exchange their public keys over any channel (doesn't need to be secure)
27//! 3. The client computes session keys using `client_session_keys()`
28//! 4. The server computes session keys using `server_session_keys()`
29//! 5. Both parties now have matching session keys for bidirectional communication
30//!
31//! ```rust
32//! use libsodium_rs as sodium;
33//! use sodium::crypto_kx;
34//! use sodium::ensure_init;
35//!
36//! // Initialize libsodium
37//! ensure_init().expect("Failed to initialize libsodium");
38//!
39//! // Client and server each generate their keypairs
40//! let client_keypair = crypto_kx::KeyPair::generate().unwrap();
41//! let (client_pk, client_sk) = (client_keypair.public_key, client_keypair.secret_key);
42//! let server_keypair = crypto_kx::KeyPair::generate().unwrap();
43//! let (server_pk, server_sk) = (server_keypair.public_key, server_keypair.secret_key);
44//!
45//! // Exchange public keys (this would happen over a network in practice)
46//!
47//! // Client computes session keys
48//! let client_keys = crypto_kx::client_session_keys(
49//!     &client_pk,
50//!     &client_sk,
51//!     &server_pk,
52//! ).unwrap();
53//!
54//! // Server computes session keys
55//! let server_keys = crypto_kx::server_session_keys(
56//!     &server_pk,
57//!     &server_sk,
58//!     &client_pk,
59//! ).unwrap();
60//!
61//! // Now client_keys.tx matches server_keys.rx
62//! // and client_keys.rx matches server_keys.tx
63//! assert_eq!(client_keys.tx, server_keys.rx);
64//! assert_eq!(client_keys.rx, server_keys.tx);
65//!
66//! // These keys can now be used for symmetric encryption
67//! ```
68//!
69//! ## Security Considerations
70//!
71//! - Keep secret keys private at all times
72//! - Public keys can be freely shared
73//! - Generate new keypairs for each communication session for forward secrecy
74//! - The session keys should be used with appropriate symmetric encryption algorithms
75//! - For maximum security, authenticate the public keys through a trusted channel
76//! - The shared secret established through X25519 is automatically hashed before being used as session keys
77//! - Be aware that X25519 is based on Curve25519, which has a cofactor of 8
78//! - The key exchange protocol provides protection against man-in-the-middle attacks
79//!   when public keys are properly authenticated
80//! - The session keys are derived using BLAKE2b, which is resistant to length extension attacks
81//! - Different keys are used for each direction to prevent reflection attacks
82
83use crate::{Result, SodiumError};
84use libsodium_sys;
85use std::ffi::CStr;
86
87/// Number of bytes in a public key (32)
88///
89/// This is the size of a Curve25519 public key used in the X25519 key exchange.
90pub const PUBLICKEYBYTES: usize = libsodium_sys::crypto_kx_PUBLICKEYBYTES as usize;
91
92/// Number of bytes in a secret key (32)
93///
94/// This is the size of a Curve25519 secret key used in the X25519 key exchange.
95pub const SECRETKEYBYTES: usize = libsodium_sys::crypto_kx_SECRETKEYBYTES as usize;
96
97/// Number of bytes in a seed for deterministic key generation (32)
98pub const SEEDBYTES: usize = libsodium_sys::crypto_kx_SEEDBYTES as usize;
99
100/// Number of bytes in a session key (32)
101///
102/// This is the size of the symmetric keys generated through the key exchange.
103/// These keys can be used for symmetric encryption algorithms like XChaCha20-Poly1305.
104pub const SESSIONKEYBYTES: usize = libsodium_sys::crypto_kx_SESSIONKEYBYTES as usize;
105
106/// Name of the key exchange primitive.
107pub const PRIMITIVE: &str = "x25519blake2b";
108
109/// A public key for key exchange
110///
111/// This represents a Curve25519 public key used in the X25519 key exchange.
112/// Public keys can be freely shared with other parties.
113///
114/// ## Size
115///
116/// A public key is always exactly `PUBLICKEYBYTES` (32) bytes.
117///
118/// ## Security Considerations
119///
120/// While public keys can be freely shared, it's important to authenticate
121/// them through a trusted channel to prevent man-in-the-middle attacks.
122///
123/// ## Usage
124///
125/// Public keys are typically generated with the `KeyPair::generate()` function
126/// and then shared with the other party to establish a secure communication channel.
127#[derive(Debug, Clone, Eq, PartialEq)]
128pub struct PublicKey([u8; PUBLICKEYBYTES]);
129
130/// A secret key for key exchange
131///
132/// This represents a Curve25519 secret key used in the X25519 key exchange.
133/// Secret keys must be kept private and never shared.
134///
135/// ## Size
136///
137/// A secret key is always exactly `SECRETKEYBYTES` (32) bytes.
138///
139/// ## Security Considerations
140///
141/// Secret keys should be generated using a secure random number generator
142/// and should never be exposed. When a secret key is no longer needed,
143/// it should be securely erased from memory.
144///
145/// ## Security
146///
147/// Secret keys should be protected with the same care as passwords or encryption keys.
148/// They should never be transmitted over a network or stored in plaintext.
149///
150/// ## Usage
151///
152/// Secret keys are typically generated with the `KeyPair::generate()` function
153/// and used locally to compute shared session keys.
154#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
155pub struct SecretKey([u8; SECRETKEYBYTES]);
156
157/// A key pair for key exchange
158///
159/// Contains both a public key and a secret key for use with crypto_kx functions.
160/// The key pair is used to establish a secure communication channel between
161/// two parties using the X25519 key exchange protocol.
162pub struct KeyPair {
163    /// Public key
164    pub public_key: PublicKey,
165    /// Secret key
166    pub secret_key: SecretKey,
167}
168
169/// A pair of session keys for bidirectional communication
170///
171/// This struct contains two symmetric keys for secure bidirectional communication:
172/// - `tx`: Used for encrypting outgoing messages (and decrypting by the other party)
173/// - `rx`: Used for decrypting incoming messages (and encrypting by the other party)
174///
175/// Using separate keys for each direction provides additional security by preventing
176/// reflection attacks and ensuring that encryption and decryption operations use
177/// different keys.
178///
179/// ## Size
180///
181/// Each session key is exactly `SESSIONKEYBYTES` (32) bytes.
182///
183/// ## Usage
184///
185/// Session keys are computed using either `client_session_keys()` or `server_session_keys()`
186/// and then used with symmetric encryption algorithms like XChaCha20-Poly1305.
187///
188/// ```rust
189/// use libsodium_rs as sodium;
190/// use sodium::crypto_kx;
191/// use sodium::crypto_secretbox;
192/// use sodium::random;
193/// use sodium::ensure_init;
194///
195/// // Initialize libsodium
196/// ensure_init().expect("Failed to initialize libsodium");
197///
198/// // Generate keypairs and compute session keys (abbreviated)
199/// let client_keypair = crypto_kx::KeyPair::generate().unwrap();
200/// let client_pk = client_keypair.public_key;
201/// let client_sk = client_keypair.secret_key;
202/// let server_keypair = crypto_kx::KeyPair::generate().unwrap();
203/// let server_pk = server_keypair.public_key;
204/// let server_sk = server_keypair.secret_key;
205/// let client_keys = crypto_kx::client_session_keys(&client_pk, &client_sk, &server_pk).unwrap();
206///
207/// // Use the tx key for encryption
208/// let nonce = crypto_secretbox::Nonce::generate();
209/// let message = b"Hello, server!";
210///
211/// // Create a key from the session key bytes
212/// let tx_key = crypto_secretbox::Key::from_bytes(&client_keys.tx).unwrap();
213///
214/// // Encrypt the message
215/// let ciphertext = crypto_secretbox::seal(message, &nonce, &tx_key);
216///
217/// // The server would decrypt using its rx key (which matches client's tx key)
218/// ```
219#[derive(Debug, Clone, Eq, PartialEq, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
220pub struct SessionKeys {
221    /// Key for sending messages (rx for the other party)
222    pub tx: [u8; SESSIONKEYBYTES],
223    /// Key for receiving messages (tx for the other party)
224    pub rx: [u8; SESSIONKEYBYTES],
225}
226
227impl PublicKey {
228    /// Create a public key from existing bytes
229    ///
230    /// This function creates a public key from an existing byte array.
231    /// It's useful when you need to deserialize a public key that was
232    /// previously serialized or received from another party.
233    ///
234    /// ## Arguments
235    ///
236    /// * `bytes` - A byte slice of exactly `PUBLICKEYBYTES` (32) length
237    ///
238    /// ## Returns
239    ///
240    /// * `Result<Self>` - A new public key or an error if the input is invalid
241    ///
242    /// ## Errors
243    ///
244    /// Returns an error if the input is not exactly `PUBLICKEYBYTES` bytes long.
245    ///
246    /// ## Example
247    ///
248    /// ```rust
249    /// use libsodium_rs as sodium;
250    /// use sodium::crypto_kx::PublicKey;
251    /// use sodium::ensure_init;
252    ///
253    /// // Initialize libsodium
254    /// ensure_init().expect("Failed to initialize libsodium");
255    ///
256    /// // Create a public key from bytes (e.g., received from a peer)
257    /// let key_bytes = [0x42; 32]; // 32 bytes of data
258    /// let public_key = PublicKey::from_bytes(&key_bytes).unwrap();
259    /// ```
260    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
261        if bytes.len() != PUBLICKEYBYTES {
262            return Err(SodiumError::InvalidInput(format!(
263                "public key must be exactly {PUBLICKEYBYTES} bytes"
264            )));
265        }
266
267        let mut key = [0u8; PUBLICKEYBYTES];
268        key.copy_from_slice(bytes);
269        Ok(PublicKey(key))
270    }
271
272    /// Get the raw bytes of the public key
273    ///
274    /// This function returns a reference to the internal byte array of the public key.
275    /// It's useful when you need to serialize the public key for transmission or storage.
276    ///
277    /// ## Returns
278    ///
279    /// * `&[u8]` - A reference to the public key bytes
280    ///
281    /// ## Example
282    ///
283    /// ```rust
284    /// use libsodium_rs as sodium;
285    /// use sodium::crypto_kx;
286    /// use sodium::ensure_init;
287    ///
288    /// // Initialize libsodium
289    /// ensure_init().expect("Failed to initialize libsodium");
290    ///
291    /// // Generate a keypair
292    /// let keypair = crypto_kx::KeyPair::generate().unwrap();
293    /// let public_key = keypair.public_key;
294    ///
295    /// // Get the raw bytes of the public key
296    /// let key_bytes = public_key.as_bytes();
297    /// assert_eq!(key_bytes.len(), crypto_kx::PUBLICKEYBYTES);
298    /// ```
299    pub fn as_bytes(&self) -> &[u8] {
300        &self.0
301    }
302}
303
304impl AsRef<[u8]> for PublicKey {
305    fn as_ref(&self) -> &[u8] {
306        self.as_bytes()
307    }
308}
309
310impl TryFrom<&[u8]> for PublicKey {
311    type Error = SodiumError;
312
313    fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
314        Self::from_bytes(bytes)
315    }
316}
317
318impl From<[u8; PUBLICKEYBYTES]> for PublicKey {
319    fn from(bytes: [u8; PUBLICKEYBYTES]) -> Self {
320        PublicKey(bytes)
321    }
322}
323
324impl From<PublicKey> for [u8; PUBLICKEYBYTES] {
325    fn from(key: PublicKey) -> Self {
326        key.0
327    }
328}
329
330impl SecretKey {
331    /// Create a secret key from existing bytes
332    ///
333    /// This function creates a secret key from an existing byte array.
334    /// It's useful when you need to deserialize a secret key that was
335    /// previously serialized or derived from another source.
336    ///
337    /// ## Security Considerations
338    ///
339    /// Be extremely careful when handling secret key material. Secret keys should
340    /// never be transmitted over a network or stored in plaintext.
341    ///
342    /// ## Arguments
343    ///
344    /// * `bytes` - A byte slice of exactly `SECRETKEYBYTES` (32) length
345    ///
346    /// ## Returns
347    ///
348    /// * `Result<Self>` - A new secret key or an error if the input is invalid
349    ///
350    /// ## Errors
351    ///
352    /// Returns an error if the input is not exactly `SECRETKEYBYTES` bytes long.
353    ///
354    /// ## Example
355    ///
356    /// ```rust
357    /// use libsodium_rs as sodium;
358    /// use sodium::crypto_kx::SecretKey;
359    /// use sodium::ensure_init;
360    ///
361    /// // Initialize libsodium
362    /// ensure_init().expect("Failed to initialize libsodium");
363    ///
364    /// // Create a secret key from bytes (e.g., from secure storage)
365    /// let key_bytes = [0x42; 32]; // 32 bytes of data
366    /// let secret_key = SecretKey::from_bytes(&key_bytes).unwrap();
367    /// ```
368    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
369        if bytes.len() != SECRETKEYBYTES {
370            return Err(SodiumError::InvalidInput(format!(
371                "secret key must be exactly {SECRETKEYBYTES} bytes"
372            )));
373        }
374
375        let mut key = [0u8; SECRETKEYBYTES];
376        key.copy_from_slice(bytes);
377        Ok(SecretKey(key))
378    }
379
380    /// Get the raw bytes of the secret key
381    ///
382    /// This function returns a reference to the internal byte array of the secret key.
383    /// It's useful when you need to serialize the secret key for secure storage.
384    ///
385    /// ## Security Considerations
386    ///
387    /// Be extremely careful when handling the raw bytes of a secret key.
388    /// They should never be logged, transmitted over a network, or stored in plaintext.
389    ///
390    /// ## Returns
391    ///
392    /// * `&[u8]` - A reference to the secret key bytes
393    ///
394    /// ## Example
395    ///
396    /// ```rust
397    /// use libsodium_rs as sodium;
398    /// use sodium::crypto_kx;
399    /// use sodium::ensure_init;
400    ///
401    /// // Initialize libsodium
402    /// ensure_init().expect("Failed to initialize libsodium");
403    ///
404    /// // Generate a keypair
405    /// let keypair = crypto_kx::KeyPair::generate().unwrap();
406    /// let secret_key = keypair.secret_key;
407    ///
408    /// // Get the raw bytes of the secret key (handle with care!)
409    /// let key_bytes = secret_key.as_bytes();
410    /// assert_eq!(key_bytes.len(), crypto_kx::SECRETKEYBYTES);
411    /// ```
412    pub fn as_bytes(&self) -> &[u8] {
413        &self.0
414    }
415}
416
417impl AsRef<[u8]> for SecretKey {
418    fn as_ref(&self) -> &[u8] {
419        self.as_bytes()
420    }
421}
422
423impl TryFrom<&[u8]> for SecretKey {
424    type Error = SodiumError;
425
426    fn try_from(bytes: &[u8]) -> std::result::Result<Self, Self::Error> {
427        Self::from_bytes(bytes)
428    }
429}
430
431impl From<[u8; SECRETKEYBYTES]> for SecretKey {
432    fn from(bytes: [u8; SECRETKEYBYTES]) -> Self {
433        SecretKey(bytes)
434    }
435}
436
437impl From<SecretKey> for [u8; SECRETKEYBYTES] {
438    fn from(key: SecretKey) -> Self {
439        key.0
440    }
441}
442
443impl KeyPair {
444    /// Generate a new key pair for key exchange
445    ///
446    /// This function generates a new random keypair for use in the X25519 key exchange.
447    /// The keypair consists of a public key that can be shared with other parties,
448    /// and a secret key that must be kept private.
449    ///
450    /// ## Algorithm Details
451    ///
452    /// The keypair is generated using the X25519 function, which is based on the
453    /// Curve25519 elliptic curve. This provides 128 bits of security, which is
454    /// considered sufficient for most applications.
455    ///
456    /// ## Security Considerations
457    ///
458    /// - The secret key should be kept private at all times
459    /// - For maximum security, generate a new keypair for each session
460    /// - The public key can be freely shared with other parties
461    ///
462    /// ## Returns
463    ///
464    /// * `Result<KeyPair>` - A key pair containing the public and secret keys
465    ///
466    /// ## Errors
467    ///
468    /// Returns an error if the keypair generation fails (extremely rare with proper libsodium initialization)
469    ///
470    /// ## Example
471    ///
472    /// ```rust
473    /// use libsodium_rs as sodium;
474    /// use sodium::crypto_kx;
475    /// use sodium::ensure_init;
476    ///
477    /// // Initialize libsodium
478    /// ensure_init().expect("Failed to initialize libsodium");
479    ///
480    /// // Generate a keypair
481    /// let keypair = crypto_kx::KeyPair::generate().unwrap();
482    ///
483    /// // The public key can be shared with other parties
484    /// let public_key_bytes = keypair.public_key.as_bytes();
485    ///
486    /// // The secret key must be kept private
487    /// let secret_key_bytes = keypair.secret_key.as_bytes();
488    /// ```
489    pub fn generate() -> Result<Self> {
490        let mut pk = [0u8; PUBLICKEYBYTES];
491        let mut sk = [0u8; SECRETKEYBYTES];
492
493        let result = unsafe { libsodium_sys::crypto_kx_keypair(pk.as_mut_ptr(), sk.as_mut_ptr()) };
494
495        if result != 0 {
496            return Err(SodiumError::OperationError(
497                "failed to generate keypair".into(),
498            ));
499        }
500
501        Ok(Self {
502            public_key: PublicKey(pk),
503            secret_key: SecretKey(sk),
504        })
505    }
506
507    /// Generate a deterministic key pair from a seed.
508    pub fn from_seed(seed: &[u8]) -> Result<Self> {
509        if seed.len() != SEEDBYTES {
510            return Err(SodiumError::InvalidInput(format!(
511                "invalid seed length: expected {}, got {}",
512                SEEDBYTES,
513                seed.len()
514            )));
515        }
516
517        let mut pk = [0u8; PUBLICKEYBYTES];
518        let mut sk = [0u8; SECRETKEYBYTES];
519
520        let result = unsafe {
521            libsodium_sys::crypto_kx_seed_keypair(pk.as_mut_ptr(), sk.as_mut_ptr(), seed.as_ptr())
522        };
523
524        if result != 0 {
525            return Err(SodiumError::OperationError(
526                "failed to generate keypair from seed".into(),
527            ));
528        }
529
530        Ok(Self {
531            public_key: PublicKey(pk),
532            secret_key: SecretKey(sk),
533        })
534    }
535
536    /// Convert the KeyPair into a tuple of (PublicKey, SecretKey)
537    pub fn into_tuple(self) -> (PublicKey, SecretKey) {
538        (self.public_key, self.secret_key)
539    }
540}
541
542/// Returns the seed size for deterministic key generation.
543pub fn seedbytes() -> usize {
544    unsafe { libsodium_sys::crypto_kx_seedbytes() }
545}
546
547/// Returns the session key size.
548pub fn sessionkeybytes() -> usize {
549    unsafe { libsodium_sys::crypto_kx_sessionkeybytes() }
550}
551
552/// Returns the name of the key exchange primitive.
553pub fn primitive() -> &'static str {
554    unsafe {
555        CStr::from_ptr(libsodium_sys::crypto_kx_primitive())
556            .to_str()
557            .expect("crypto_kx primitive should be valid UTF-8")
558    }
559}
560
561/// Computes session keys for a client
562///
563/// This function computes a pair of session keys that can be used for secure
564/// communication between a client and a server. It must be called by the client
565/// using the client's keypair and the server's public key.
566///
567/// The client and server roles are important because they determine the order
568/// of inputs to the key derivation function, ensuring that the client's tx key
569/// matches the server's rx key, and vice versa.
570///
571/// ## Algorithm Details
572///
573/// The session keys are derived using the X25519 function to compute a shared secret,
574/// which is then hashed using BLAKE2b to produce two separate keys for sending and receiving.
575/// This ensures that different keys are used in each direction and that the raw output of
576/// the X25519 function is never directly used as a cryptographic key.
577///
578/// ## Security Considerations
579///
580/// - The client must verify the authenticity of the server's public key
581/// - The resulting session keys should be used with appropriate symmetric encryption
582/// - The client's tx key corresponds to the server's rx key, and vice versa
583/// - The session keys are derived using BLAKE2b, which is resistant to length extension attacks
584/// - Different keys are used for each direction to prevent reflection attacks
585/// - The key exchange provides forward secrecy if new keypairs are generated for each session
586///
587/// ## Arguments
588///
589/// * `client_pk` - The client's public key
590/// * `client_sk` - The client's secret key
591/// * `server_pk` - The server's public key
592///
593/// ## Returns
594///
595/// * `Result<SessionKeys>` - A pair of session keys for bidirectional communication
596///
597/// ## Errors
598///
599/// Returns an error if the key computation fails, which can happen if:
600/// - The public keys are invalid
601/// - The secret key is invalid
602/// - The public keys represent the same identity (client_pk == server_pk)
603///
604/// ## Example
605///
606/// ```rust
607/// use libsodium_rs as sodium;
608/// use sodium::crypto_kx;
609/// use sodium::ensure_init;
610///
611/// // Initialize libsodium
612/// ensure_init().expect("Failed to initialize libsodium");
613///
614/// // Generate keypairs for client and server
615/// let client_keypair = crypto_kx::KeyPair::generate().unwrap();
616/// let client_pk = client_keypair.public_key;
617/// let client_sk = client_keypair.secret_key;
618/// let server_keypair = crypto_kx::KeyPair::generate().unwrap();
619/// let server_pk = server_keypair.public_key;
620/// let server_sk = server_keypair.secret_key;
621///
622/// // Client computes session keys
623/// let client_keys = crypto_kx::client_session_keys(
624///     &client_pk,
625///     &client_sk,
626///     &server_pk,
627/// ).unwrap();
628///
629/// // Now client_keys.tx can be used to encrypt messages to the server,
630/// // and client_keys.rx can be used to decrypt messages from the server.
631/// ```
632pub fn client_session_keys(
633    client_pk: &PublicKey,
634    client_sk: &SecretKey,
635    server_pk: &PublicKey,
636) -> Result<SessionKeys> {
637    let mut rx = [0u8; SESSIONKEYBYTES];
638    let mut tx = [0u8; SESSIONKEYBYTES];
639
640    let result = unsafe {
641        libsodium_sys::crypto_kx_client_session_keys(
642            rx.as_mut_ptr(),
643            tx.as_mut_ptr(),
644            client_pk.as_bytes().as_ptr(),
645            client_sk.as_bytes().as_ptr(),
646            server_pk.as_bytes().as_ptr(),
647        )
648    };
649
650    if result != 0 {
651        return Err(SodiumError::OperationError(
652            "failed to compute session keys".into(),
653        ));
654    }
655
656    Ok(SessionKeys { rx, tx })
657}
658
659/// Server side: compute session keys
660///
661/// This function computes a pair of session keys for secure bidirectional
662/// communication between a client and a server. It must be called by the server
663/// using the server's keypair and the client's public key.
664///
665/// ## Algorithm Details
666///
667/// The session keys are derived using the X25519 function to compute a shared secret,
668/// which is then hashed using BLAKE2b to produce two separate keys for sending and receiving.
669/// This ensures that different keys are used in each direction and that the raw output of
670/// the X25519 function is never directly used as a cryptographic key.
671///
672/// ## Security Considerations
673///
674/// - The server must verify the authenticity of the client's public key
675/// - The resulting session keys should be used with appropriate symmetric encryption
676/// - The server's tx key corresponds to the client's rx key, and vice versa
677/// - The session keys are derived using BLAKE2b, which is resistant to length extension attacks
678/// - Different keys are used for each direction to prevent reflection attacks
679/// - The key exchange provides forward secrecy if new keypairs are generated for each session
680///
681/// ## Arguments
682///
683/// * `server_pk` - The server's public key
684/// * `server_sk` - The server's secret key
685/// * `client_pk` - The client's public key
686///
687/// ## Returns
688///
689/// * `Result<SessionKeys>` - A pair of session keys for bidirectional communication
690///
691/// ## Errors
692///
693/// Returns an error if the key computation fails, which can happen if:
694/// - The public keys are invalid
695/// - The secret key is invalid
696/// - The public keys represent the same identity (server_pk == client_pk)
697///
698/// ## Example
699///
700/// ```rust
701/// use libsodium_rs as sodium;
702/// use sodium::crypto_kx;
703/// use sodium::ensure_init;
704///
705/// // Initialize libsodium
706/// ensure_init().expect("Failed to initialize libsodium");
707///
708/// // Generate keypairs for client and server
709/// let client_keypair = crypto_kx::KeyPair::generate().unwrap();
710/// let client_pk = client_keypair.public_key;
711/// let client_sk = client_keypair.secret_key;
712/// let server_keypair = crypto_kx::KeyPair::generate().unwrap();
713/// let server_pk = server_keypair.public_key;
714/// let server_sk = server_keypair.secret_key;
715///
716/// // Server computes session keys
717/// let server_keys = crypto_kx::server_session_keys(
718///     &server_pk,
719///     &server_sk,
720///     &client_pk,
721/// ).unwrap();
722///
723/// // Now server_keys.tx can be used to encrypt messages to the client,
724/// // and server_keys.rx can be used to decrypt messages from the client.
725/// ```
726pub fn server_session_keys(
727    server_pk: &PublicKey,
728    server_sk: &SecretKey,
729    client_pk: &PublicKey,
730) -> Result<SessionKeys> {
731    let mut rx = [0u8; SESSIONKEYBYTES];
732    let mut tx = [0u8; SESSIONKEYBYTES];
733
734    let result = unsafe {
735        libsodium_sys::crypto_kx_server_session_keys(
736            rx.as_mut_ptr(),
737            tx.as_mut_ptr(),
738            server_pk.as_bytes().as_ptr(),
739            server_sk.as_bytes().as_ptr(),
740            client_pk.as_bytes().as_ptr(),
741        )
742    };
743
744    if result != 0 {
745        return Err(SodiumError::OperationError(
746            "failed to compute session keys".into(),
747        ));
748    }
749
750    Ok(SessionKeys { rx, tx })
751}
752
753#[cfg(test)]
754mod tests {
755    use super::*;
756
757    #[test]
758    fn test_keypair_generation() {
759        let keypair = KeyPair::generate().unwrap();
760        let pk = keypair.public_key;
761        let sk = keypair.secret_key;
762        assert_eq!(pk.as_bytes().len(), PUBLICKEYBYTES);
763        assert_eq!(sk.as_bytes().len(), SECRETKEYBYTES);
764    }
765
766    #[test]
767    fn test_constants_and_primitive() {
768        assert_eq!(seedbytes(), SEEDBYTES);
769        assert_eq!(sessionkeybytes(), SESSIONKEYBYTES);
770        assert_eq!(primitive(), PRIMITIVE);
771    }
772
773    #[test]
774    fn test_seed_keypair() {
775        let seed = [42u8; SEEDBYTES];
776        let keypair1 = KeyPair::from_seed(&seed).unwrap();
777        let keypair2 = KeyPair::from_seed(&seed).unwrap();
778
779        assert_eq!(keypair1.public_key, keypair2.public_key);
780        assert_eq!(keypair1.secret_key, keypair2.secret_key);
781    }
782
783    #[test]
784    fn test_key_exchange() {
785        // Generate keypairs for client and server
786        let client_keypair = KeyPair::generate().unwrap();
787        let client_pk = client_keypair.public_key;
788        let client_sk = client_keypair.secret_key;
789        let server_keypair = KeyPair::generate().unwrap();
790        let server_pk = server_keypair.public_key;
791        let server_sk = server_keypair.secret_key;
792
793        // Compute session keys
794        let client_keys = client_session_keys(&client_pk, &client_sk, &server_pk).unwrap();
795        let server_keys = server_session_keys(&server_pk, &server_sk, &client_pk).unwrap();
796
797        // Verify that the session keys match (client_tx = server_rx and client_rx = server_tx)
798        assert_eq!(client_keys.tx, server_keys.rx);
799        assert_eq!(client_keys.rx, server_keys.tx);
800    }
801
802    #[test]
803    fn test_public_key_asref() {
804        let keypair = KeyPair::generate().unwrap();
805        let public_key = keypair.public_key;
806        let bytes_ref: &[u8] = public_key.as_ref();
807        assert_eq!(bytes_ref.len(), PUBLICKEYBYTES);
808        assert_eq!(bytes_ref, public_key.as_bytes());
809    }
810
811    #[test]
812    fn test_public_key_try_from_slice() {
813        // Valid case
814        let bytes = [42u8; PUBLICKEYBYTES];
815        let public_key = PublicKey::try_from(&bytes[..]).unwrap();
816        assert_eq!(public_key.as_bytes(), &bytes);
817
818        // Invalid case - wrong length
819        let short_bytes = [42u8; PUBLICKEYBYTES - 1];
820        assert!(PublicKey::try_from(&short_bytes[..]).is_err());
821
822        let long_bytes = [42u8; PUBLICKEYBYTES + 1];
823        assert!(PublicKey::try_from(&long_bytes[..]).is_err());
824    }
825
826    #[test]
827    fn test_public_key_from_bytes() {
828        let bytes = [42u8; PUBLICKEYBYTES];
829        let public_key = PublicKey::from(bytes);
830        assert_eq!(public_key.as_bytes(), &bytes);
831    }
832
833    #[test]
834    fn test_public_key_into_bytes() {
835        let bytes = [42u8; PUBLICKEYBYTES];
836        let public_key = PublicKey::from(bytes);
837        let array: [u8; PUBLICKEYBYTES] = public_key.into();
838        assert_eq!(array, bytes);
839    }
840
841    #[test]
842    fn test_secret_key_asref() {
843        let keypair = KeyPair::generate().unwrap();
844        let secret_key = keypair.secret_key;
845        let bytes_ref: &[u8] = secret_key.as_ref();
846        assert_eq!(bytes_ref.len(), SECRETKEYBYTES);
847        assert_eq!(bytes_ref, secret_key.as_bytes());
848    }
849
850    #[test]
851    fn test_secret_key_try_from_slice() {
852        // Valid case
853        let bytes = [42u8; SECRETKEYBYTES];
854        let secret_key = SecretKey::try_from(&bytes[..]).unwrap();
855        assert_eq!(secret_key.as_bytes(), &bytes);
856
857        // Invalid case - wrong length
858        let short_bytes = [42u8; SECRETKEYBYTES - 1];
859        assert!(SecretKey::try_from(&short_bytes[..]).is_err());
860
861        let long_bytes = [42u8; SECRETKEYBYTES + 1];
862        assert!(SecretKey::try_from(&long_bytes[..]).is_err());
863    }
864
865    #[test]
866    fn test_secret_key_from_bytes() {
867        let bytes = [42u8; SECRETKEYBYTES];
868        let secret_key = SecretKey::from(bytes);
869        assert_eq!(secret_key.as_bytes(), &bytes);
870    }
871
872    #[test]
873    fn test_secret_key_into_bytes() {
874        let bytes = [42u8; SECRETKEYBYTES];
875        let secret_key = SecretKey::from(bytes);
876        let array: [u8; SECRETKEYBYTES] = secret_key.into();
877        assert_eq!(array, bytes);
878    }
879
880    #[test]
881    fn test_secret_key_zeroization() {
882        let bytes = [42u8; SECRETKEYBYTES];
883        let secret_key = SecretKey::from(bytes);
884
885        // Convert to array and drop the original
886        let array: [u8; SECRETKEYBYTES] = secret_key.into();
887        assert_eq!(array, bytes);
888
889        // The original secret_key is now dropped and should be zeroized
890        // (We can't directly test this as the memory is freed, but the
891        // ZeroizeOnDrop trait ensures it happens)
892    }
893
894    #[test]
895    fn test_roundtrip_conversions() {
896        // Test PublicKey roundtrip
897        let keypair = KeyPair::generate().unwrap();
898        let pk = keypair.public_key;
899        let pk_bytes = pk.as_bytes().to_vec();
900
901        // Via TryFrom
902        let pk2 = PublicKey::try_from(&pk_bytes[..]).unwrap();
903        assert_eq!(pk2.as_bytes(), pk_bytes);
904
905        // Via From array
906        let pk_array: [u8; PUBLICKEYBYTES] = pk2.into();
907        let pk3 = PublicKey::from(pk_array);
908        assert_eq!(pk3.as_bytes(), pk_bytes);
909
910        // Test SecretKey roundtrip
911        let sk = keypair.secret_key;
912        let sk_bytes = sk.as_bytes().to_vec();
913
914        // Via TryFrom
915        let sk2 = SecretKey::try_from(&sk_bytes[..]).unwrap();
916        assert_eq!(sk2.as_bytes(), sk_bytes);
917
918        // Via From array
919        let sk_array: [u8; SECRETKEYBYTES] = sk2.into();
920        let sk3 = SecretKey::from(sk_array);
921        assert_eq!(sk3.as_bytes(), sk_bytes);
922    }
923}