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}