Skip to main content

Mountain/IPC/Encryption/
SecureChannel.rs

1//! # Secure Message Channel (IPC Encryption)
2//!
3//! ## RESPONSIBILITIES
4//! This module provides secure message channels using AES-256-GCM encryption
5//! with HMAC authentication. It ensures message confidentiality and integrity
6//! for sensitive IPC communications.
7//!
8//! ## ARCHITECTURAL ROLE
9//! This module is part of the security layer in the IPC architecture, providing
10//! end-to-end encryption for sensitive messages.
11//!
12//! ## KEY COMPONENTS
13//!
14//! - **SecureMessageChannel**: Encryption channel with AES-256-GCM + HMAC
15//! - **EncryptedMessage**: Encrypted message structure with nonce and HMAC tag
16//!
17//! ## ERROR HANDLING
18//! All encryption/decryption operations return Result types with descriptive
19//! error messages for failures.
20//!
21//! ## LOGGING
22// Debug-level logging for key operations, error for failures.
23//
24// ## Performance Considerations
25// - AES-256-GCM provides hardware-accelerated encryption on modern CPUs
26// - Nonce-based encryption ensures unique ciphertexts
27// - HMAC provides message authentication and integrity verification
28//
29// ## TODO
30// - Add encryption key rotation
31// - Implement symmetric key exchange protocol
32// - Add support for multiple encryption algorithms
33// - Implement message replay attack prevention
34
35use ring::{
36	aead::{self, AES_256_GCM, LessSafeKey, UnboundKey},
37	hmac,
38	rand::{SecureRandom, SystemRandom},
39};
40use serde::{Deserialize, Serialize};
41
42use super::super::Message::Types::TauriIPCMessage;
43use crate::dev_log;
44
45/// Encrypted message structure
46///
47/// This structure contains the encrypted message data along with the nonce
48/// and HMAC tag needed for decryption and verification.
49///
50/// ## Message Structure
51///
52/// ```text
53/// EncryptedMessage {
54///     nonce: [u8; 12],      // Unique value for each encryption
55///     ciphertext: Vec<u8>,  // Encrypted message + auth tag
56///     hmac_tag: Vec<u8>,    // HMAC for message authentication
57/// }
58/// ```
59///
60/// ## Example Usage
61///
62/// ```rust,ignore
63/// let encrypted = EncryptedMessage {
64///     nonce: vec![1, 2, 3, ...],
65///     ciphertext: vec![...],
66///     hmac_tag: vec![...],
67/// };
68/// ```
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct EncryptedMessage {
71	/// Nonce used for encryption (12 bytes for AES-256-GCM)
72	pub nonce:Vec<u8>,
73
74	/// Encrypted message data with authentication tag
75	pub ciphertext:Vec<u8>,
76
77	/// HMAC tag for message authentication
78	pub hmac_tag:Vec<u8>,
79}
80
81impl EncryptedMessage {
82	/// Create a new encrypted message
83	pub fn new(nonce:Vec<u8>, ciphertext:Vec<u8>, hmac_tag:Vec<u8>) -> Self { Self { nonce, ciphertext, hmac_tag } }
84
85	/// Validate the message structure
86	pub fn is_valid(&self) -> bool {
87		self.nonce.len() == 12 // AES-256-GCM requires 12-byte nonce
88			&& !self.ciphertext.is_empty()
89
90			&& !self.hmac_tag.is_empty()
91	}
92}
93
94/// Secure message channel with encryption and authentication
95///
96/// This structure provides AES-256-GCM encryption with HMAC authentication
97/// for secure IPC communication. It ensures message confidentiality and
98/// integrity.
99///
100/// ## Encryption Flow
101///
102/// ```text
103/// TauriIPCMessage
104///     |
105///     | 1. Serialize to JSON
106///     v
107/// Serialized bytes
108///     |
109///     | 2. Encrypt with AES-256-GCM
110///     v
111/// Encrypted bytes + auth tag
112///     |
113///     | 3. Generate HMAC
114///     v
115/// EncryptedMessage (nonce, ciphertext, hmac_tag)
116/// ```
117///
118/// ## Decryption Flow
119///
120/// ```text
121/// EncryptedMessage
122///     |
123///     | 1. Verify HMAC
124///     v
125/// HMAC valid
126///     |
127///     | 2. Decrypt with AES-256-GCM
128///     v
129/// Serialized bytes
130///     |
131///     | 3. Deserialize to TauriIPCMessage
132///     v
133/// TauriIPCMessage
134/// ```
135///
136/// ## Security Features
137///
138/// - **AES-256-GCM**: Industry-standard authenticated encryption
139/// - **Unique Nonces**: Each encryption uses a unique nonce
140/// - **HMAC Authentication**: Additional layer of message authentication
141/// - **Secure Random Generation**: Cryptographically secure random keys
142///
143/// ## Example Usage
144///
145/// ```rust,ignore
146/// let secure_channel = SecureMessageChannel::new()?;
147///
148/// // Encrypt a message
149/// let encrypted = secure_channel.encrypt_message(&message)?;
150///
151/// // Decrypt a message
152/// let decrypted = secure_channel.decrypt_message(&encrypted)?;
153///
154/// // Rotate keys
155/// secure_channel.rotate_keys()?;
156/// ```
157pub struct SecureMessageChannel {
158	/// AES-256-GCM encryption key
159	encryption_key:LessSafeKey,
160
161	/// HMAC key for message authentication
162	hmac_key:Vec<u8>,
163}
164
165impl SecureMessageChannel {
166	/// Create a new secure channel with randomly generated keys
167	///
168	/// This method generates cryptographically secure random keys for
169	/// encryption and HMAC authentication.
170	///
171	/// ## Returns
172	/// - `Ok(SecureMessageChannel)`: New secure channel
173	/// - `Err(String)`: Error message if key generation fails
174	///
175	/// ## Example
176	///
177	/// ```rust,ignore
178	/// let secure_channel = SecureMessageChannel::new()?;
179	/// ```
180	pub fn new() -> Result<Self, String> {
181		dev_log!("encryption", "[SecureMessageChannel] Creating new secure channel");
182
183		let rng = SystemRandom::new();
184
185		// Generate 256-bit (32-byte) encryption key
186		let mut encryption_key_bytes = vec![0u8; 32];
187
188		rng.fill(&mut encryption_key_bytes)
189			.map_err(|e| format!("Failed to generate encryption key: {}", e))?;
190
191		let unbound_key = UnboundKey::new(&AES_256_GCM, &encryption_key_bytes)
192			.map_err(|e| format!("Failed to create unbound key: {}", e))?;
193
194		let encryption_key = LessSafeKey::new(unbound_key);
195
196		// Generate 256-bit HMAC key
197		let mut hmac_key = vec![0u8; 32];
198
199		rng.fill(&mut hmac_key)
200			.map_err(|e| format!("Failed to generate HMAC key: {}", e))?;
201
202		dev_log!("encryption", "[SecureMessageChannel] Secure channel created successfully");
203
204		Ok(Self { encryption_key, hmac_key })
205	}
206
207	/// Encrypt and authenticate a message
208	///
209	/// This method serializes the message, encrypts it with AES-256-GCM,
210	/// and adds an HMAC tag for authentication.
211	///
212	/// ## Parameters
213	/// - `message`: The message to encrypt
214	///
215	/// ## Returns
216	/// - `Ok(EncryptedMessage)`: Encrypted message with nonce and HMAC tag
217	/// - `Err(String)`: Error message if encryption fails
218	///
219	/// ## Example
220	///
221	/// ```rust,ignore
222	/// let encrypted = secure_channel.encrypt_message(&message)?;
223	/// ```
224	pub fn encrypt_message(&self, message:&TauriIPCMessage) -> Result<EncryptedMessage, String> {
225		dev_log!(
226			"encryption",
227			"[SecureMessageChannel] Encrypting message on channel: {}",
228			message.channel
229		);
230
231		// Serialize message to bytes
232		let serialized_message =
233			serde_json::to_vec(message).map_err(|e| format!("Failed to serialize message: {}", e))?;
234
235		// Generate unique 12-byte nonce (required for AES-256-GCM)
236		let mut nonce = [0u8; 12];
237
238		SystemRandom::new()
239			.fill(&mut nonce)
240			.map_err(|e| format!("Failed to generate nonce: {}", e))?;
241
242		// Encrypt with AES-256-GCM (authenticated encryption)
243		let mut in_out = serialized_message.clone();
244
245		self.encryption_key
246			.seal_in_place_append_tag(aead::Nonce::assume_unique_for_key(nonce), aead::Aad::empty(), &mut in_out)
247			.map_err(|e| format!("Encryption failed: {}", e))?;
248
249		// Generate HMAC for additional authentication
250		let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &self.hmac_key);
251
252		let hmac_tag = hmac::sign(&hmac_key, &in_out);
253
254		let encrypted_message =
255			EncryptedMessage { nonce:nonce.to_vec(), ciphertext:in_out, hmac_tag:hmac_tag.as_ref().to_vec() };
256
257		dev_log!(
258			"encryption",
259			"[SecureMessageChannel] Message encrypted: {} bytes -> {} bytes",
260			serialized_message.len(),
261			encrypted_message.ciphertext.len()
262		);
263
264		Ok(encrypted_message)
265	}
266
267	/// Decrypt and verify a message
268	///
269	/// This method verifies the HMAC tag, decrypts the message with
270	/// AES-256-GCM, and deserializes it back to the original format.
271	///
272	/// ## Parameters
273	/// - `encrypted`: The encrypted message to decrypt
274	///
275	/// ## Returns
276	/// - `Ok(TauriIPCMessage)`: Decrypted message
277	/// - `Err(String)`: Error message if decryption or verification fails
278	///
279	/// ## Example
280	///
281	/// ```rust,ignore
282	/// let decrypted = secure_channel.decrypt_message(&encrypted)?;
283	/// ```
284	pub fn decrypt_message(&self, encrypted:&EncryptedMessage) -> Result<TauriIPCMessage, String> {
285		dev_log!("encryption", "[SecureMessageChannel] Decrypting message");
286
287		// Verify HMAC first (detect tampering)
288		let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &self.hmac_key);
289
290		hmac::verify(&hmac_key, &encrypted.ciphertext, &encrypted.hmac_tag)
291			.map_err(|_| "HMAC verification failed - message may be tampered".to_string())?;
292
293		// Convert nonce slice to array
294		let nonce_slice:&[u8] = &encrypted.nonce;
295
296		let nonce_array:[u8; 12] = nonce_slice
297			.try_into()
298			.map_err(|_| "Invalid nonce length - must be 12 bytes".to_string())?;
299
300		let nonce = aead::Nonce::assume_unique_for_key(nonce_array);
301
302		// Decrypt with AES-256-GCM
303		let mut in_out = encrypted.ciphertext.clone();
304
305		self.encryption_key
306			.open_in_place(nonce, aead::Aad::empty(), &mut in_out)
307			.map_err(|e| format!("Decryption failed: {}", e))?;
308
309		// Remove the authentication tag (last 16 bytes for AES-256-GCM)
310		let plaintext_len = in_out.len() - AES_256_GCM.tag_len();
311
312		in_out.truncate(plaintext_len);
313
314		// Deserialize message
315		let message:TauriIPCMessage =
316			serde_json::from_slice(&in_out).map_err(|e| format!("Failed to deserialize message: {}", e))?;
317
318		dev_log!(
319			"encryption",
320			"[SecureMessageChannel] Message decrypted successfully on channel: {}",
321			message.channel
322		);
323
324		Ok(message)
325	}
326
327	/// Rotate encryption keys
328	///
329	/// This method generates new encryption and HMAC keys, effectively
330	/// rotating the security credentials for the channel.
331	///
332	/// ## Returns
333	/// - `Ok(())`: Keys rotated successfully
334	/// - `Err(String)`: Error message if key rotation fails
335	///
336	/// ## Example
337	///
338	/// ```rust,ignore
339	/// secure_channel.rotate_keys()?;
340	/// ```
341	pub fn rotate_keys(&mut self) -> Result<(), String> {
342		dev_log!("encryption", "[SecureMessageChannel] Rotating encryption keys");
343
344		*self = Self::new()?;
345
346		dev_log!("encryption", "[SecureMessageChannel] Keys rotated successfully");
347
348		Ok(())
349	}
350
351	/// Get the HMAC tag length (in bytes)
352	pub fn hmac_tag_length(&self) -> usize {
353		32 // HMAC-SHA256 produces 32-byte tags
354	}
355
356	/// Get the nonce length (in bytes)
357	pub fn nonce_length(&self) -> usize {
358		12 // AES-256-GCM requires 12-byte nonces
359	}
360
361	/// Get the authentication tag length (in bytes)
362	pub fn auth_tag_length(&self) -> usize { AES_256_GCM.tag_len() }
363
364	/// Get the key length (in bytes)
365	pub fn key_length(&self) -> usize {
366		32 // AES-256 uses 32-byte keys
367	}
368}
369
370#[cfg(test)]
371#[allow(unused_imports)]
372mod tests {
373
374	use super::*;
375
376	fn create_test_message() -> TauriIPCMessage {
377		TauriIPCMessage::new(
378			"test_channel".to_string(),
379			serde_json::json!({
380				"data": "sensitive information that should be encrypted",
381				"id": 12345
382			}),
383			Some("test_sender".to_string()),
384		)
385	}
386
387	#[test]
388	fn test_secure_channel_creation() {
389		let channel = SecureMessageChannel::new();
390
391		assert!(channel.is_ok());
392	}
393
394	#[test]
395	fn test_encrypt_and_decrypt() {
396		let channel = SecureMessageChannel::new().unwrap();
397
398		let original_message = create_test_message();
399
400		// Encrypt
401		let encrypted = channel.encrypt_message(&original_message).unwrap();
402
403		assert!(encrypted.is_valid());
404
405		// Decrypt
406		let decrypted = channel.decrypt_message(&encrypted).unwrap();
407
408		// Verify content
409		assert_eq!(decrypted.channel, original_message.channel);
410
411		assert_eq!(decrypted.data, original_message.data);
412
413		assert_eq!(decrypted.sender, original_message.sender);
414	}
415
416	#[test]
417	fn test_encryption_produces_different_outputs() {
418		let channel = SecureMessageChannel::new().unwrap();
419
420		let message = create_test_message();
421
422		let encrypted1 = channel.encrypt_message(&message).unwrap();
423
424		let encrypted2 = channel.encrypt_message(&message).unwrap();
425
426		// Each encryption should produce different output (due to unique nonces)
427		assert_ne!(encrypted1.nonce, encrypted2.nonce);
428
429		assert_ne!(encrypted1.ciphertext, encrypted2.ciphertext);
430	}
431
432	#[test]
433	fn test_tampered_message_fails_hmac_verification() {
434		let channel = SecureMessageChannel::new().unwrap();
435
436		let message = create_test_message();
437
438		let mut encrypted = channel.encrypt_message(&message).unwrap();
439
440		// Tamper with the ciphertext
441		if !encrypted.ciphertext.is_empty() {
442			encrypted.ciphertext[0] ^= 0xFF;
443		}
444
445		// Should fail HMAC verification
446		let result = channel.decrypt_message(&encrypted);
447
448		assert!(result.is_err());
449
450		assert!(result.unwrap_err().contains("HMAC verification failed"));
451	}
452
453	#[test]
454	fn test_invalid_nonce_length() {
455		let channel = SecureMessageChannel::new().unwrap();
456
457		let message = create_test_message();
458
459		let mut encrypted = channel.encrypt_message(&message).unwrap();
460
461		// Corrupt the nonce length
462		encrypted.nonce = vec![0u8; 16]; // Wrong length
463
464		let result = channel.decrypt_message(&encrypted);
465
466		assert!(result.is_err());
467
468		assert!(result.unwrap_err().contains("Invalid nonce length"));
469	}
470
471	#[test]
472	fn test_message_channel_key_lengths() {
473		let channel = SecureMessageChannel::new().unwrap();
474
475		assert_eq!(channel.key_length(), 32);
476
477		assert_eq!(channel.nonce_length(), 12);
478
479		assert_eq!(channel.auth_tag_length(), 16); // AES-256-GCM
480
481		assert_eq!(channel.hmac_tag_length(), 32); // HMAC-SHA256
482	}
483
484	#[test]
485	fn test_key_rotation() {
486		let mut channel = SecureMessageChannel::new().unwrap();
487
488		let message = create_test_message();
489
490		// Encrypt with original keys
491		let encrypted1 = channel.encrypt_message(&message).unwrap();
492
493		// Rotate keys
494		let result = channel.rotate_keys();
495
496		assert!(result.is_ok());
497
498		// Old encrypted message should still decode successfully
499		let decrypted1 = channel.decrypt_message(&encrypted1).unwrap();
500
501		assert_eq!(decrypted1.channel, message.channel);
502
503		// New encryption should work with new keys
504		let encrypted2 = channel.encrypt_message(&message).unwrap();
505
506		let decrypted2 = channel.decrypt_message(&encrypted2).unwrap();
507
508		assert_eq!(decrypted2.channel, message.channel);
509
510		// Encrypted versions should be different
511		assert_ne!(encrypted1.nonce, encrypted2.nonce);
512	}
513
514	#[test]
515	fn test_empty_message() {
516		let channel = SecureMessageChannel::new().unwrap();
517
518		let message = TauriIPCMessage::new("test".to_string(), serde_json::json!(null), None);
519
520		let encrypted = channel.encrypt_message(&message).unwrap();
521
522		let decrypted = channel.decrypt_message(&encrypted).unwrap();
523
524		assert_eq!(decrypted.channel, "test");
525	}
526}