dryoc/
kx.rs

1//! # Key exchange functions
2//!
3//! [`Session`] implements libsodium's key exchange functions, which use a
4//! combination of Curve25519, Diffie-Hellman, and Blake2b to generate shared
5//! session keys between two parties who know each other's public keys.
6//!
7//! You should use [`Session`] when you want to:
8//!
9//! * derive shared secrets between two parties
10//! * use public-key cryptography, but do so with another cipher that only
11//!   supports pre-shared secrets
12//! * create a session key or token that can't be used to derive the original
13//!   inputs should it become compromised
14//!
15//! # Rustaceous API example
16//!
17//! ```
18//! use dryoc::kx::*;
19//!
20//! // Generate random client/server keypairs
21//! let client_keypair = KeyPair::gen();
22//! let server_keypair = KeyPair::gen();
23//!
24//! // Compute client session keys, into default stack-allocated byte array
25//! let client_session_keys =
26//!     Session::new_client_with_defaults(&client_keypair, &server_keypair.public_key)
27//!         .expect("compute client failed");
28//!
29//! // Compute server session keys, into default stack-allocated byte array
30//! let server_session_keys =
31//!     Session::new_server_with_defaults(&server_keypair, &client_keypair.public_key)
32//!         .expect("compute client failed");
33//!
34//! let (client_rx, client_tx) = client_session_keys.into_parts();
35//! let (server_rx, server_tx) = server_session_keys.into_parts();
36//!
37//! // Client Rx should match server Tx keys
38//! assert_eq!(client_rx, server_tx);
39//! // Client Tx should match server Rx keys
40//! assert_eq!(client_tx, server_rx);
41//! ```
42//!
43//! ## Additional resources
44//!
45//! * See <https://doc.libsodium.org/key_exchange> for additional details on key
46//!   exchange
47
48#[cfg(feature = "serde")]
49use serde::{Deserialize, Serialize};
50use zeroize::Zeroize;
51
52use crate::classic::crypto_kx::{crypto_kx_client_session_keys, crypto_kx_server_session_keys};
53use crate::constants::{
54    CRYPTO_KX_PUBLICKEYBYTES, CRYPTO_KX_SECRETKEYBYTES, CRYPTO_KX_SESSIONKEYBYTES,
55};
56use crate::error::Error;
57use crate::types::*;
58
59/// Stack-allocated session key type alias
60pub type SessionKey = StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>;
61/// Stack-allocated public key type alias
62pub type PublicKey = StackByteArray<CRYPTO_KX_PUBLICKEYBYTES>;
63/// Stack-allocated secret key type alias
64pub type SecretKey = StackByteArray<CRYPTO_KX_SECRETKEYBYTES>;
65/// Stack-allocated keypair type alias
66pub type KeyPair = crate::keypair::KeyPair<PublicKey, SecretKey>;
67
68#[cfg_attr(
69    feature = "serde",
70    derive(Zeroize, Clone, Debug, Serialize, Deserialize)
71)]
72#[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))]
73/// Key derivation implemantation based on Curve25519, Diffie-Hellman, and
74/// Blake2b. Compatible with libsodium's `crypto_kx_*` functions.
75pub struct Session<SessionKey: ByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize> {
76    rx_key: SessionKey,
77    tx_key: SessionKey,
78}
79
80/// Stack-allocated type alias for [`Session`]. Provided for convenience.
81pub type StackSession = Session<SessionKey>;
82
83#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
84#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
85pub mod protected {
86    //! #  Protected memory type aliases for [`Session`]
87    //!
88    //! This mod provides re-exports of type aliases for protected memory usage
89    //! with [`Session`]. These type aliases are provided for
90    //! convenience.
91    //!
92    //! ## Example
93    //!
94    //! ```
95    //! use dryoc::kx::Session;
96    //! use dryoc::kx::protected::*;
97    //!
98    //! // Generate random client/server keypairs
99    //! let client_keypair =
100    //!     LockedROKeyPair::gen_readonly_locked_keypair().expect("couldn't generate client keypair");
101    //! let server_keypair =
102    //!     LockedROKeyPair::gen_readonly_locked_keypair().expect("couldn't generate server keypair");
103    //!
104    //! // Compute client session keys, into default stack-allocated byte array
105    //! let client_session_keys: LockedSession =
106    //!     Session::new_client(&client_keypair, &server_keypair.public_key)
107    //!         .expect("compute client failed");
108    //!
109    //! // Compute server session keys, into default stack-allocated byte array
110    //! let server_session_keys: LockedSession =
111    //!     Session::new_server(&server_keypair, &client_keypair.public_key)
112    //!         .expect("compute client failed");
113    //!
114    //! let (client_rx, client_tx) = client_session_keys.into_parts();
115    //! let (server_rx, server_tx) = server_session_keys.into_parts();
116    //!
117    //! // Client Rx should match server Tx keys
118    //! assert_eq!(client_rx.as_slice(), server_tx.as_slice());
119    //! // Client Tx should match server Rx keys
120    //! assert_eq!(client_tx.as_slice(), server_rx.as_slice());
121    //! ```
122    use super::*;
123    pub use crate::keypair::protected::*;
124
125    /// Heap-allocated, paged-aligned session key type alias for use with
126    /// protected memory
127    pub type SessionKey = HeapByteArray<CRYPTO_KX_SESSIONKEYBYTES>;
128    /// Heap-allocated, paged-aligned public key type alias for use with
129    /// protected memory
130    pub type PublicKey = HeapByteArray<CRYPTO_KX_PUBLICKEYBYTES>;
131    /// Heap-allocated, paged-aligned secret key type alias for use with
132    /// protected memory
133    pub type SecretKey = HeapByteArray<CRYPTO_KX_SECRETKEYBYTES>;
134
135    /// Heap-allocated, paged-aligned keypair type alias for use with
136    /// protected memory
137    pub type LockedKeyPair = crate::keypair::KeyPair<Locked<PublicKey>, Locked<SecretKey>>;
138    /// Heap-allocated, paged-aligned keypair type alias for use with
139    /// protected memory
140    pub type LockedROKeyPair = crate::keypair::KeyPair<LockedRO<PublicKey>, LockedRO<SecretKey>>;
141    /// Locked session keys type alias, for use with protected memory
142    pub type LockedSession = Session<Locked<SessionKey>>;
143}
144
145impl<SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize> Session<SessionKey> {
146    /// Computes client session keys, given `client_keypair` and
147    /// `server_public_key`, returning a new session upon success.
148    pub fn new_client<
149        PublicKey: ByteArray<CRYPTO_KX_PUBLICKEYBYTES> + Zeroize,
150        SecretKey: ByteArray<CRYPTO_KX_SECRETKEYBYTES> + Zeroize,
151    >(
152        client_keypair: &crate::keypair::KeyPair<PublicKey, SecretKey>,
153        server_public_key: &PublicKey,
154    ) -> Result<Self, Error> {
155        let mut rx_key = SessionKey::new_byte_array();
156        let mut tx_key = SessionKey::new_byte_array();
157
158        crypto_kx_client_session_keys(
159            rx_key.as_mut_array(),
160            tx_key.as_mut_array(),
161            client_keypair.public_key.as_array(),
162            client_keypair.secret_key.as_array(),
163            server_public_key.as_array(),
164        )?;
165
166        Ok(Self { rx_key, tx_key })
167    }
168
169    /// Computes server session keys, given `server_keypair` and
170    /// `client_public_key`, returning a new session upon success.
171    pub fn new_server<
172        PublicKey: ByteArray<CRYPTO_KX_PUBLICKEYBYTES> + Zeroize,
173        SecretKey: ByteArray<CRYPTO_KX_SECRETKEYBYTES> + Zeroize,
174    >(
175        server_keypair: &crate::keypair::KeyPair<PublicKey, SecretKey>,
176        client_public_key: &PublicKey,
177    ) -> Result<Self, Error> {
178        let mut rx_key = SessionKey::new_byte_array();
179        let mut tx_key = SessionKey::new_byte_array();
180
181        crypto_kx_server_session_keys(
182            rx_key.as_mut_array(),
183            tx_key.as_mut_array(),
184            server_keypair.public_key.as_array(),
185            server_keypair.secret_key.as_array(),
186            client_public_key.as_array(),
187        )?;
188
189        Ok(Self { rx_key, tx_key })
190    }
191}
192
193impl Session<SessionKey> {
194    /// Returns a new client session upon success using the default types for
195    /// the given `client_keypair` and `server_public_key`. Wraps
196    /// [`Session::new_client`], provided for convenience.
197    pub fn new_client_with_defaults<
198        PublicKey: ByteArray<CRYPTO_KX_PUBLICKEYBYTES> + Zeroize,
199        SecretKey: ByteArray<CRYPTO_KX_SECRETKEYBYTES> + Zeroize,
200    >(
201        client_keypair: &crate::keypair::KeyPair<PublicKey, SecretKey>,
202        server_public_key: &PublicKey,
203    ) -> Result<Self, Error> {
204        Self::new_client(client_keypair, server_public_key)
205    }
206
207    /// Returns a new server session upon success using the default types for
208    /// the given `server_keypair` and `client_public_key`. Wraps
209    /// [`Session::new_server`], provided for convenience.
210    pub fn new_server_with_defaults<
211        PublicKey: ByteArray<CRYPTO_KX_PUBLICKEYBYTES> + Zeroize,
212        SecretKey: ByteArray<CRYPTO_KX_SECRETKEYBYTES> + Zeroize,
213    >(
214        server_keypair: &crate::keypair::KeyPair<PublicKey, SecretKey>,
215        client_public_key: &PublicKey,
216    ) -> Result<Self, Error> {
217        Self::new_server(server_keypair, client_public_key)
218    }
219}
220
221impl<SessionKey: ByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize> Session<SessionKey> {
222    /// Moves the rx_key and tx_key out of this instance, returning them as a
223    /// tuple with `(rx_key, tx_key)`.
224    pub fn into_parts(self) -> (SessionKey, SessionKey) {
225        (self.rx_key, self.tx_key)
226    }
227
228    /// Returns a reference to a slice of the Rx session key.
229    #[inline]
230    pub fn rx_as_slice(&self) -> &[u8] {
231        self.rx_key.as_slice()
232    }
233
234    /// Returns a reference to a slice of the Tx session key.
235    #[inline]
236    pub fn tx_as_slice(&self) -> &[u8] {
237        self.tx_key.as_slice()
238    }
239
240    /// Returns a reference to an array of the Rx session key.
241    #[inline]
242    pub fn rx_as_array(&self) -> &[u8; CRYPTO_KX_SESSIONKEYBYTES] {
243        self.rx_key.as_array()
244    }
245
246    /// Returns a reference to an array of the Tx session key.
247    #[inline]
248    pub fn tx_as_array(&self) -> &[u8; CRYPTO_KX_SESSIONKEYBYTES] {
249        self.tx_key.as_array()
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[test]
258    fn test_kx() {
259        let client_keypair = KeyPair::gen();
260        let server_keypair = KeyPair::gen();
261
262        let client_session_keys =
263            Session::new_client_with_defaults(&client_keypair, &server_keypair.public_key)
264                .expect("compute client failed");
265
266        let server_session_keys =
267            Session::new_server_with_defaults(&server_keypair, &client_keypair.public_key)
268                .expect("compute client failed");
269
270        let (client_rx, client_tx) = client_session_keys.into_parts();
271        let (server_rx, server_tx) = server_session_keys.into_parts();
272
273        assert_eq!(client_rx, server_tx);
274        assert_eq!(client_tx, server_rx);
275    }
276}