1use zeroize::Zeroizing;
37
38use super::crypto_core::{crypto_scalarmult, crypto_scalarmult_base};
39use super::crypto_generichash::crypto_generichash;
40use crate::constants::{
41 CRYPTO_KX_PUBLICKEYBYTES, CRYPTO_KX_SECRETKEYBYTES, CRYPTO_KX_SEEDBYTES,
42 CRYPTO_KX_SESSIONKEYBYTES, CRYPTO_SCALARMULT_BYTES,
43};
44use crate::error::Error;
45use crate::types::*;
46
47pub type PublicKey = [u8; CRYPTO_KX_PUBLICKEYBYTES];
49pub type SecretKey = [u8; CRYPTO_KX_SECRETKEYBYTES];
51pub type SessionKey = [u8; CRYPTO_KX_SESSIONKEYBYTES];
53
54pub fn crypto_kx_seed_keypair(
64 seed: &[u8; CRYPTO_KX_SEEDBYTES],
65) -> Result<(PublicKey, SecretKey), Error> {
66 let mut sk = SecretKey::default();
67 let mut pk = PublicKey::default();
68
69 crypto_generichash(&mut sk, seed, None)?;
70
71 crypto_scalarmult_base(&mut pk, &sk);
72
73 Ok((pk, sk))
74}
75
76#[must_use]
80pub fn crypto_kx_keypair() -> (PublicKey, SecretKey) {
81 let sk = SecretKey::generate();
82 let mut pk = PublicKey::default();
83
84 crypto_scalarmult_base(&mut pk, &sk);
85
86 (pk, sk)
87}
88
89fn crypto_kx(
95 x1: &mut SessionKey,
96 x2: &mut SessionKey,
97 client_pk: &PublicKey,
98 server_pk: &PublicKey,
99 shared_secret: &[u8; CRYPTO_SCALARMULT_BYTES],
100) -> Result<(), Error> {
101 let mut input = Zeroizing::new([0u8; CRYPTO_SCALARMULT_BYTES + 2 * CRYPTO_KX_PUBLICKEYBYTES]);
102 let (secret, public_keys) = input.split_at_mut(CRYPTO_SCALARMULT_BYTES);
103 let (client, server) = public_keys.split_at_mut(CRYPTO_KX_PUBLICKEYBYTES);
104 secret.copy_from_slice(shared_secret);
105 client.copy_from_slice(client_pk);
106 server.copy_from_slice(server_pk);
107
108 let mut keys = Zeroizing::new([0u8; 2 * CRYPTO_KX_SESSIONKEYBYTES]);
109 crypto_generichash(&mut keys[..], &input[..], None)?;
110
111 x1.copy_from_slice(&keys[..CRYPTO_KX_SESSIONKEYBYTES]);
112 x2.copy_from_slice(&keys[CRYPTO_KX_SESSIONKEYBYTES..]);
113
114 Ok(())
115}
116
117pub fn crypto_kx_client_session_keys(
127 rx: &mut SessionKey,
128 tx: &mut SessionKey,
129 client_pk: &PublicKey,
130 client_sk: &SecretKey,
131 server_pk: &PublicKey,
132) -> Result<(), Error> {
133 let mut shared_secret = Zeroizing::new([0u8; CRYPTO_SCALARMULT_BYTES]);
134
135 crypto_scalarmult(&mut shared_secret, client_sk, server_pk)?;
136
137 crypto_kx(rx, tx, client_pk, server_pk, &shared_secret)
138}
139
140pub fn crypto_kx_server_session_keys(
150 rx: &mut SessionKey,
151 tx: &mut SessionKey,
152 server_pk: &PublicKey,
153 server_sk: &SecretKey,
154 client_pk: &PublicKey,
155) -> Result<(), Error> {
156 let mut shared_secret = Zeroizing::new([0u8; CRYPTO_SCALARMULT_BYTES]);
157
158 crypto_scalarmult(&mut shared_secret, server_sk, client_pk)?;
159
160 crypto_kx(tx, rx, client_pk, server_pk, &shared_secret)
161}
162
163#[cfg(all(test, dryoc_native_tests))]
164mod tests {
165 use super::*;
166 use crate::native_test_util::{
167 kx_client_session_keys, kx_seed_keypair, kx_server_session_keys,
168 };
169 use crate::scalarmult_curve25519::test_vectors::low_order_u_encodings;
170 use crate::utils::test_util::XorShift64;
171
172 #[test]
175 fn test_kx_seed_keypair_matches_libsodium() {
176 let mut rng = XorShift64::new(0x510e_527f_ade6_82d1);
177 for seed in [
178 [0u8; CRYPTO_KX_SEEDBYTES],
179 [0xff; CRYPTO_KX_SEEDBYTES],
180 rng.next_bytes32(),
181 ] {
182 let (pk, sk) = crypto_kx_seed_keypair(&seed).expect("seed keypair failed");
183 let (sodium_pk, sodium_sk) = kx_seed_keypair(&seed);
184 assert_eq!(sk, sodium_sk, "seed {seed:02x?}");
185 assert_eq!(pk, sodium_pk, "seed {seed:02x?}");
186 }
187 }
188
189 #[test]
192 fn test_kx_session_keys_match_libsodium_for_seeded_pair() {
193 let (client_pk, client_sk) = crypto_kx_seed_keypair(&[0x11; CRYPTO_KX_SEEDBYTES]).unwrap();
194 let (server_pk, server_sk) = crypto_kx_seed_keypair(&[0x22; CRYPTO_KX_SEEDBYTES]).unwrap();
195
196 let (mut crx, mut ctx, mut srx, mut stx) = (
197 SessionKey::default(),
198 SessionKey::default(),
199 SessionKey::default(),
200 SessionKey::default(),
201 );
202 crypto_kx_client_session_keys(&mut crx, &mut ctx, &client_pk, &client_sk, &server_pk)
203 .expect("client kx failed");
204 crypto_kx_server_session_keys(&mut srx, &mut stx, &server_pk, &server_sk, &client_pk)
205 .expect("server kx failed");
206 assert_eq!(crx, stx);
207 assert_eq!(ctx, srx);
208
209 let (so_crx, so_ctx) =
210 kx_client_session_keys(&client_pk, &client_sk, &server_pk).expect("libsodium client");
211 let (so_srx, so_stx) =
212 kx_server_session_keys(&server_pk, &server_sk, &client_pk).expect("libsodium server");
213 assert_eq!(crx, so_crx);
214 assert_eq!(ctx, so_ctx);
215 assert_eq!(srx, so_srx);
216 assert_eq!(stx, so_stx);
217 }
218
219 #[test]
223 fn test_kx_rejects_low_order_public_keys() {
224 let (pk, sk) = crypto_kx_seed_keypair(&[0x33; CRYPTO_KX_SEEDBYTES]).unwrap();
225
226 for peer_pk in low_order_u_encodings() {
227 let mut rx = [0xa5; CRYPTO_KX_SESSIONKEYBYTES];
228 let mut tx = [0x5a; CRYPTO_KX_SESSIONKEYBYTES];
229 assert!(
230 crypto_kx_client_session_keys(&mut rx, &mut tx, &pk, &sk, &peer_pk).is_err(),
231 "client {peer_pk:02x?}"
232 );
233 assert!(
234 crypto_kx_server_session_keys(&mut rx, &mut tx, &pk, &sk, &peer_pk).is_err(),
235 "server {peer_pk:02x?}"
236 );
237 assert_eq!(rx, [0xa5; CRYPTO_KX_SESSIONKEYBYTES]);
238 assert_eq!(tx, [0x5a; CRYPTO_KX_SESSIONKEYBYTES]);
239
240 assert!(
242 kx_client_session_keys(&pk, &sk, &peer_pk).is_err(),
243 "libsodium client {peer_pk:02x?}"
244 );
245 assert!(
246 kx_server_session_keys(&pk, &sk, &peer_pk).is_err(),
247 "libsodium server {peer_pk:02x?}"
248 );
249 }
250 }
251
252 #[test]
253 fn test_kx() {
254 for _ in 0..20 {
255 let (client_pk, client_sk) = crypto_kx_keypair();
256 let (server_pk, server_sk) = crypto_kx_keypair();
257
258 let (mut crx, mut ctx, mut srx, mut stx) = (
259 SessionKey::default(),
260 SessionKey::default(),
261 SessionKey::default(),
262 SessionKey::default(),
263 );
264
265 crypto_kx_client_session_keys(&mut crx, &mut ctx, &client_pk, &client_sk, &server_pk)
266 .expect("client kx failed");
267
268 crypto_kx_server_session_keys(&mut srx, &mut stx, &server_pk, &server_sk, &client_pk)
269 .expect("server kx failed");
270
271 assert_eq!(crx, stx);
272 assert_eq!(ctx, srx);
273
274 let (rx1, tx1) = match kx_client_session_keys(&client_pk, &client_sk, &server_pk) {
275 Ok((rx, tx)) => (rx, tx),
276 Err(()) => panic!("bad server signature"),
277 };
278
279 let (rx2, tx2) = match kx_server_session_keys(&server_pk, &server_sk, &client_pk) {
281 Ok((rx, tx)) => (rx, tx),
282 Err(()) => panic!("bad client signature"),
283 };
284
285 assert_eq!(rx1, crx);
286 assert_eq!(rx2, srx);
287 assert_eq!(tx1, ctx);
288 assert_eq!(tx2, stx);
289 }
290 }
291}