1use zeroize::Zeroize;
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(
62 seed: &[u8; CRYPTO_KX_SEEDBYTES],
63) -> Result<(PublicKey, SecretKey), Error> {
64 let mut sk = SecretKey::default();
65 let mut pk = PublicKey::default();
66
67 crypto_generichash(&mut sk, seed, None)?;
68
69 crypto_scalarmult_base(&mut pk, &sk);
70
71 Ok((pk, sk))
72}
73
74pub fn crypto_kx_keypair() -> (PublicKey, SecretKey) {
78 let sk = SecretKey::gen();
79 let mut pk = PublicKey::default();
80
81 crypto_scalarmult_base(&mut pk, &sk);
82
83 (pk, sk)
84}
85
86fn crypto_kx(
87 x1: &mut SessionKey,
88 x2: &mut SessionKey,
89 client_pk: &PublicKey,
90 server_pk: &PublicKey,
91 mut shared_secret: [u8; CRYPTO_SCALARMULT_BYTES],
92) -> Result<(), Error> {
93 let mut keys = [0u8; 2 * CRYPTO_KX_SESSIONKEYBYTES];
94
95 let mut hasher = crypto_generichash_init(None, 2 * CRYPTO_KX_SESSIONKEYBYTES)?;
96 crypto_generichash_update(&mut hasher, &shared_secret);
97 shared_secret.zeroize();
98 crypto_generichash_update(&mut hasher, client_pk);
99 crypto_generichash_update(&mut hasher, server_pk);
100 crypto_generichash_final(hasher, &mut keys)?;
101
102 x1.copy_from_slice(&keys[..CRYPTO_KX_SESSIONKEYBYTES]);
103 x2.copy_from_slice(&keys[CRYPTO_KX_SESSIONKEYBYTES..]);
104
105 keys.zeroize();
106
107 Ok(())
108}
109
110pub fn crypto_kx_client_session_keys(
115 rx: &mut SessionKey,
116 tx: &mut SessionKey,
117 client_pk: &PublicKey,
118 client_sk: &SecretKey,
119 server_pk: &PublicKey,
120) -> Result<(), Error> {
121 let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
122
123 crypto_scalarmult(&mut shared_secret, client_sk, server_pk);
124
125 crypto_kx(rx, tx, client_pk, server_pk, shared_secret)
126}
127
128pub fn crypto_kx_server_session_keys(
133 rx: &mut SessionKey,
134 tx: &mut SessionKey,
135 server_pk: &PublicKey,
136 server_sk: &SecretKey,
137 client_pk: &PublicKey,
138) -> Result<(), Error> {
139 let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES];
140
141 crypto_scalarmult(&mut shared_secret, server_sk, client_pk);
142
143 crypto_kx(tx, rx, client_pk, server_pk, shared_secret)
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_kx() {
152 for _ in 0..20 {
153 let (client_pk, client_sk) = crypto_kx_keypair();
154 let (server_pk, server_sk) = crypto_kx_keypair();
155
156 let (mut crx, mut ctx, mut srx, mut stx) = (
157 SessionKey::default(),
158 SessionKey::default(),
159 SessionKey::default(),
160 SessionKey::default(),
161 );
162
163 crypto_kx_client_session_keys(&mut crx, &mut ctx, &client_pk, &client_sk, &server_pk)
164 .expect("client kx failed");
165
166 crypto_kx_server_session_keys(&mut srx, &mut stx, &server_pk, &server_sk, &client_pk)
167 .expect("server kx failed");
168
169 assert_eq!(crx, stx);
170 assert_eq!(ctx, srx);
171
172 use sodiumoxide::crypto::kx;
173
174 let client_pk = kx::PublicKey::from_slice(&client_pk).expect("client pk failed");
175 let client_sk = kx::SecretKey::from_slice(&client_sk).expect("client sk failed");
176 let server_pk = kx::PublicKey::from_slice(&server_pk).expect("server pk failed");
177 let server_sk = kx::SecretKey::from_slice(&server_sk).expect("server sk failed");
178
179 let (rx1, tx1) = match kx::client_session_keys(&client_pk, &client_sk, &server_pk) {
180 Ok((rx, tx)) => (rx, tx),
181 Err(()) => panic!("bad server signature"),
182 };
183
184 let (rx2, tx2) = match kx::server_session_keys(&server_pk, &server_sk, &client_pk) {
186 Ok((rx, tx)) => (rx, tx),
187 Err(()) => panic!("bad client signature"),
188 };
189
190 assert_eq!(rx1.as_ref(), crx);
191 assert_eq!(rx2.as_ref(), srx);
192 assert_eq!(tx1.as_ref(), ctx);
193 assert_eq!(tx2.as_ref(), stx);
194 }
195 }
196}