Mountain/IPC/Encryption/
SecureChannel.rs1use ring::{
36 aead::{self, AES_256_GCM, LessSafeKey, UnboundKey},
37 hmac,
38 rand::{SecureRandom, SystemRandom},
39};
40use serde::{Deserialize, Serialize};
41use log::debug;
42
43use super::super::Message::TauriIPCMessage;
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct EncryptedMessage {
71 pub nonce:Vec<u8>,
73
74 pub ciphertext:Vec<u8>,
76
77 pub hmac_tag:Vec<u8>,
79}
80
81impl EncryptedMessage {
82 pub fn new(nonce:Vec<u8>, ciphertext:Vec<u8>, hmac_tag:Vec<u8>) -> Self { Self { nonce, ciphertext, hmac_tag } }
84
85 pub fn is_valid(&self) -> bool {
87 self.nonce.len() == 12 && !self.ciphertext.is_empty()
89 && !self.hmac_tag.is_empty()
90 }
91}
92
93pub struct SecureMessageChannel {
157 encryption_key:LessSafeKey,
159
160 hmac_key:Vec<u8>,
162}
163
164impl SecureMessageChannel {
165 pub fn new() -> Result<Self, String> {
180 debug!("[SecureMessageChannel] Creating new secure channel");
181
182 let rng = SystemRandom::new();
183
184 let mut encryption_key_bytes = vec![0u8; 32];
186 rng.fill(&mut encryption_key_bytes)
187 .map_err(|e| format!("Failed to generate encryption key: {}", e))?;
188
189 let unbound_key = UnboundKey::new(&AES_256_GCM, &encryption_key_bytes)
190 .map_err(|e| format!("Failed to create unbound key: {}", e))?;
191
192 let encryption_key = LessSafeKey::new(unbound_key);
193
194 let mut hmac_key = vec![0u8; 32];
196 rng.fill(&mut hmac_key)
197 .map_err(|e| format!("Failed to generate HMAC key: {}", e))?;
198
199 debug!("[SecureMessageChannel] Secure channel created successfully");
200
201 Ok(Self { encryption_key, hmac_key })
202 }
203
204 pub fn encrypt_message(&self, message:&TauriIPCMessage) -> Result<EncryptedMessage, String> {
222 debug!("[SecureMessageChannel] Encrypting message on channel: {}", message.channel);
223
224 let serialized_message =
226 serde_json::to_vec(message).map_err(|e| format!("Failed to serialize message: {}", e))?;
227
228 let mut nonce = [0u8; 12];
230 SystemRandom::new()
231 .fill(&mut nonce)
232 .map_err(|e| format!("Failed to generate nonce: {}", e))?;
233
234 let mut in_out = serialized_message.clone();
236 self.encryption_key
237 .seal_in_place_append_tag(aead::Nonce::assume_unique_for_key(nonce), aead::Aad::empty(), &mut in_out)
238 .map_err(|e| format!("Encryption failed: {}", e))?;
239
240 let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &self.hmac_key);
242 let hmac_tag = hmac::sign(&hmac_key, &in_out);
243
244 let encrypted_message =
245 EncryptedMessage { nonce:nonce.to_vec(), ciphertext:in_out, hmac_tag:hmac_tag.as_ref().to_vec() };
246
247 debug!(
248 "[SecureMessageChannel] Message encrypted: {} bytes -> {} bytes",
249 serialized_message.len(),
250 encrypted_message.ciphertext.len()
251 );
252
253 Ok(encrypted_message)
254 }
255
256 pub fn decrypt_message(&self, encrypted:&EncryptedMessage) -> Result<TauriIPCMessage, String> {
274 debug!("[SecureMessageChannel] Decrypting message");
275
276 let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &self.hmac_key);
278 hmac::verify(&hmac_key, &encrypted.ciphertext, &encrypted.hmac_tag)
279 .map_err(|_| "HMAC verification failed - message may be tampered".to_string())?;
280
281 let nonce_slice:&[u8] = &encrypted.nonce;
283 let nonce_array:[u8; 12] = nonce_slice
284 .try_into()
285 .map_err(|_| "Invalid nonce length - must be 12 bytes".to_string())?;
286
287 let nonce = aead::Nonce::assume_unique_for_key(nonce_array);
288
289 let mut in_out = encrypted.ciphertext.clone();
291 self.encryption_key
292 .open_in_place(nonce, aead::Aad::empty(), &mut in_out)
293 .map_err(|e| format!("Decryption failed: {}", e))?;
294
295 let plaintext_len = in_out.len() - AES_256_GCM.tag_len();
297 in_out.truncate(plaintext_len);
298
299 let message:TauriIPCMessage =
301 serde_json::from_slice(&in_out).map_err(|e| format!("Failed to deserialize message: {}", e))?;
302
303 debug!(
304 "[SecureMessageChannel] Message decrypted successfully on channel: {}",
305 message.channel
306 );
307
308 Ok(message)
309 }
310
311 pub fn rotate_keys(&mut self) -> Result<(), String> {
326 debug!("[SecureMessageChannel] Rotating encryption keys");
327
328 *self = Self::new()?;
329
330 debug!("[SecureMessageChannel] Keys rotated successfully");
331
332 Ok(())
333 }
334
335 pub fn hmac_tag_length(&self) -> usize {
337 32 }
339
340 pub fn nonce_length(&self) -> usize {
342 12 }
344
345 pub fn auth_tag_length(&self) -> usize { AES_256_GCM.tag_len() }
347
348 pub fn key_length(&self) -> usize {
350 32 }
352}
353
354#[cfg(test)]
355#[allow(unused_imports)]
356mod tests {
357 use super::*;
358
359 fn create_test_message() -> TauriIPCMessage {
360 TauriIPCMessage::new(
361 "test_channel".to_string(),
362 serde_json::json!({
363 "data": "sensitive information that should be encrypted",
364 "id": 12345
365 }),
366 Some("test_sender".to_string()),
367 )
368 }
369
370 #[test]
371 fn test_secure_channel_creation() {
372 let channel = SecureMessageChannel::new();
373 assert!(channel.is_ok());
374 }
375
376 #[test]
377 fn test_encrypt_and_decrypt() {
378 let channel = SecureMessageChannel::new().unwrap();
379 let original_message = create_test_message();
380
381 let encrypted = channel.encrypt_message(&original_message).unwrap();
383 assert!(encrypted.is_valid());
384
385 let decrypted = channel.decrypt_message(&encrypted).unwrap();
387
388 assert_eq!(decrypted.channel, original_message.channel);
390 assert_eq!(decrypted.data, original_message.data);
391 assert_eq!(decrypted.sender, original_message.sender);
392 }
393
394 #[test]
395 fn test_encryption_produces_different_outputs() {
396 let channel = SecureMessageChannel::new().unwrap();
397 let message = create_test_message();
398
399 let encrypted1 = channel.encrypt_message(&message).unwrap();
400 let encrypted2 = channel.encrypt_message(&message).unwrap();
401
402 assert_ne!(encrypted1.nonce, encrypted2.nonce);
404 assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
405 }
406
407 #[test]
408 fn test_tampered_message_fails_hmac_verification() {
409 let channel = SecureMessageChannel::new().unwrap();
410 let message = create_test_message();
411
412 let mut encrypted = channel.encrypt_message(&message).unwrap();
413
414 if !encrypted.ciphertext.is_empty() {
416 encrypted.ciphertext[0] ^= 0xFF;
417 }
418
419 let result = channel.decrypt_message(&encrypted);
421 assert!(result.is_err());
422 assert!(result.unwrap_err().contains("HMAC verification failed"));
423 }
424
425 #[test]
426 fn test_invalid_nonce_length() {
427 let channel = SecureMessageChannel::new().unwrap();
428 let message = create_test_message();
429
430 let mut encrypted = channel.encrypt_message(&message).unwrap();
431
432 encrypted.nonce = vec![0u8; 16]; let result = channel.decrypt_message(&encrypted);
436 assert!(result.is_err());
437 assert!(result.unwrap_err().contains("Invalid nonce length"));
438 }
439
440 #[test]
441 fn test_message_channel_key_lengths() {
442 let channel = SecureMessageChannel::new().unwrap();
443
444 assert_eq!(channel.key_length(), 32);
445 assert_eq!(channel.nonce_length(), 12);
446 assert_eq!(channel.auth_tag_length(), 16); assert_eq!(channel.hmac_tag_length(), 32); }
449
450 #[test]
451 fn test_key_rotation() {
452 let mut channel = SecureMessageChannel::new().unwrap();
453 let message = create_test_message();
454
455 let encrypted1 = channel.encrypt_message(&message).unwrap();
457
458 let result = channel.rotate_keys();
460 assert!(result.is_ok());
461
462 let decrypted1 = channel.decrypt_message(&encrypted1).unwrap();
464 assert_eq!(decrypted1.channel, message.channel);
465
466 let encrypted2 = channel.encrypt_message(&message).unwrap();
468 let decrypted2 = channel.decrypt_message(&encrypted2).unwrap();
469 assert_eq!(decrypted2.channel, message.channel);
470
471 assert_ne!(encrypted1.nonce, encrypted2.nonce);
473 }
474
475 #[test]
476 fn test_empty_message() {
477 let channel = SecureMessageChannel::new().unwrap();
478 let message = TauriIPCMessage::new("test".to_string(), serde_json::json!(null), None);
479
480 let encrypted = channel.encrypt_message(&message).unwrap();
481 let decrypted = channel.decrypt_message(&encrypted).unwrap();
482
483 assert_eq!(decrypted.channel, "test");
484 }
485}