saorsa_core/messaging/
encryption.rs

1// End-to-end encryption for messaging
2
3use super::DhtClient;
4use super::key_exchange::KeyExchange;
5use super::types::*;
6use crate::identity::FourWordAddress;
7
8use anyhow::Result;
9use blake3::Hasher;
10use chacha20poly1305::{
11    ChaCha20Poly1305, Nonce,
12    aead::{Aead, AeadCore, KeyInit, OsRng},
13};
14use std::collections::HashMap;
15use std::sync::Arc;
16use tokio::sync::RwLock;
17
18/// Secure messaging with quantum-resistant encryption
19pub struct SecureMessaging {
20    /// Current user identity
21    identity: FourWordAddress,
22    /// Key exchange manager
23    pub key_exchange: KeyExchange,
24    /// Session keys cache
25    session_keys: Arc<RwLock<HashMap<FourWordAddress, SessionKey>>>,
26    /// Device keys for multi-device support
27    device_keys: Arc<RwLock<HashMap<DeviceId, DeviceKey>>>,
28}
29
30impl SecureMessaging {
31    /// Create new secure messaging instance
32    pub async fn new(identity: FourWordAddress, dht: DhtClient) -> Result<Self> {
33        let key_exchange = KeyExchange::new(identity.clone(), dht).await?;
34
35        Ok(Self {
36            identity,
37            key_exchange,
38            session_keys: Arc::new(RwLock::new(HashMap::new())),
39            device_keys: Arc::new(RwLock::new(HashMap::new())),
40        })
41    }
42
43    /// Encrypt a message for recipients
44    pub async fn encrypt_message(&self, message: &RichMessage) -> Result<EncryptedMessage> {
45        // For channel messages, we'll use a channel-specific key
46        // For DMs, we'll use peer-to-peer key exchange
47        // For now, using a simple approach
48
49        // Try to get existing session key or establish new one
50        let session_key = if let Ok(key) = self
51            .key_exchange
52            .get_session_key(&message.channel_id.0.to_string().into())
53            .await
54        {
55            key
56        } else {
57            // Use a deterministic key for the channel (in production, this would be properly negotiated)
58            let mut hasher = Hasher::new();
59            hasher.update(self.identity.to_string().as_bytes());
60            hasher.update(message.channel_id.0.as_bytes());
61            let key_material = hasher.finalize();
62            key_material.as_bytes()[..32].to_vec()
63        };
64
65        // Encrypt with ChaCha20Poly1305
66        let cipher = ChaCha20Poly1305::new_from_slice(&session_key)?;
67        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
68
69        // Serialize message
70        let plaintext = serde_json::to_vec(message)?;
71
72        let ciphertext = cipher
73            .encrypt(&nonce, plaintext.as_ref())
74            .map_err(|e| anyhow::anyhow!("Encryption failed: {}", e))?;
75
76        Ok(EncryptedMessage {
77            id: message.id,
78            channel_id: message.channel_id,
79            sender: self.identity.clone(),
80            ciphertext,
81            nonce: nonce.to_vec(),
82            key_id: self.identity.to_string(),
83        })
84    }
85
86    /// Decrypt an encrypted message
87    pub async fn decrypt_message(&self, encrypted: EncryptedMessage) -> Result<RichMessage> {
88        // Get session key for sender
89        let session_key = self.get_or_create_session_key(&encrypted.sender).await?;
90
91        // Decrypt with ChaCha20Poly1305
92        let cipher = ChaCha20Poly1305::new_from_slice(&session_key.key)?;
93        let nonce = Nonce::from_slice(&encrypted.nonce);
94
95        let plaintext = cipher
96            .decrypt(nonce, encrypted.ciphertext.as_ref())
97            .map_err(|e| anyhow::anyhow!("Decryption failed: {}", e))?;
98
99        // Deserialize message
100        let message: RichMessage = serde_json::from_slice(&plaintext)?;
101
102        Ok(message)
103    }
104
105    /// Sign a message for verification
106    pub fn sign_message(&self, message: &RichMessage) -> Vec<u8> {
107        // Hash message content
108        let mut hasher = Hasher::new();
109        hasher.update(&serde_json::to_vec(message).unwrap_or_default());
110        let hash = hasher.finalize();
111
112        // Sign with ML-DSA
113        // In production, use actual ML-DSA signing
114        hash.as_bytes().to_vec()
115    }
116
117    /// Verify message signature
118    pub fn verify_message(&self, message: &RichMessage) -> bool {
119        // Hash message content
120        let mut hasher = Hasher::new();
121        hasher.update(&serde_json::to_vec(message).unwrap_or_default());
122        let hash = hasher.finalize();
123
124        // Verify with ML-DSA
125        // In production, verify actual ML-DSA signature
126        message.signature.signature == hash.as_bytes().to_vec()
127    }
128
129    /// Establish quantum-safe session key
130    pub async fn establish_session(&self, peer: &FourWordAddress) -> Result<SessionKey> {
131        // For now, use a simple key derivation
132        // In production, this would use ML-KEM for quantum-safe key exchange
133
134        // Derive session key from peer identities
135        let mut hasher = Hasher::new();
136        hasher.update(self.identity.to_string().as_bytes());
137        hasher.update(peer.to_string().as_bytes());
138        let key_material = hasher.finalize();
139
140        let session_key = SessionKey {
141            peer: peer.clone(),
142            key: key_material.as_bytes()[..32].to_vec(),
143            established_at: chrono::Utc::now(),
144            expires_at: chrono::Utc::now() + chrono::Duration::hours(24),
145        };
146
147        // Cache session key
148        let mut keys = self.session_keys.write().await;
149        keys.insert(peer.clone(), session_key.clone());
150
151        Ok(session_key)
152    }
153
154    /// Rotate session keys periodically
155    pub async fn rotate_session_keys(&self) -> Result<()> {
156        let mut keys = self.session_keys.write().await;
157        let now = chrono::Utc::now();
158
159        // Remove expired keys
160        keys.retain(|_, key| key.expires_at > now);
161
162        // Rotate keys older than 12 hours
163        let rotation_threshold = now - chrono::Duration::hours(12);
164        for (peer, key) in keys.iter_mut() {
165            if key.established_at < rotation_threshold {
166                // Re-establish session
167                let new_key = self.establish_session(peer).await?;
168                *key = new_key;
169            }
170        }
171
172        Ok(())
173    }
174
175    /// Create device-specific keys for multi-device
176    pub async fn register_device(&self, device_id: DeviceId) -> Result<DeviceKey> {
177        // Generate device-specific key
178        let mut hasher = Hasher::new();
179        hasher.update(self.identity.to_string().as_bytes());
180        hasher.update(device_id.0.as_bytes());
181        let key_material = hasher.finalize();
182
183        let device_key = DeviceKey {
184            device_id: device_id.clone(),
185            public_key: key_material.as_bytes().to_vec(),
186            private_key: vec![0; 32], // In production, generate proper keypair
187            created_at: chrono::Utc::now(),
188        };
189
190        // Store device key
191        let mut keys = self.device_keys.write().await;
192        keys.insert(device_id, device_key.clone());
193
194        Ok(device_key)
195    }
196
197    /// Encrypt for specific devices
198    pub async fn encrypt_for_devices(
199        &self,
200        message: &RichMessage,
201        devices: Vec<DeviceId>,
202    ) -> Result<Vec<EncryptedMessage>> {
203        let mut encrypted_messages = Vec::new();
204
205        for device_id in devices {
206            // Get device key
207            let keys = self.device_keys.read().await;
208            if let Some(device_key) = keys.get(&device_id) {
209                // Encrypt with device-specific key
210                let encrypted = self
211                    .encrypt_with_key(message, &device_key.public_key)
212                    .await?;
213                encrypted_messages.push(encrypted);
214            }
215        }
216
217        Ok(encrypted_messages)
218    }
219
220    /// Perfect forward secrecy with ephemeral keys
221    pub async fn create_ephemeral_session(
222        &self,
223        peer: &FourWordAddress,
224    ) -> Result<EphemeralSession> {
225        // Generate ephemeral keypair
226        // In production, this would use proper quantum-safe ephemeral key generation
227        let mut hasher = Hasher::new();
228        hasher.update(&chrono::Utc::now().timestamp().to_le_bytes());
229        hasher.update(peer.to_string().as_bytes());
230        let key_material = hasher.finalize();
231
232        Ok(EphemeralSession {
233            peer: peer.clone(),
234            ephemeral_public: key_material.as_bytes()[..32].to_vec(),
235            ephemeral_private: key_material.as_bytes().get(32..64).unwrap_or(&[]).to_vec(),
236            created_at: chrono::Utc::now(),
237            message_count: 0,
238        })
239    }
240
241    /// Get or create session key
242    async fn get_or_create_session_key(&self, peer: &FourWordAddress) -> Result<SessionKey> {
243        let keys = self.session_keys.read().await;
244
245        if let Some(key) = keys.get(peer)
246            && key.expires_at > chrono::Utc::now()
247        {
248            return Ok(key.clone());
249        }
250        drop(keys);
251
252        // Create new session
253        self.establish_session(peer).await
254    }
255
256    /// Encrypt with specific key
257    async fn encrypt_with_key(
258        &self,
259        message: &RichMessage,
260        key: &[u8],
261    ) -> Result<EncryptedMessage> {
262        let cipher = ChaCha20Poly1305::new_from_slice(&key[..32])?;
263        let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
264
265        let plaintext = serde_json::to_vec(message)?;
266        let ciphertext = cipher
267            .encrypt(&nonce, plaintext.as_ref())
268            .map_err(|e| anyhow::anyhow!("Encryption failed: {}", e))?;
269
270        Ok(EncryptedMessage {
271            id: message.id,
272            channel_id: message.channel_id,
273            sender: self.identity.clone(),
274            ciphertext,
275            nonce: nonce.to_vec(),
276            key_id: self.identity.to_string(),
277        })
278    }
279}
280
281/// Session key for peer communication
282#[derive(Debug, Clone)]
283pub struct SessionKey {
284    pub peer: FourWordAddress,
285    pub key: Vec<u8>,
286    pub established_at: chrono::DateTime<chrono::Utc>,
287    pub expires_at: chrono::DateTime<chrono::Utc>,
288}
289
290/// Device-specific key
291#[derive(Debug, Clone)]
292pub struct DeviceKey {
293    pub device_id: DeviceId,
294    pub public_key: Vec<u8>,
295    pub private_key: Vec<u8>,
296    pub created_at: chrono::DateTime<chrono::Utc>,
297}
298
299/// Ephemeral session for perfect forward secrecy
300#[derive(Debug, Clone)]
301pub struct EphemeralSession {
302    pub peer: FourWordAddress,
303    pub ephemeral_public: Vec<u8>,
304    pub ephemeral_private: Vec<u8>,
305    pub created_at: chrono::DateTime<chrono::Utc>,
306    pub message_count: u32,
307}
308
309/// Key ratcheting for forward secrecy
310pub struct KeyRatchet {
311    current_key: Vec<u8>,
312    generation: u32,
313}
314
315impl KeyRatchet {
316    /// Create new key ratchet
317    pub fn new(initial_key: Vec<u8>) -> Self {
318        Self {
319            current_key: initial_key,
320            generation: 0,
321        }
322    }
323
324    /// Ratchet forward
325    pub fn ratchet(&mut self) -> Vec<u8> {
326        let mut hasher = Hasher::new();
327        hasher.update(&self.current_key);
328        hasher.update(&self.generation.to_le_bytes());
329        let new_key = hasher.finalize();
330
331        self.current_key = new_key.as_bytes().to_vec();
332        self.generation += 1;
333
334        self.current_key.clone()
335    }
336}
337
338#[cfg(test)]
339mod tests {
340    use super::*;
341    use crate::messaging::user_handle::UserHandle;
342
343    #[tokio::test]
344    async fn test_message_encryption() {
345        let identity = FourWordAddress::from("alice-bob-charlie-david");
346        let dht = super::DhtClient::new().unwrap();
347        let secure = SecureMessaging::new(identity.clone(), dht).await.unwrap();
348
349        let message = RichMessage::new(
350            UserHandle::from(identity.to_string()),
351            ChannelId::new(),
352            MessageContent::Text("Secret message".to_string()),
353        );
354
355        let encrypted = secure.encrypt_message(&message).await.unwrap();
356        assert!(!encrypted.ciphertext.is_empty());
357        assert_eq!(encrypted.id, message.id);
358    }
359
360    #[tokio::test]
361    async fn test_message_signing() {
362        let identity = FourWordAddress::from("alice-bob-charlie-david");
363        let dht = super::DhtClient::new().unwrap();
364        let secure = SecureMessaging::new(identity.clone(), dht).await.unwrap();
365
366        let message = RichMessage::new(
367            UserHandle::from(identity.to_string()),
368            ChannelId::new(),
369            MessageContent::Text("Sign me".to_string()),
370        );
371
372        let signature = secure.sign_message(&message);
373        assert!(!signature.is_empty());
374        assert_eq!(signature.len(), 32); // Blake3 hash is 32 bytes
375    }
376
377    #[test]
378    fn test_key_ratchet() {
379        let initial_key = vec![0u8; 32];
380        let mut ratchet = KeyRatchet::new(initial_key.clone());
381
382        let key1 = ratchet.ratchet();
383        let key2 = ratchet.ratchet();
384
385        assert_ne!(key1, initial_key);
386        assert_ne!(key2, key1);
387        assert_eq!(ratchet.generation, 2);
388    }
389}