saorsa_core/messaging/
encryption.rs

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