1use crate::identity::FourWordAddress;
5use anyhow::Result;
6use chrono::{DateTime, Duration, Utc};
7use hkdf::Hkdf;
8use rand_core::OsRng;
9use serde::{Deserialize, Serialize};
10use sha2::Sha256;
11use std::collections::HashMap;
12use std::sync::Arc;
13use tokio::sync::RwLock;
14use tracing::{info, warn};
15use x25519_dalek::{EphemeralSecret, PublicKey, SharedSecret};
16
17struct DebugEphemeralSecret(EphemeralSecret);
19
20impl std::fmt::Debug for DebugEphemeralSecret {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("EphemeralSecret")
23 .field("redacted", &"[REDACTED]")
24 .finish()
25 }
26}
27
28impl DebugEphemeralSecret {
29 fn new() -> Self {
30 Self(EphemeralSecret::random_from_rng(OsRng))
31 }
32
33 fn public_key(&self) -> PublicKey {
34 PublicKey::from(&self.0)
35 }
36
37 fn into_secret(self) -> EphemeralSecret {
38 self.0
39 }
40}
41
42pub struct KeyExchange {
44 identity: FourWordAddress,
46 sessions: Arc<RwLock<HashMap<FourWordAddress, KeyExchangeSession>>>,
48 established_keys: Arc<RwLock<HashMap<FourWordAddress, EstablishedKey>>>,
50 prekeys: Arc<RwLock<PrekeyBundle>>,
52}
53
54impl KeyExchange {
55 pub fn new(identity: FourWordAddress) -> Result<Self> {
57 let prekeys = PrekeyBundle::generate()?;
58
59 Ok(Self {
60 identity,
61 sessions: Arc::new(RwLock::new(HashMap::new())),
62 established_keys: Arc::new(RwLock::new(HashMap::new())),
63 prekeys: Arc::new(RwLock::new(prekeys)),
64 })
65 }
66
67 pub async fn initiate_exchange(&self, peer: FourWordAddress) -> Result<KeyExchangeMessage> {
69 let ephemeral_secret = DebugEphemeralSecret::new();
71 let ephemeral_public = ephemeral_secret.public_key();
72
73 let session = KeyExchangeSession {
75 _peer: peer.clone(),
76 our_ephemeral: Some(ephemeral_secret),
77 our_public: ephemeral_public,
78 _their_public: None,
79 _state: KeyExchangeState::_Initiated,
80 _created_at: Utc::now(),
81 expires_at: Utc::now() + Duration::minutes(5),
82 };
83
84 let mut sessions = self.sessions.write().await;
86 sessions.insert(peer.clone(), session);
87
88 let message = KeyExchangeMessage {
90 sender: self.identity.clone(),
91 recipient: peer.clone(),
92 message_type: KeyExchangeType::Initiation,
93 ephemeral_public: ephemeral_public.as_bytes().to_vec(),
94 prekey_id: None,
95 signature: self.sign_key_exchange(ephemeral_public.as_bytes()),
96 timestamp: Utc::now(),
97 };
98
99 info!("Initiated key exchange with {}", peer);
100 Ok(message)
101 }
102
103 pub async fn respond_to_exchange(
105 &self,
106 message: KeyExchangeMessage,
107 ) -> Result<KeyExchangeMessage> {
108 if !self.verify_key_exchange_signature(&message) {
110 return Err(anyhow::anyhow!("Invalid key exchange signature"));
111 }
112
113 let their_public_bytes: [u8; 32] = message
115 .ephemeral_public
116 .try_into()
117 .map_err(|_| anyhow::anyhow!("Invalid public key length"))?;
118 let their_public = PublicKey::from(their_public_bytes);
119
120 let our_ephemeral = DebugEphemeralSecret::new();
122 let our_public = our_ephemeral.public_key();
123
124 let shared_secret = our_ephemeral.into_secret().diffie_hellman(&their_public);
126
127 let (encryption_key, mac_key) = self.derive_keys(&shared_secret, &message.sender)?;
129
130 let established = EstablishedKey {
132 _peer: message.sender.clone(),
133 encryption_key,
134 _mac_key: mac_key,
135 _our_public: our_public,
136 _their_public: their_public,
137 _established_at: Utc::now(),
138 expires_at: Utc::now() + Duration::hours(24),
139 _messages_sent: 0,
140 _messages_received: 0,
141 };
142
143 let mut keys = self.established_keys.write().await;
144 keys.insert(message.sender.clone(), established);
145
146 let response = KeyExchangeMessage {
148 sender: self.identity.clone(),
149 recipient: message.sender.clone(),
150 message_type: KeyExchangeType::Response,
151 ephemeral_public: our_public.as_bytes().to_vec(),
152 prekey_id: None,
153 signature: self.sign_key_exchange(our_public.as_bytes()),
154 timestamp: Utc::now(),
155 };
156
157 info!("Responded to key exchange from {}", message.sender);
158 Ok(response)
159 }
160
161 pub async fn complete_exchange(&self, message: KeyExchangeMessage) -> Result<()> {
163 if !self.verify_key_exchange_signature(&message) {
165 return Err(anyhow::anyhow!("Invalid key exchange signature"));
166 }
167
168 let mut sessions = self.sessions.write().await;
170 let session = sessions
171 .get_mut(&message.sender)
172 .ok_or_else(|| anyhow::anyhow!("No pending key exchange session"))?;
173
174 let their_public_bytes: [u8; 32] = message
176 .ephemeral_public
177 .try_into()
178 .map_err(|_| anyhow::anyhow!("Invalid public key length"))?;
179 let their_public = PublicKey::from(their_public_bytes);
180
181 let our_ephemeral = session
183 .our_ephemeral
184 .take()
185 .ok_or_else(|| anyhow::anyhow!("Missing ephemeral secret"))?;
186 let shared_secret = our_ephemeral.into_secret().diffie_hellman(&their_public);
187
188 let (encryption_key, mac_key) = self.derive_keys(&shared_secret, &message.sender)?;
190
191 let established = EstablishedKey {
193 _peer: message.sender.clone(),
194 encryption_key,
195 _mac_key: mac_key,
196 _our_public: session.our_public,
197 _their_public: their_public,
198 _established_at: Utc::now(),
199 expires_at: Utc::now() + Duration::hours(24),
200 _messages_sent: 0,
201 _messages_received: 0,
202 };
203
204 sessions.remove(&message.sender);
206 drop(sessions);
207
208 let mut keys = self.established_keys.write().await;
209 keys.insert(message.sender.clone(), established);
210
211 info!("Completed key exchange with {}", message.sender);
212 Ok(())
213 }
214
215 pub async fn get_session_key(&self, peer: &FourWordAddress) -> Result<Vec<u8>> {
217 let keys = self.established_keys.read().await;
218
219 if let Some(established) = keys.get(peer) {
220 if established.expires_at > Utc::now() {
221 return Ok(established.encryption_key.clone());
222 }
223 warn!("Session key for {} has expired", peer);
224 }
225
226 Err(anyhow::anyhow!("No established session with {}", peer))
227 }
228
229 pub async fn use_prekey(
231 &self,
232 peer: FourWordAddress,
233 prekey: &PrekeyMessage,
234 ) -> Result<Vec<u8>> {
235 let ephemeral_secret = DebugEphemeralSecret::new();
237 let ephemeral_public = ephemeral_secret.public_key();
238
239 let prekey_bytes: [u8; 32] = prekey
241 .public_key
242 .clone()
243 .try_into()
244 .map_err(|_| anyhow::anyhow!("Invalid prekey length"))?;
245 let prekey_public = PublicKey::from(prekey_bytes);
246
247 let shared_secret = ephemeral_secret
249 .into_secret()
250 .diffie_hellman(&prekey_public);
251
252 let (encryption_key, mac_key) = self.derive_keys(&shared_secret, &peer)?;
254
255 let established = EstablishedKey {
257 _peer: peer.clone(),
258 encryption_key: encryption_key.clone(),
259 _mac_key: mac_key,
260 _our_public: ephemeral_public,
261 _their_public: prekey_public,
262 _established_at: Utc::now(),
263 expires_at: Utc::now() + Duration::hours(24),
264 _messages_sent: 0,
265 _messages_received: 0,
266 };
267
268 let mut keys = self.established_keys.write().await;
269 keys.insert(peer, established);
270
271 Ok(encryption_key)
272 }
273
274 pub async fn get_prekey_bundle(&self) -> PrekeyBundle {
276 let prekeys = self.prekeys.read().await;
277 prekeys.clone()
278 }
279
280 pub async fn rotate_prekeys(&self) -> Result<()> {
282 let new_prekeys = PrekeyBundle::generate()?;
283 let mut prekeys = self.prekeys.write().await;
284 *prekeys = new_prekeys;
285
286 info!("Rotated prekeys");
287 Ok(())
288 }
289
290 pub async fn cleanup_expired(&self) -> Result<()> {
292 let now = Utc::now();
293
294 let mut sessions = self.sessions.write().await;
296 sessions.retain(|_, session| session.expires_at > now);
297
298 let mut keys = self.established_keys.write().await;
300 let expired_count = keys.len();
301 keys.retain(|_, key| key.expires_at > now);
302 let removed = expired_count - keys.len();
303
304 if removed > 0 {
305 info!("Cleaned up {} expired sessions", removed);
306 }
307
308 Ok(())
309 }
310
311 fn derive_keys(
313 &self,
314 shared_secret: &SharedSecret,
315 peer: &FourWordAddress,
316 ) -> Result<(Vec<u8>, Vec<u8>)> {
317 let hkdf = Hkdf::<Sha256>::new(None, shared_secret.as_bytes());
318
319 let info = format!("{}-{}", self.identity, peer);
321
322 let mut okm = [0u8; 64];
324 hkdf.expand(info.as_bytes(), &mut okm)
325 .map_err(|_| anyhow::anyhow!("Key derivation failed"))?;
326
327 let encryption_key = okm[..32].to_vec();
328 let mac_key = okm[32..].to_vec();
329
330 Ok((encryption_key, mac_key))
331 }
332
333 fn sign_key_exchange(&self, public_key: &[u8]) -> Vec<u8> {
335 let mut hasher = blake3::Hasher::new();
337 hasher.update(self.identity.to_string().as_bytes());
338 hasher.update(public_key);
339 hasher.finalize().as_bytes().to_vec()
340 }
341
342 fn verify_key_exchange_signature(&self, message: &KeyExchangeMessage) -> bool {
344 let mut hasher = blake3::Hasher::new();
346 hasher.update(message.sender.to_string().as_bytes());
347 hasher.update(&message.ephemeral_public);
348 let expected = hasher.finalize().as_bytes().to_vec();
349
350 message.signature == expected
351 }
352}
353
354#[derive(Debug)]
356struct KeyExchangeSession {
357 _peer: FourWordAddress,
358 our_ephemeral: Option<DebugEphemeralSecret>,
359 our_public: PublicKey,
360 _their_public: Option<PublicKey>,
361 _state: KeyExchangeState,
362 _created_at: DateTime<Utc>,
363 expires_at: DateTime<Utc>,
364}
365
366#[derive(Debug, Clone, PartialEq)]
368enum KeyExchangeState {
369 _Initiated,
370 _Responded,
371 _Completed,
372}
373
374#[derive(Debug, Clone)]
376struct EstablishedKey {
377 _peer: FourWordAddress,
378 encryption_key: Vec<u8>,
379 _mac_key: Vec<u8>,
380 _our_public: PublicKey,
381 _their_public: PublicKey,
382 _established_at: DateTime<Utc>,
383 expires_at: DateTime<Utc>,
384 _messages_sent: u32,
385 _messages_received: u32,
386}
387
388#[derive(Debug, Clone, Serialize, Deserialize)]
390pub struct KeyExchangeMessage {
391 pub sender: FourWordAddress,
392 pub recipient: FourWordAddress,
393 pub message_type: KeyExchangeType,
394 pub ephemeral_public: Vec<u8>,
395 pub prekey_id: Option<u32>,
396 pub signature: Vec<u8>,
397 pub timestamp: DateTime<Utc>,
398}
399
400#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
402pub enum KeyExchangeType {
403 Initiation,
404 Response,
405 PrekeyBundle,
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize)]
410pub struct PrekeyBundle {
411 pub identity_key: Vec<u8>,
412 pub signed_prekey: SignedPrekey,
413 pub one_time_prekeys: Vec<OnetimePrekey>,
414 pub timestamp: DateTime<Utc>,
415}
416
417impl PrekeyBundle {
418 pub fn generate() -> Result<Self> {
420 let identity_secret = DebugEphemeralSecret::new();
422 let identity_public = identity_secret.public_key();
423
424 let signed_secret = DebugEphemeralSecret::new();
426 let signed_public = signed_secret.public_key();
427
428 let mut hasher = blake3::Hasher::new();
430 hasher.update(signed_public.as_bytes());
431 let signature = hasher.finalize().as_bytes().to_vec();
432
433 let signed_prekey = SignedPrekey {
434 id: rand::random(),
435 public_key: signed_public.as_bytes().to_vec(),
436 signature,
437 timestamp: Utc::now(),
438 };
439
440 let mut one_time_prekeys = Vec::new();
442 for i in 0..100 {
443 let secret = DebugEphemeralSecret::new();
444 let public = secret.public_key();
445
446 one_time_prekeys.push(OnetimePrekey {
447 id: i,
448 public_key: public.as_bytes().to_vec(),
449 });
450 }
451
452 Ok(Self {
453 identity_key: identity_public.as_bytes().to_vec(),
454 signed_prekey,
455 one_time_prekeys,
456 timestamp: Utc::now(),
457 })
458 }
459}
460
461#[derive(Debug, Clone, Serialize, Deserialize)]
463pub struct SignedPrekey {
464 pub id: u32,
465 pub public_key: Vec<u8>,
466 pub signature: Vec<u8>,
467 pub timestamp: DateTime<Utc>,
468}
469
470#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct OnetimePrekey {
473 pub id: u32,
474 pub public_key: Vec<u8>,
475}
476
477#[derive(Debug, Clone, Serialize, Deserialize)]
479pub struct PrekeyMessage {
480 pub prekey_id: u32,
481 pub public_key: Vec<u8>,
482 pub identity_key: Vec<u8>,
483}
484
485#[cfg(test)]
486mod tests {
487 use super::*;
488
489 #[tokio::test]
490 async fn test_key_exchange_flow() {
491 let alice = FourWordAddress::from("alice-bob-charlie-david");
492 let bob = FourWordAddress::from("eve-frank-grace-henry");
493
494 let alice_kex = KeyExchange::new(alice.clone()).unwrap();
495 let bob_kex = KeyExchange::new(bob.clone()).unwrap();
496
497 let init_msg = alice_kex.initiate_exchange(bob.clone()).await.unwrap();
499 assert_eq!(init_msg.message_type, KeyExchangeType::Initiation);
500
501 let resp_msg = bob_kex.respond_to_exchange(init_msg).await.unwrap();
503 assert_eq!(resp_msg.message_type, KeyExchangeType::Response);
504
505 alice_kex.complete_exchange(resp_msg).await.unwrap();
507
508 let alice_key = alice_kex.get_session_key(&bob).await.unwrap();
510 let bob_key = bob_kex.get_session_key(&alice).await.unwrap();
511
512 assert_eq!(alice_key, bob_key);
514 }
515
516 #[tokio::test]
517 async fn test_prekey_bundle() {
518 let alice = FourWordAddress::from("alice-bob-charlie-david");
519 let alice_kex = KeyExchange::new(alice.clone()).unwrap();
520
521 let bundle = alice_kex.get_prekey_bundle().await;
522
523 assert!(!bundle.identity_key.is_empty());
524 assert_eq!(bundle.one_time_prekeys.len(), 100);
525 assert!(!bundle.signed_prekey.signature.is_empty());
526 }
527
528 #[tokio::test]
529 async fn test_cleanup_expired() {
530 let alice = FourWordAddress::from("alice-bob-charlie-david");
531 let alice_kex = KeyExchange::new(alice.clone()).unwrap();
532
533 alice_kex.cleanup_expired().await.unwrap();
535 }
536
537 #[test]
538 fn test_prekey_generation() {
539 let bundle = PrekeyBundle::generate().unwrap();
540
541 assert_eq!(bundle.identity_key.len(), 32);
542 assert_eq!(bundle.signed_prekey.public_key.len(), 32);
543 assert_eq!(bundle.one_time_prekeys.len(), 100);
544
545 for (i, prekey) in bundle.one_time_prekeys.iter().enumerate() {
546 assert_eq!(prekey.id, i as u32);
547 assert_eq!(prekey.public_key.len(), 32);
548 }
549 }
550}