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};
41use log::debug;
42
43use super::super::Message::TauriIPCMessage;
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			&& !self.hmac_tag.is_empty()
90	}
91}
92
93/// Secure message channel with encryption and authentication
94///
95/// This structure provides AES-256-GCM encryption with HMAC authentication
96/// for secure IPC communication. It ensures message confidentiality and
97/// integrity.
98///
99/// ## Encryption Flow
100///
101/// ```text
102/// TauriIPCMessage
103///     |
104///     | 1. Serialize to JSON
105///     v
106/// Serialized bytes
107///     |
108///     | 2. Encrypt with AES-256-GCM
109///     v
110/// Encrypted bytes + auth tag
111///     |
112///     | 3. Generate HMAC
113///     v
114/// EncryptedMessage (nonce, ciphertext, hmac_tag)
115/// ```
116///
117/// ## Decryption Flow
118///
119/// ```text
120/// EncryptedMessage
121///     |
122///     | 1. Verify HMAC
123///     v
124/// HMAC valid
125///     |
126///     | 2. Decrypt with AES-256-GCM
127///     v
128/// Serialized bytes
129///     |
130///     | 3. Deserialize to TauriIPCMessage
131///     v
132/// TauriIPCMessage
133/// ```
134///
135/// ## Security Features
136///
137/// - **AES-256-GCM**: Industry-standard authenticated encryption
138/// - **Unique Nonces**: Each encryption uses a unique nonce
139/// - **HMAC Authentication**: Additional layer of message authentication
140/// - **Secure Random Generation**: Cryptographically secure random keys
141///
142/// ## Example Usage
143///
144/// ```rust,ignore
145/// let secure_channel = SecureMessageChannel::new()?;
146///
147/// // Encrypt a message
148/// let encrypted = secure_channel.encrypt_message(&message)?;
149///
150/// // Decrypt a message
151/// let decrypted = secure_channel.decrypt_message(&encrypted)?;
152///
153/// // Rotate keys
154/// secure_channel.rotate_keys()?;
155/// ```
156pub struct SecureMessageChannel {
157	/// AES-256-GCM encryption key
158	encryption_key:LessSafeKey,
159
160	/// HMAC key for message authentication
161	hmac_key:Vec<u8>,
162}
163
164impl SecureMessageChannel {
165	/// Create a new secure channel with randomly generated keys
166	///
167	/// This method generates cryptographically secure random keys for
168	/// encryption and HMAC authentication.
169	///
170	/// ## Returns
171	/// - `Ok(SecureMessageChannel)`: New secure channel
172	/// - `Err(String)`: Error message if key generation fails
173	///
174	/// ## Example
175	///
176	/// ```rust,ignore
177	/// let secure_channel = SecureMessageChannel::new()?;
178	/// ```
179	pub fn new() -> Result<Self, String> {
180		debug!("[SecureMessageChannel] Creating new secure channel");
181
182		let rng = SystemRandom::new();
183
184		// Generate 256-bit (32-byte) encryption key
185		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		// Generate 256-bit HMAC key
195		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	/// Encrypt and authenticate a message
205	///
206	/// This method serializes the message, encrypts it with AES-256-GCM,
207	/// and adds an HMAC tag for authentication.
208	///
209	/// ## Parameters
210	/// - `message`: The message to encrypt
211	///
212	/// ## Returns
213	/// - `Ok(EncryptedMessage)`: Encrypted message with nonce and HMAC tag
214	/// - `Err(String)`: Error message if encryption fails
215	///
216	/// ## Example
217	///
218	/// ```rust,ignore
219	/// let encrypted = secure_channel.encrypt_message(&message)?;
220	/// ```
221	pub fn encrypt_message(&self, message:&TauriIPCMessage) -> Result<EncryptedMessage, String> {
222		debug!("[SecureMessageChannel] Encrypting message on channel: {}", message.channel);
223
224		// Serialize message to bytes
225		let serialized_message =
226			serde_json::to_vec(message).map_err(|e| format!("Failed to serialize message: {}", e))?;
227
228		// Generate unique 12-byte nonce (required for AES-256-GCM)
229		let mut nonce = [0u8; 12];
230		SystemRandom::new()
231			.fill(&mut nonce)
232			.map_err(|e| format!("Failed to generate nonce: {}", e))?;
233
234		// Encrypt with AES-256-GCM (authenticated encryption)
235		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		// Generate HMAC for additional authentication
241		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	/// Decrypt and verify a message
257	///
258	/// This method verifies the HMAC tag, decrypts the message with
259	/// AES-256-GCM, and deserializes it back to the original format.
260	///
261	/// ## Parameters
262	/// - `encrypted`: The encrypted message to decrypt
263	///
264	/// ## Returns
265	/// - `Ok(TauriIPCMessage)`: Decrypted message
266	/// - `Err(String)`: Error message if decryption or verification fails
267	///
268	/// ## Example
269	///
270	/// ```rust,ignore
271	/// let decrypted = secure_channel.decrypt_message(&encrypted)?;
272	/// ```
273	pub fn decrypt_message(&self, encrypted:&EncryptedMessage) -> Result<TauriIPCMessage, String> {
274		debug!("[SecureMessageChannel] Decrypting message");
275
276		// Verify HMAC first (detect tampering)
277		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		// Convert nonce slice to array
282		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		// Decrypt with AES-256-GCM
290		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		// Remove the authentication tag (last 16 bytes for AES-256-GCM)
296		let plaintext_len = in_out.len() - AES_256_GCM.tag_len();
297		in_out.truncate(plaintext_len);
298
299		// Deserialize message
300		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	/// Rotate encryption keys
312	///
313	/// This method generates new encryption and HMAC keys, effectively
314	/// rotating the security credentials for the channel.
315	///
316	/// ## Returns
317	/// - `Ok(())`: Keys rotated successfully
318	/// - `Err(String)`: Error message if key rotation fails
319	///
320	/// ## Example
321	///
322	/// ```rust,ignore
323	/// secure_channel.rotate_keys()?;
324	/// ```
325	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	/// Get the HMAC tag length (in bytes)
336	pub fn hmac_tag_length(&self) -> usize {
337		32 // HMAC-SHA256 produces 32-byte tags
338	}
339
340	/// Get the nonce length (in bytes)
341	pub fn nonce_length(&self) -> usize {
342		12 // AES-256-GCM requires 12-byte nonces
343	}
344
345	/// Get the authentication tag length (in bytes)
346	pub fn auth_tag_length(&self) -> usize { AES_256_GCM.tag_len() }
347
348	/// Get the key length (in bytes)
349	pub fn key_length(&self) -> usize {
350		32 // AES-256 uses 32-byte keys
351	}
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		// Encrypt
382		let encrypted = channel.encrypt_message(&original_message).unwrap();
383		assert!(encrypted.is_valid());
384
385		// Decrypt
386		let decrypted = channel.decrypt_message(&encrypted).unwrap();
387
388		// Verify content
389		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		// Each encryption should produce different output (due to unique nonces)
403		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		// Tamper with the ciphertext
415		if !encrypted.ciphertext.is_empty() {
416			encrypted.ciphertext[0] ^= 0xFF;
417		}
418
419		// Should fail HMAC verification
420		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		// Corrupt the nonce length
433		encrypted.nonce = vec![0u8; 16]; // Wrong length
434
435		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); // AES-256-GCM
447		assert_eq!(channel.hmac_tag_length(), 32); // HMAC-SHA256
448	}
449
450	#[test]
451	fn test_key_rotation() {
452		let mut channel = SecureMessageChannel::new().unwrap();
453		let message = create_test_message();
454
455		// Encrypt with original keys
456		let encrypted1 = channel.encrypt_message(&message).unwrap();
457
458		// Rotate keys
459		let result = channel.rotate_keys();
460		assert!(result.is_ok());
461
462		// Old encrypted message should still decode successfully
463		let decrypted1 = channel.decrypt_message(&encrypted1).unwrap();
464		assert_eq!(decrypted1.channel, message.channel);
465
466		// New encryption should work with new keys
467		let encrypted2 = channel.encrypt_message(&message).unwrap();
468		let decrypted2 = channel.decrypt_message(&encrypted2).unwrap();
469		assert_eq!(decrypted2.channel, message.channel);
470
471		// Encrypted versions should be different
472		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}