1use 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
18pub struct SecureMessaging {
20 identity: FourWordAddress,
22 pub key_exchange: KeyExchange,
24 session_keys: Arc<RwLock<HashMap<FourWordAddress, SessionKey>>>,
26 device_keys: Arc<RwLock<HashMap<DeviceId, DeviceKey>>>,
28}
29
30impl SecureMessaging {
31 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 pub async fn encrypt_message(&self, message: &RichMessage) -> Result<EncryptedMessage> {
45 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 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 let cipher = ChaCha20Poly1305::new_from_slice(&session_key)?;
67 let nonce = ChaCha20Poly1305::generate_nonce(&mut OsRng);
68
69 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 pub async fn decrypt_message(&self, encrypted: EncryptedMessage) -> Result<RichMessage> {
88 let session_key = self.get_or_create_session_key(&encrypted.sender).await?;
90
91 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 let message: RichMessage = serde_json::from_slice(&plaintext)?;
101
102 Ok(message)
103 }
104
105 pub fn sign_message(&self, message: &RichMessage) -> Vec<u8> {
107 let mut hasher = Hasher::new();
109 hasher.update(&serde_json::to_vec(message).unwrap_or_default());
110 let hash = hasher.finalize();
111
112 hash.as_bytes().to_vec()
115 }
116
117 pub fn verify_message(&self, message: &RichMessage) -> bool {
119 let mut hasher = Hasher::new();
121 hasher.update(&serde_json::to_vec(message).unwrap_or_default());
122 let hash = hasher.finalize();
123
124 message.signature.signature == hash.as_bytes().to_vec()
127 }
128
129 pub async fn establish_session(&self, peer: &FourWordAddress) -> Result<SessionKey> {
131 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 let mut keys = self.session_keys.write().await;
149 keys.insert(peer.clone(), session_key.clone());
150
151 Ok(session_key)
152 }
153
154 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 keys.retain(|_, key| key.expires_at > now);
161
162 let rotation_threshold = now - chrono::Duration::hours(12);
164 for (peer, key) in keys.iter_mut() {
165 if key.established_at < rotation_threshold {
166 let new_key = self.establish_session(peer).await?;
168 *key = new_key;
169 }
170 }
171
172 Ok(())
173 }
174
175 pub async fn register_device(&self, device_id: DeviceId) -> Result<DeviceKey> {
177 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], created_at: chrono::Utc::now(),
188 };
189
190 let mut keys = self.device_keys.write().await;
192 keys.insert(device_id, device_key.clone());
193
194 Ok(device_key)
195 }
196
197 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 let keys = self.device_keys.read().await;
208 if let Some(device_key) = keys.get(&device_id) {
209 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 pub async fn create_ephemeral_session(
222 &self,
223 peer: &FourWordAddress,
224 ) -> Result<EphemeralSession> {
225 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 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 self.establish_session(peer).await
254 }
255
256 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#[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#[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#[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
309pub struct KeyRatchet {
311 current_key: Vec<u8>,
312 generation: u32,
313}
314
315impl KeyRatchet {
316 pub fn new(initial_key: Vec<u8>) -> Self {
318 Self {
319 current_key: initial_key,
320 generation: 0,
321 }
322 }
323
324 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); }
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}