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}