saorsa_core/messaging/
key_exchange.rs

1// Diffie-Hellman Key Exchange using X25519 for secure messaging
2// Provides perfect forward secrecy and quantum-resistant key derivation
3
4use 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
17/// Wrapper for EphemeralSecret that can be debugged safely
18struct 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
42/// Key Exchange Protocol for establishing secure sessions
43pub struct KeyExchange {
44    /// Current user identity
45    identity: FourWordAddress,
46    /// Active key exchange sessions
47    sessions: Arc<RwLock<HashMap<FourWordAddress, KeyExchangeSession>>>,
48    /// Completed handshakes
49    established_keys: Arc<RwLock<HashMap<FourWordAddress, EstablishedKey>>>,
50    /// Prekeys for asynchronous key exchange
51    prekeys: Arc<RwLock<PrekeyBundle>>,
52}
53
54impl KeyExchange {
55    /// Create a new key exchange manager
56    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    /// Initiate key exchange with a peer
68    pub async fn initiate_exchange(&self, peer: FourWordAddress) -> Result<KeyExchangeMessage> {
69        // Generate ephemeral keypair
70        let ephemeral_secret = DebugEphemeralSecret::new();
71        let ephemeral_public = ephemeral_secret.public_key();
72
73        // Create session
74        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        // Store session
85        let mut sessions = self.sessions.write().await;
86        sessions.insert(peer.clone(), session);
87
88        // Create initiation message
89        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    /// Respond to key exchange initiation
104    pub async fn respond_to_exchange(
105        &self,
106        message: KeyExchangeMessage,
107    ) -> Result<KeyExchangeMessage> {
108        // Verify signature
109        if !self.verify_key_exchange_signature(&message) {
110            return Err(anyhow::anyhow!("Invalid key exchange signature"));
111        }
112
113        // Parse their public key
114        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        // Generate our ephemeral keypair
121        let our_ephemeral = DebugEphemeralSecret::new();
122        let our_public = our_ephemeral.public_key();
123
124        // Compute shared secret
125        let shared_secret = our_ephemeral.into_secret().diffie_hellman(&their_public);
126
127        // Derive session keys
128        let (encryption_key, mac_key) = self.derive_keys(&shared_secret, &message.sender)?;
129
130        // Store established key
131        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        // Create response message
147        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    /// Complete key exchange after receiving response
162    pub async fn complete_exchange(&self, message: KeyExchangeMessage) -> Result<()> {
163        // Verify signature
164        if !self.verify_key_exchange_signature(&message) {
165            return Err(anyhow::anyhow!("Invalid key exchange signature"));
166        }
167
168        // Get our session
169        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        // Parse their public key
175        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        // Compute shared secret
182        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        // Derive session keys
189        let (encryption_key, mac_key) = self.derive_keys(&shared_secret, &message.sender)?;
190
191        // Store established key
192        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        // Remove session and store established key
205        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    /// Get established session key for a peer
216    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    /// Use prekey for asynchronous key exchange (Signal Protocol style)
230    pub async fn use_prekey(
231        &self,
232        peer: FourWordAddress,
233        prekey: &PrekeyMessage,
234    ) -> Result<Vec<u8>> {
235        // Generate ephemeral keypair
236        let ephemeral_secret = DebugEphemeralSecret::new();
237        let ephemeral_public = ephemeral_secret.public_key();
238
239        // Parse prekey
240        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        // Compute shared secret
248        let shared_secret = ephemeral_secret
249            .into_secret()
250            .diffie_hellman(&prekey_public);
251
252        // Derive session keys
253        let (encryption_key, mac_key) = self.derive_keys(&shared_secret, &peer)?;
254
255        // Store established key
256        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    /// Get our prekey bundle for others to use
275    pub async fn get_prekey_bundle(&self) -> PrekeyBundle {
276        let prekeys = self.prekeys.read().await;
277        prekeys.clone()
278    }
279
280    /// Rotate prekeys periodically
281    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    /// Clean up expired sessions
291    pub async fn cleanup_expired(&self) -> Result<()> {
292        let now = Utc::now();
293
294        // Clean up pending sessions
295        let mut sessions = self.sessions.write().await;
296        sessions.retain(|_, session| session.expires_at > now);
297
298        // Clean up established keys
299        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    /// Derive encryption and MAC keys from shared secret
312    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        // Create info string from identities
320        let info = format!("{}-{}", self.identity, peer);
321
322        // Derive 64 bytes (32 for encryption, 32 for MAC)
323        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    /// Sign key exchange message
334    fn sign_key_exchange(&self, public_key: &[u8]) -> Vec<u8> {
335        // In production, use actual signing with identity key
336        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    /// Verify key exchange signature
343    fn verify_key_exchange_signature(&self, message: &KeyExchangeMessage) -> bool {
344        // In production, verify actual signature
345        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/// Key exchange session state
355#[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/// Key exchange state
367#[derive(Debug, Clone, PartialEq)]
368enum KeyExchangeState {
369    _Initiated,
370    _Responded,
371    _Completed,
372}
373
374/// Established session key
375#[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/// Key exchange message
389#[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/// Key exchange message type
401#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
402pub enum KeyExchangeType {
403    Initiation,
404    Response,
405    PrekeyBundle,
406}
407
408/// Prekey bundle for asynchronous key exchange
409#[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    /// Generate a new prekey bundle
419    pub fn generate() -> Result<Self> {
420        // Generate identity key
421        let identity_secret = DebugEphemeralSecret::new();
422        let identity_public = identity_secret.public_key();
423
424        // Generate signed prekey
425        let signed_secret = DebugEphemeralSecret::new();
426        let signed_public = signed_secret.public_key();
427
428        // Sign the prekey
429        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        // Generate one-time prekeys
441        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/// Signed prekey
462#[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/// One-time prekey
471#[derive(Debug, Clone, Serialize, Deserialize)]
472pub struct OnetimePrekey {
473    pub id: u32,
474    pub public_key: Vec<u8>,
475}
476
477/// Prekey message for initial contact
478#[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        // Alice initiates
498        let init_msg = alice_kex.initiate_exchange(bob.clone()).await.unwrap();
499        assert_eq!(init_msg.message_type, KeyExchangeType::Initiation);
500
501        // Bob responds
502        let resp_msg = bob_kex.respond_to_exchange(init_msg).await.unwrap();
503        assert_eq!(resp_msg.message_type, KeyExchangeType::Response);
504
505        // Alice completes
506        alice_kex.complete_exchange(resp_msg).await.unwrap();
507
508        // Both should have session keys
509        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        // Keys should be the same (they derived from same shared secret)
513        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        // Clean up (should do nothing since nothing is expired)
534        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}