1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
//! X25519 key exchange for secure P2P communication.
//!
//! This module provides Diffie-Hellman key exchange using the X25519 elliptic curve,
//! enabling peers in the CHIE network to establish secure encrypted channels.
//!
//! # Features
//! - X25519 Diffie-Hellman key exchange
//! - Ephemeral and static key support
//! - Shared secret derivation with HKDF
//! - Key serialization for network transmission
//!
//! # Example
//! ```
//! use chie_crypto::keyexchange::{KeyExchange, KeyExchangeKeypair};
//!
//! // Alice generates a keypair
//! let alice = KeyExchangeKeypair::generate();
//! // Bob generates a keypair
//! let bob = KeyExchangeKeypair::generate();
//!
//! // Exchange public keys and derive shared secret
//! let alice_shared = alice.exchange(bob.public_key());
//! let bob_shared = bob.exchange(alice.public_key());
//!
//! // Both parties now have the same shared secret
//! assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
//! ```
use thiserror::Error;
use x25519_dalek::{PublicKey as X25519PublicKey, StaticSecret};
use zeroize::{Zeroize, ZeroizeOnDrop};
/// Shared secret derived from key exchange (32 bytes).
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
pub struct SharedSecret([u8; 32]);
/// Key exchange keypair for X25519 Diffie-Hellman.
pub struct KeyExchangeKeypair {
secret: StaticSecret,
public: X25519PublicKey,
}
/// Key exchange trait for performing Diffie-Hellman.
pub trait KeyExchange {
/// Perform key exchange to derive a shared secret.
fn exchange(&self, their_public: &X25519PublicKey) -> SharedSecret;
}
/// Errors that can occur during key exchange operations.
#[derive(Debug, Error)]
pub enum KeyExchangeError {
/// Invalid public key (low-order point).
#[error("Invalid public key")]
InvalidPublicKey,
/// Invalid secret key.
#[error("Invalid secret key")]
InvalidSecretKey,
/// Shared secret derivation failed.
#[error("Shared secret derivation failed")]
DerivationFailed,
}
pub type KeyExchangeResult<T> = Result<T, KeyExchangeError>;
impl SharedSecret {
/// Create a new shared secret from bytes.
pub fn from_bytes(bytes: [u8; 32]) -> Self {
Self(bytes)
}
/// Get the shared secret as a byte slice.
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
/// Convert to byte array.
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
/// Derive an encryption key from the shared secret using HKDF.
///
/// # Arguments
/// * `info` - Context-specific information for key derivation
///
/// # Returns
/// A 32-byte derived key suitable for symmetric encryption.
pub fn derive_key(&self, info: &[u8]) -> [u8; 32] {
use crate::kdf::hkdf_extract_expand;
let salt = b"chie-p2p-v1";
hkdf_extract_expand(&self.0, salt, info)
}
/// Derive multiple keys from the shared secret.
///
/// # Arguments
/// * `infos` - Slice of context information for each key
///
/// # Returns
/// Vector of 32-byte derived keys.
pub fn derive_keys(&self, infos: &[&[u8]]) -> Vec<[u8; 32]> {
infos.iter().map(|info| self.derive_key(info)).collect()
}
}
impl KeyExchangeKeypair {
/// Generate a new random keypair.
///
/// # Example
/// ```
/// use chie_crypto::keyexchange::KeyExchangeKeypair;
///
/// let keypair = KeyExchangeKeypair::generate();
/// ```
pub fn generate() -> Self {
let secret = StaticSecret::random_from_rng(rand_core06::OsRng);
let public = X25519PublicKey::from(&secret);
Self { secret, public }
}
/// Create a keypair from a secret key (32 bytes).
///
/// # Arguments
/// * `secret_bytes` - 32-byte secret key
///
/// # Example
/// ```
/// use chie_crypto::keyexchange::KeyExchangeKeypair;
///
/// let secret = [1u8; 32];
/// let keypair = KeyExchangeKeypair::from_bytes(secret);
/// ```
pub fn from_bytes(secret_bytes: [u8; 32]) -> Self {
let secret = StaticSecret::from(secret_bytes);
let public = X25519PublicKey::from(&secret);
Self { secret, public }
}
/// Get the public key.
///
/// # Returns
/// Reference to the X25519 public key.
pub fn public_key(&self) -> &X25519PublicKey {
&self.public
}
/// Get the public key as bytes.
///
/// # Returns
/// 32-byte array containing the public key.
pub fn public_key_bytes(&self) -> [u8; 32] {
*self.public.as_bytes()
}
}
impl KeyExchange for KeyExchangeKeypair {
/// Perform X25519 Diffie-Hellman key exchange.
///
/// # Arguments
/// * `their_public` - The other party's public key
///
/// # Returns
/// Shared secret derived from the key exchange.
///
/// # Example
/// ```
/// use chie_crypto::keyexchange::{KeyExchange, KeyExchangeKeypair};
///
/// let alice = KeyExchangeKeypair::generate();
/// let bob = KeyExchangeKeypair::generate();
///
/// let shared = alice.exchange(bob.public_key());
/// ```
fn exchange(&self, their_public: &X25519PublicKey) -> SharedSecret {
let shared = self.secret.diffie_hellman(their_public);
SharedSecret(*shared.as_bytes())
}
}
/// Create an ephemeral keypair for one-time key exchange.
///
/// # Returns
/// A newly generated keypair intended for ephemeral use.
///
/// # Example
/// ```
/// use chie_crypto::keyexchange::ephemeral_keypair;
///
/// let ephemeral = ephemeral_keypair();
/// ```
pub fn ephemeral_keypair() -> KeyExchangeKeypair {
KeyExchangeKeypair::generate()
}
/// Perform a complete key exchange and derive an encryption key.
///
/// # Arguments
/// * `our_secret` - Our keypair
/// * `their_public` - The other party's public key
/// * `context` - Context information for key derivation
///
/// # Returns
/// A 32-byte encryption key derived from the shared secret.
///
/// # Example
/// ```
/// use chie_crypto::keyexchange::{KeyExchangeKeypair, exchange_and_derive};
///
/// let alice = KeyExchangeKeypair::generate();
/// let bob = KeyExchangeKeypair::generate();
///
/// let alice_key = exchange_and_derive(&alice, bob.public_key(), b"session-1");
/// let bob_key = exchange_and_derive(&bob, alice.public_key(), b"session-1");
///
/// assert_eq!(alice_key, bob_key);
/// ```
pub fn exchange_and_derive(
our_secret: &KeyExchangeKeypair,
their_public: &X25519PublicKey,
context: &[u8],
) -> [u8; 32] {
let shared = our_secret.exchange(their_public);
shared.derive_key(context)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_key_exchange_roundtrip() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let alice_shared = alice.exchange(bob.public_key());
let bob_shared = bob.exchange(alice.public_key());
assert_eq!(alice_shared.as_bytes(), bob_shared.as_bytes());
}
#[test]
fn test_different_pairs_different_secrets() {
let alice1 = KeyExchangeKeypair::generate();
let alice2 = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let shared1 = alice1.exchange(bob.public_key());
let shared2 = alice2.exchange(bob.public_key());
assert_ne!(shared1.as_bytes(), shared2.as_bytes());
}
#[test]
fn test_derive_key_from_shared_secret() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let alice_shared = alice.exchange(bob.public_key());
let bob_shared = bob.exchange(alice.public_key());
let alice_key = alice_shared.derive_key(b"encryption");
let bob_key = bob_shared.derive_key(b"encryption");
assert_eq!(alice_key, bob_key);
}
#[test]
fn test_different_info_different_keys() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let shared = alice.exchange(bob.public_key());
let key1 = shared.derive_key(b"encryption");
let key2 = shared.derive_key(b"authentication");
assert_ne!(key1, key2);
}
#[test]
fn test_public_key_serialization() {
let keypair = KeyExchangeKeypair::generate();
let public_bytes = keypair.public_key_bytes();
// Should be 32 bytes
assert_eq!(public_bytes.len(), 32);
// Should be non-zero
assert_ne!(public_bytes, [0u8; 32]);
}
#[test]
fn test_keypair_from_bytes() {
let secret_bytes = [42u8; 32];
let keypair = KeyExchangeKeypair::from_bytes(secret_bytes);
// Should produce valid public key
assert_ne!(keypair.public_key_bytes(), [0u8; 32]);
}
#[test]
fn test_commutative_exchange() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let carol = KeyExchangeKeypair::generate();
let alice_bob = alice.exchange(bob.public_key());
let bob_alice = bob.exchange(alice.public_key());
let alice_carol = alice.exchange(carol.public_key());
// Same pair = same secret
assert_eq!(alice_bob.as_bytes(), bob_alice.as_bytes());
// Different pair = different secret
assert_ne!(alice_bob.as_bytes(), alice_carol.as_bytes());
}
#[test]
fn test_ephemeral_keypair() {
let ephemeral1 = ephemeral_keypair();
let ephemeral2 = ephemeral_keypair();
// Different ephemeral keypairs
assert_ne!(ephemeral1.public_key_bytes(), ephemeral2.public_key_bytes());
}
#[test]
fn test_exchange_and_derive() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let alice_key = exchange_and_derive(&alice, bob.public_key(), b"test-session");
let bob_key = exchange_and_derive(&bob, alice.public_key(), b"test-session");
assert_eq!(alice_key, bob_key);
}
#[test]
fn test_derive_multiple_keys() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let shared = alice.exchange(bob.public_key());
let keys = shared.derive_keys(&[b"key1", b"key2", b"key3"]);
assert_eq!(keys.len(), 3);
assert_ne!(keys[0], keys[1]);
assert_ne!(keys[1], keys[2]);
assert_ne!(keys[0], keys[2]);
}
#[test]
fn test_shared_secret_serialization() {
let alice = KeyExchangeKeypair::generate();
let bob = KeyExchangeKeypair::generate();
let shared = alice.exchange(bob.public_key());
let bytes = shared.to_bytes();
let restored = SharedSecret::from_bytes(bytes);
assert_eq!(shared.as_bytes(), restored.as_bytes());
}
}