1use zeroize::Zeroizing;
37
38use super::crypto_core::{crypto_scalarmult, crypto_scalarmult_base};
39use super::crypto_generichash::{
40 crypto_generichash, crypto_generichash_final, crypto_generichash_init,
41 crypto_generichash_update,
42};
43use crate::constants::{
44 CRYPTO_KX_PUBLICKEYBYTES, CRYPTO_KX_SECRETKEYBYTES, CRYPTO_KX_SEEDBYTES,
45 CRYPTO_KX_SESSIONKEYBYTES, CRYPTO_SCALARMULT_BYTES,
46};
47use crate::error::Error;
48use crate::types::*;
49
50pub type PublicKey = [u8; CRYPTO_KX_PUBLICKEYBYTES];
52pub type SecretKey = [u8; CRYPTO_KX_SECRETKEYBYTES];
54pub type SessionKey = [u8; CRYPTO_KX_SESSIONKEYBYTES];
56
57pub fn crypto_kx_seed_keypair(
67 seed: &[u8; CRYPTO_KX_SEEDBYTES],
68) -> Result<(PublicKey, SecretKey), Error> {
69 let mut sk = SecretKey::default();
70 let mut pk = PublicKey::default();
71
72 crypto_generichash(&mut sk, seed, None)?;
73
74 crypto_scalarmult_base(&mut pk, &sk);
75
76 Ok((pk, sk))
77}
78
79pub fn crypto_kx_keypair() -> (PublicKey, SecretKey) {
83 let sk = SecretKey::generate();
84 let mut pk = PublicKey::default();
85
86 crypto_scalarmult_base(&mut pk, &sk);
87
88 (pk, sk)
89}
90
91fn crypto_kx(
92 x1: &mut SessionKey,
93 x2: &mut SessionKey,
94 client_pk: &PublicKey,
95 server_pk: &PublicKey,
96 shared_secret: Zeroizing<[u8; CRYPTO_SCALARMULT_BYTES]>,
97) -> Result<(), Error> {
98 let mut keys = Zeroizing::new([0u8; 2 * CRYPTO_KX_SESSIONKEYBYTES]);
99
100 let mut hasher = crypto_generichash_init(None, 2 * CRYPTO_KX_SESSIONKEYBYTES)?;
101 crypto_generichash_update(&mut hasher, &shared_secret[..]);
102 crypto_generichash_update(&mut hasher, client_pk);
103 crypto_generichash_update(&mut hasher, server_pk);
104 crypto_generichash_final(hasher, &mut keys[..])?;
105
106 x1.copy_from_slice(&keys[..CRYPTO_KX_SESSIONKEYBYTES]);
107 x2.copy_from_slice(&keys[CRYPTO_KX_SESSIONKEYBYTES..]);
108
109 Ok(())
110}
111
112pub fn crypto_kx_client_session_keys(
122 rx: &mut SessionKey,
123 tx: &mut SessionKey,
124 client_pk: &PublicKey,
125 client_sk: &SecretKey,
126 server_pk: &PublicKey,
127) -> Result<(), Error> {
128 let mut shared_secret = Zeroizing::new([0u8; CRYPTO_SCALARMULT_BYTES]);
129
130 crypto_scalarmult(&mut shared_secret, client_sk, server_pk)?;
131
132 crypto_kx(rx, tx, client_pk, server_pk, shared_secret)
133}
134
135pub fn crypto_kx_server_session_keys(
145 rx: &mut SessionKey,
146 tx: &mut SessionKey,
147 server_pk: &PublicKey,
148 server_sk: &SecretKey,
149 client_pk: &PublicKey,
150) -> Result<(), Error> {
151 let mut shared_secret = Zeroizing::new([0u8; CRYPTO_SCALARMULT_BYTES]);
152
153 crypto_scalarmult(&mut shared_secret, server_sk, client_pk)?;
154
155 crypto_kx(tx, rx, client_pk, server_pk, shared_secret)
156}
157
158#[cfg(all(test, dryoc_native_tests))]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn test_kx_rejects_low_order_public_keys() {
164 use sodiumoxide::crypto::kx;
165
166 let (client_pk, client_sk) = crypto_kx_keypair();
167 let mut rx = SessionKey::default();
168 let mut tx = SessionKey::default();
169 let mut one = PublicKey::default();
170 one[0] = 1;
171
172 for server_pk in [PublicKey::default(), one] {
173 assert!(
174 crypto_kx_client_session_keys(
175 &mut rx,
176 &mut tx,
177 &client_pk,
178 &client_sk,
179 &server_pk,
180 )
181 .is_err()
182 );
183 assert!(
184 kx::client_session_keys(
185 &kx::PublicKey::from_slice(&client_pk).unwrap(),
186 &kx::SecretKey::from_slice(&client_sk).unwrap(),
187 &kx::PublicKey::from_slice(&server_pk).unwrap(),
188 )
189 .is_err()
190 );
191 }
192 }
193
194 #[test]
195 fn test_kx() {
196 for _ in 0..20 {
197 let (client_pk, client_sk) = crypto_kx_keypair();
198 let (server_pk, server_sk) = crypto_kx_keypair();
199
200 let (mut crx, mut ctx, mut srx, mut stx) = (
201 SessionKey::default(),
202 SessionKey::default(),
203 SessionKey::default(),
204 SessionKey::default(),
205 );
206
207 crypto_kx_client_session_keys(&mut crx, &mut ctx, &client_pk, &client_sk, &server_pk)
208 .expect("client kx failed");
209
210 crypto_kx_server_session_keys(&mut srx, &mut stx, &server_pk, &server_sk, &client_pk)
211 .expect("server kx failed");
212
213 assert_eq!(crx, stx);
214 assert_eq!(ctx, srx);
215
216 use sodiumoxide::crypto::kx;
217
218 let client_pk = kx::PublicKey::from_slice(&client_pk).expect("client pk failed");
219 let client_sk = kx::SecretKey::from_slice(&client_sk).expect("client sk failed");
220 let server_pk = kx::PublicKey::from_slice(&server_pk).expect("server pk failed");
221 let server_sk = kx::SecretKey::from_slice(&server_sk).expect("server sk failed");
222
223 let (rx1, tx1) = match kx::client_session_keys(&client_pk, &client_sk, &server_pk) {
224 Ok((rx, tx)) => (rx, tx),
225 Err(()) => panic!("bad server signature"),
226 };
227
228 let (rx2, tx2) = match kx::server_session_keys(&server_pk, &server_sk, &client_pk) {
230 Ok((rx, tx)) => (rx, tx),
231 Err(()) => panic!("bad client signature"),
232 };
233
234 assert_eq!(rx1.as_ref(), crx);
235 assert_eq!(rx2.as_ref(), srx);
236 assert_eq!(tx1.as_ref(), ctx);
237 assert_eq!(tx2.as_ref(), stx);
238 }
239 }
240}