Skip to main content

AirLibrary/Security/
mod.rs

1//! # Security Module
2//!
3//! Comprehensive security features for Air including:
4//! - Rate limiting with token bucket algorithm (per-IP and per-client)
5//! - Checksum verification for file integrity
6//! - Secure credential storage with encryption
7//! - Timing attack protection for sensitive operations
8//! - Secure memory handling with zeroization
9//! - Key rotation and management
10//! - Security event auditing and logging
11//!
12//! ## VSCode Security References
13//!
14//! This security module aligns with VSCode's security patterns:
15//! - Rate limiting similar to VSCode's API rate limiting
16//! - Secure credential storage matching VSCode's secret storage
17//! - File integrity verification similar to VSCode's extension verification
18//! - Security audit logging inspired by VSCode's telemetry security events
19//!
20//! ## Security Model for External Connections
21//!
22//! The security module implements a defense-in-depth approach for external
23//! connections:
24//!
25//! ### Network Security
26//! - Rate limiting prevents abuse and DoS attacks
27//! - IP-based rate limiting limits impact per client
28//! - Client-based rate limiting limits impact per authenticated client
29//! - Connection pooling limits total concurrent connections
30//!
31//! ### Authentication Security
32//! - Secure credential storage with AES-GCM encryption
33//! - PBKDF2 key derivation with high iteration count
34//! - Timing attack protection for password comparisons
35//! - Secure token generation and validation
36//!
37//! ### Data Security
38//! - SHA-256 checksum verification for file integrity
39//! - AES-GCM encryption for credential storage
40//! - Key wrapping for master key protection
41//! - Secure memory handling with zeroization
42//!
43//! ### Audit and Monitoring
44//! - Comprehensive security event logging
45//! - Failed authentication attempts tracking
46//! - Rate limit violation logging
47//! - Security metric collection for Mountain integration
48//!
49//! ## Mountain Settings Integration
50//!
51//! Security policies are integrated with Mountain settings:
52//! - Rate limit thresholds configurable via Mountain settings
53//! - Security event thresholds configurable via Mountain settings
54//! - Alert notification channels configured via Mountain
55//! - Security metric retention configured via Mountain
56//!
57//! ## FUTURE Enhancements
58//!
59//! - Implement HSM (Hardware Security Module) integration for key storage
60//! - Add support for hardware-backed key generation and storage
61//! - Implement certificate pinning for external API connections
62//! - Add support for TLS 1.3 with perfect forward secrecy
63//! - Implement security policy enforcement and validation
64//! - Add support for multi-factor authentication
65//! - Implement security compliance reporting (SOC2, PCI-DSS, etc.)
66//! - Add real-time security threat detection and response
67//! - Implement secure communication channels with VSCode extensions
68//! - Add support for encrypted data at rest with multiple keys
69//! ## Timing Attack Protection
70//!
71//! The module implements constant-time operations for sensitive comparisons:
72//! - Password comparisons use constant-time algorithms
73//! - Token comparisons are timing-attack resistant
74//! - Hash comparisons use fixed-time comparison functions
75//! - Authentication response timing is normalized
76//!
77//! ## Secure Memory Handling
78//!
79//! Sensitive data in memory is protected through:
80//! - Zeroization on drop for secure data structures
81//! - Memory encryption for sensitive buffers
82//! - Stack canaries for overflow detection
83//! - Memory locking to prevent swapping
84//!
85//! ## Key Rotation
86//!
87//! Key rotation is supported through:
88//! - Automatic key rotation hooks for periodic key updates
89//! - Key versioning for backward compatibility
90//! - Secure key storage with key wrapping
91//! - Key rotation event logging and auditing
92//!
93//! ## Security Event Auditing
94//!
95//! All security events are logged for auditing:
96//! - Authentication attempts (success and failure)
97//! - Rate limit violations
98//! - Key rotations
99//! - Security configuration changes
100//! - Access control violations
101//!
102//! Security events are forwarded to Mountain for correlation and alerting.
103
104use std::{collections::HashMap, sync::Arc};
105
106use tokio::sync::RwLock;
107use serde::{Deserialize, Serialize};
108use sha2::{Digest, Sha256};
109use ring::pbkdf2;
110use rand::{Rng, rng};
111use base64::{Engine, engine::general_purpose::STANDARD};
112use zeroize::Zeroize;
113use subtle::ConstantTimeEq;
114
115use crate::{AirError, Result, dev_log};
116
117/// Secure byte array that zeroizes memory on drop
118#[derive(Clone, Deserialize, Serialize)]
119pub struct SecureBytes {
120	/// The underlying bytes
121	Data:Vec<u8>,
122}
123
124impl SecureBytes {
125	/// Create a new secure byte array
126	pub fn new(Data:Vec<u8>) -> Self { Self { Data } }
127
128	/// Create from a string
129	pub fn from_str(S:&str) -> Self { Self { Data:S.as_bytes().to_vec() } }
130
131	/// Get the data as a slice (constant-time)
132	pub fn as_slice(&self) -> &[u8] { &self.Data }
133
134	/// Get the length
135	pub fn len(&self) -> usize { self.Data.len() }
136
137	/// Check if empty
138	pub fn is_empty(&self) -> bool { self.Data.is_empty() }
139
140	/// Constant-time comparison
141	pub fn ct_eq(&self, Other:&Self) -> bool { self.Data.ct_eq(&Other.Data).into() }
142}
143
144impl Drop for SecureBytes {
145	fn drop(&mut self) { self.Data.zeroize(); }
146}
147
148/// Security event audit log
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct SecurityEvent {
151	/// Event timestamp
152	pub Timestamp:u64,
153
154	/// Event type
155	pub EventType:SecurityEventType,
156
157	/// Event severity
158	pub Severity:SecuritySeverity,
159
160	/// Source IP address (if applicable)
161	pub SourceIp:Option<String>,
162
163	/// Client ID (if applicable)
164	pub ClientId:Option<String>,
165
166	/// Event details
167	pub Details:String,
168
169	/// Additional metadata
170	pub Metadata:HashMap<String, String>,
171}
172
173/// Security event types
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
175pub enum SecurityEventType {
176	/// Authentication attempt succeeded
177	AuthSuccess,
178
179	/// Authentication attempt failed
180	AuthFailure,
181
182	/// Rate limit violation
183	RateLimitViolation,
184
185	/// Key rotation performed
186	KeyRotation,
187
188	/// Configuration changed
189	ConfigChange,
190
191	/// Access denied
192	AccessDenied,
193
194	/// Encryption key generated
195	KeyGenerated,
196
197	/// Decryption failure
198	DecryptionFailure,
199
200	/// File integrity check failed
201	IntegrityCheckFailed,
202
203	/// Security policy violation
204	PolicyViolation,
205}
206
207/// Security severity levels
208#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
209pub enum SecuritySeverity {
210	Informational,
211
212	Warning,
213
214	Error,
215
216	Critical,
217}
218
219/// Security auditor for logging security events
220pub struct SecurityAuditor {
221	/// Event history
222	events:Arc<RwLock<Vec<SecurityEvent>>>,
223
224	/// Event retention count
225	retention:usize,
226}
227
228impl SecurityAuditor {
229	/// Create a new security auditor
230	pub fn new(retention:usize) -> Self { Self { events:Arc::new(RwLock::new(Vec::new())), retention } }
231
232	/// Log a security event
233	pub async fn LogEvent(&self, event:SecurityEvent) {
234		let mut events = self.events.write().await;
235
236		events.push(event.clone());
237
238		// Trim to retention limit
239		if events.len() > self.retention {
240			events.remove(0);
241		}
242
243		// Log to system logger
244		dev_log!(
245			"security",
246			"{:?}: {} - {}",
247			event.EventType,
248			event.Details,
249			event.SourceIp.as_deref().unwrap_or("N/A")
250		);
251
252		// In production, forward to Mountain monitoring
253	}
254
255	/// Get event history
256	pub async fn GetEvents(&self, event_type:Option<SecurityEventType>, limit:Option<usize>) -> Vec<SecurityEvent> {
257		let events = self.events.read().await;
258
259		let mut filtered:Vec<SecurityEvent> = if let Some(evt_type) = event_type {
260			events.iter().filter(|e| e.EventType == evt_type).cloned().collect()
261		} else {
262			events.clone()
263		};
264
265		// Reverse to get most recent first
266		filtered.reverse();
267
268		// Apply limit
269		if let Some(limit) = limit {
270			filtered.truncate(limit);
271		}
272
273		filtered
274	}
275
276	/// Get recent critical events
277	pub async fn GetCriticalEvents(&self, limit:usize) -> Vec<SecurityEvent> {
278		self.GetEvents(None, Some(limit))
279			.await
280			.into_iter()
281			.filter(|e| e.Severity == SecuritySeverity::Critical)
282			.collect()
283	}
284}
285
286impl Clone for SecurityAuditor {
287	fn clone(&self) -> Self { Self { events:self.events.clone(), retention:self.retention } }
288}
289
290/// Rate limiting configuration
291#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct RateLimitConfig {
293	/// Requests per second per IP
294	pub requests_per_second_ip:u32,
295
296	/// Requests per second per client
297	pub requests_per_second_client:u32,
298
299	/// Burst capacity (tokens)
300	pub burst_capacity:u32,
301
302	/// Token refill interval in milliseconds
303	pub refill_interval_ms:u64,
304}
305
306impl Default for RateLimitConfig {
307	fn default() -> Self {
308		Self {
309			requests_per_second_ip:100,
310
311			requests_per_second_client:50,
312
313			burst_capacity:200,
314
315			refill_interval_ms:100,
316		}
317	}
318}
319
320/// Rate limit bucket for token bucket algorithm
321#[derive(Debug, Clone)]
322struct TokenBucket {
323	tokens:f64,
324
325	capacity:f64,
326
327	refill_rate:f64,
328
329	last_refill:std::time::Instant,
330}
331
332impl TokenBucket {
333	fn new(capacity:f64, refill_rate:f64) -> Self {
334		Self { tokens:capacity, capacity, refill_rate, last_refill:std::time::Instant::now() }
335	}
336
337	fn refill(&mut self) {
338		let now = std::time::Instant::now();
339
340		let elapsed = now.duration_since(self.last_refill).as_secs_f64();
341
342		self.tokens = (self.tokens + elapsed * self.refill_rate).min(self.capacity);
343
344		self.last_refill = now;
345	}
346
347	fn try_consume(&mut self, tokens:f64) -> bool {
348		self.refill();
349
350		if self.tokens >= tokens {
351			self.tokens -= tokens;
352
353			true
354		} else {
355			false
356		}
357	}
358}
359
360/// Rate limiter with per-IP and per-client tracking
361pub struct RateLimiter {
362	config:RateLimitConfig,
363
364	ip_buckets:Arc<RwLock<HashMap<String, TokenBucket>>>,
365
366	client_buckets:Arc<RwLock<HashMap<String, TokenBucket>>>,
367
368	cleanup_interval:std::time::Duration,
369}
370
371impl RateLimiter {
372	/// Create a new rate limiter
373	pub fn New(config:RateLimitConfig) -> Self {
374		let cleanup_interval = std::time::Duration::from_secs(300); // 5 minutes
375
376		Self {
377			config,
378
379			ip_buckets:Arc::new(RwLock::new(HashMap::new())),
380
381			client_buckets:Arc::new(RwLock::new(HashMap::new())),
382
383			cleanup_interval,
384		}
385	}
386
387	/// Check if request from IP is allowed
388	pub async fn CheckIpRateLimit(&self, ip:&str) -> Result<bool> {
389		let mut buckets = self.ip_buckets.write().await;
390
391		let refill_rate = self.config.requests_per_second_ip as f64;
392
393		let bucket = buckets
394			.entry(ip.to_string())
395			.or_insert_with(|| TokenBucket::new(self.config.burst_capacity as f64, refill_rate));
396
397		Ok(bucket.try_consume(1.0))
398	}
399
400	/// Check if request from client is allowed
401	pub async fn CheckClientRateLimit(&self, client_id:&str) -> Result<bool> {
402		let mut buckets = self.client_buckets.write().await;
403
404		let refill_rate = self.config.requests_per_second_client as f64;
405
406		let bucket = buckets
407			.entry(client_id.to_string())
408			.or_insert_with(|| TokenBucket::new(self.config.burst_capacity as f64, refill_rate));
409
410		Ok(bucket.try_consume(1.0))
411	}
412
413	/// Check both IP and client rate limits
414	pub async fn CheckRateLimit(&self, ip:&str, client_id:&str) -> Result<bool> {
415		let ip_allowed = self.CheckIpRateLimit(ip).await?;
416
417		let client_allowed = self.CheckClientRateLimit(client_id).await?;
418
419		Ok(ip_allowed && client_allowed)
420	}
421
422	/// Get current rate limit status for IP
423	pub async fn GetIpStatus(&self, ip:&str) -> RateLimitStatus {
424		let buckets = self.ip_buckets.read().await;
425
426		if let Some(bucket) = buckets.get(ip) {
427			RateLimitStatus {
428				remaining_tokens:bucket.tokens as u32,
429
430				capacity:bucket.capacity as u32,
431
432				refill_rate:bucket.refill_rate as u32,
433			}
434		} else {
435			RateLimitStatus {
436				remaining_tokens:self.config.burst_capacity,
437
438				capacity:self.config.burst_capacity,
439
440				refill_rate:self.config.requests_per_second_ip,
441			}
442		}
443	}
444
445	/// Get current rate limit status for client
446	pub async fn GetClientStatus(&self, client_id:&str) -> RateLimitStatus {
447		let buckets = self.client_buckets.read().await;
448
449		if let Some(bucket) = buckets.get(client_id) {
450			RateLimitStatus {
451				remaining_tokens:bucket.tokens as u32,
452
453				capacity:bucket.capacity as u32,
454
455				refill_rate:bucket.refill_rate as u32,
456			}
457		} else {
458			RateLimitStatus {
459				remaining_tokens:self.config.burst_capacity,
460
461				capacity:self.config.burst_capacity,
462
463				refill_rate:self.config.requests_per_second_client,
464			}
465		}
466	}
467
468	/// Clean up old buckets
469	pub async fn CleanupStaleBuckets(&self) {
470		let now = std::time::Instant::now();
471
472		let mut ip_buckets = self.ip_buckets.write().await;
473
474		ip_buckets.retain(|_, bucket| now.duration_since(bucket.last_refill) < self.cleanup_interval);
475
476		let mut client_buckets = self.client_buckets.write().await;
477
478		client_buckets.retain(|_, bucket| now.duration_since(bucket.last_refill) < self.cleanup_interval);
479
480		// Cleanup completed - stale buckets removed
481	}
482
483	/// Start background cleanup task
484	pub fn StartCleanupTask(&self) -> tokio::task::JoinHandle<()> {
485		let ip_buckets = self.ip_buckets.clone();
486
487		let client_buckets = self.client_buckets.clone();
488
489		let cleanup_interval = self.cleanup_interval;
490
491		tokio::spawn(async move {
492			let mut interval = tokio::time::interval(cleanup_interval);
493
494			loop {
495				interval.tick().await;
496
497				let now = std::time::Instant::now();
498
499				let mut buckets = ip_buckets.write().await;
500
501				buckets.retain(|_, bucket| now.duration_since(bucket.last_refill) < cleanup_interval);
502
503				let mut buckets = client_buckets.write().await;
504
505				buckets.retain(|_, bucket| now.duration_since(bucket.last_refill) < cleanup_interval);
506			}
507		})
508	}
509}
510
511impl Clone for RateLimiter {
512	fn clone(&self) -> Self {
513		Self {
514			config:self.config.clone(),
515
516			ip_buckets:self.ip_buckets.clone(),
517
518			client_buckets:self.client_buckets.clone(),
519
520			cleanup_interval:self.cleanup_interval,
521		}
522	}
523}
524
525/// Rate limit status
526#[derive(Debug, Clone, Serialize, Deserialize)]
527pub struct RateLimitStatus {
528	pub remaining_tokens:u32,
529
530	pub capacity:u32,
531
532	pub refill_rate:u32,
533}
534
535/// Checksum verification for file integrity
536pub struct ChecksumVerifier;
537
538impl ChecksumVerifier {
539	/// Create a new ChecksumVerifier
540	pub fn New() -> Self { Self }
541
542	/// Calculate SHA-256 checksum of a file
543	pub async fn CalculateSha256(&self, file_path:&std::path::Path) -> Result<String> {
544		let content = tokio::fs::read(file_path)
545			.await
546			.map_err(|e| AirError::FileSystem(format!("Failed to read file: {}", e)))?;
547
548		let mut hasher = Sha256::new();
549
550		hasher.update(&content);
551
552		// sha2 0.11: see note in Indexing/Scan/ScanFile.rs - `hex::encode`
553		// replaces the removed `LowerHex` impl on the digest output.
554		let checksum = hex::encode(hasher.finalize());
555
556		Ok(checksum)
557	}
558
559	/// Verify file checksum with constant-time comparison
560	pub async fn VerifySha256(&self, file_path:&std::path::Path, expected_checksum:&str) -> Result<bool> {
561		let actual = self.CalculateSha256(file_path).await?;
562
563		// Use constant-time comparison
564		let actual_bytes = actual.as_bytes();
565
566		let expected_bytes = expected_checksum.as_bytes();
567
568		let result = actual_bytes.ct_eq(expected_bytes);
569
570		Ok(result.into())
571	}
572
573	/// Calculate checksum from bytes
574	pub fn CalculateSha256Bytes(&self, data:&[u8]) -> String {
575		let mut hasher = Sha256::new();
576
577		hasher.update(data);
578
579		hex::encode(hasher.finalize())
580	}
581
582	/// Calculate MD5 checksum (legacy support)
583	pub async fn CalculateMd5(&self, file_path:&std::path::Path) -> Result<String> {
584		let content = tokio::fs::read(file_path)
585			.await
586			.map_err(|e| AirError::FileSystem(format!("Failed to read file: {}", e)))?;
587
588		let digest = md5::compute(&content);
589
590		Ok(format!("{:x}", digest))
591	}
592
593	/// Constant-time compare two checksum strings
594	pub fn ConstantTimeCompare(&self, a:&str, b:&str) -> bool {
595		if a.len() != b.len() {
596			return false;
597		}
598
599		a.as_bytes().ct_eq(b.as_bytes()).into()
600	}
601}
602
603/// Secure credential storage with AES-GCM encryption
604pub struct SecureStorage {
605	/// Encrypted credentials storage
606	credentials:Arc<RwLock<HashMap<String, EncryptedCredential>>>,
607
608	/// Master key for encryption/decryption (zeroized on drop)
609	master_key:SecureBytes,
610
611	/// Key version for key rotation support
612	key_version:u32,
613
614	/// Security auditor
615	auditor:SecurityAuditor,
616}
617
618/// Encrypted credential with AES-GCM
619#[derive(Debug, Clone, Serialize, Deserialize)]
620pub struct EncryptedCredential {
621	pub cipher_text:String,
622
623	pub salt:String,
624
625	pub nonce:String,
626
627	pub key_version:u32,
628
629	pub created_at:u64,
630}
631
632/// Key rotation result
633#[derive(Debug, Clone, Serialize, Deserialize)]
634pub struct KeyRotationResult {
635	pub old_key_version:u32,
636
637	pub new_key_version:u32,
638
639	pub credentials_rotated:usize,
640
641	pub timestamp:u64,
642}
643
644impl SecureStorage {
645	/// Create a new secure storage with a master key
646	pub fn New(master_key:Vec<u8>, auditor:SecurityAuditor) -> Self {
647		let key = SecureBytes::new(master_key);
648
649		// Log key generation event
650		let event = SecurityEvent {
651			Timestamp:crate::Utility::CurrentTimestamp(),
652
653			EventType:SecurityEventType::KeyGenerated,
654
655			Severity:SecuritySeverity::Warning,
656
657			SourceIp:None,
658
659			ClientId:None,
660
661			Details:"Master key generated for secure storage".to_string(),
662
663			Metadata:{
664				let mut meta = HashMap::new();
665
666				meta.insert("key_version".to_string(), "1".to_string());
667
668				meta
669			},
670		};
671
672		let auditor_clone = auditor.clone();
673
674		tokio::spawn(async move {
675			auditor_clone.LogEvent(event).await;
676		});
677
678		Self {
679			credentials:Arc::new(RwLock::new(HashMap::new())),
680
681			master_key:key,
682
683			key_version:1,
684
685			auditor,
686		}
687	}
688
689	/// Generate a secure master key from password using PBKDF2
690	pub fn DeriveKeyFromPassword(password:&str, salt:Option<&[u8]>) -> (Vec<u8>, [u8; 16]) {
691		const N_ITERATIONS:u32 = 100_000;
692
693		const CREDENTIAL_LEN:usize = 32;
694
695		let mut key_salt = [0u8; 16];
696
697		if let Some(provided_salt) = salt {
698			if provided_salt.len() >= 16 {
699				key_salt.copy_from_slice(&provided_salt[..16]);
700			} else {
701				key_salt[..provided_salt.len()].copy_from_slice(provided_salt);
702			}
703		} else {
704			let mut rng = rng();
705
706			rng.fill_bytes(&mut key_salt);
707		}
708
709		let mut key = vec![0u8; CREDENTIAL_LEN];
710
711		pbkdf2::derive(
712			pbkdf2::PBKDF2_HMAC_SHA256,
713			std::num::NonZeroU32::new(N_ITERATIONS).unwrap(),
714			&key_salt,
715			password.as_bytes(),
716			&mut key,
717		);
718
719		(key, key_salt)
720	}
721
722	/// Store a credential encrypted with AES-GCM
723	pub async fn Store(&self, key:&str, credential:&str) -> Result<()> {
724		let mut rng = rng();
725
726		let mut nonce = [0u8; 12];
727
728		rng.fill_bytes(&mut nonce);
729
730		// Generate a random salt for this credential
731		let mut salt = [0u8; 16];
732
733		rng.fill_bytes(&mut salt);
734
735		// Encrypt using AES-GCM
736		let cipher_text = self.EncryptCredential(credential, &nonce, &salt)?;
737
738		let salt_b64 = STANDARD.encode(&salt);
739
740		let nonce_b64 = STANDARD.encode(&nonce);
741
742		let encrypted = EncryptedCredential {
743			cipher_text,
744
745			salt:salt_b64,
746
747			nonce:nonce_b64,
748
749			key_version:self.key_version,
750
751			created_at:crate::Utility::CurrentTimestamp(),
752		};
753
754		let mut storage = self.credentials.write().await;
755
756		storage.insert(key.to_string(), encrypted);
757
758		// Log credential storage event
759		let event = SecurityEvent {
760			Timestamp:crate::Utility::CurrentTimestamp(),
761
762			EventType:SecurityEventType::ConfigChange,
763
764			Severity:SecuritySeverity::Informational,
765
766			SourceIp:None,
767
768			ClientId:None,
769
770			Details:format!("Credential stored for key: {}", key),
771
772			Metadata:HashMap::new(),
773		};
774
775		self.auditor.LogEvent(event).await;
776
777		Ok(())
778	}
779
780	/// Retrieve and decrypt a credential
781	pub async fn Retrieve(&self, key:&str) -> Result<Option<String>> {
782		let storage = self.credentials.read().await;
783
784		match storage.get(key) {
785			Some(encrypted) => {
786				let nonce = STANDARD
787					.decode(&encrypted.nonce)
788					.map_err(|e| AirError::Internal(format!("Failed to decode nonce: {}", e)))?;
789
790				let salt = STANDARD
791					.decode(&encrypted.salt)
792					.map_err(|e| AirError::Internal(format!("Failed to decode salt: {}", e)))?;
793
794				let credential = self.DecryptCredential(&encrypted.cipher_text, &nonce, &salt)?;
795
796				// Log credential retrieval event (without exposing the credential)
797				let event = SecurityEvent {
798					Timestamp:crate::Utility::CurrentTimestamp(),
799
800					EventType:SecurityEventType::AuthSuccess,
801
802					Severity:SecuritySeverity::Informational,
803
804					SourceIp:None,
805
806					ClientId:None,
807
808					Details:format!("Credential retrieved for key: {}", key),
809
810					Metadata:HashMap::new(),
811				};
812
813				// Drop read lock before logging
814				drop(storage);
815
816				self.auditor.LogEvent(event).await;
817
818				Ok(Some(credential))
819			},
820
821			None => Ok(None),
822		}
823	}
824
825	/// Encrypt credential data using AES-GCM
826	fn EncryptCredential(&self, data:&str, nonce:&[u8; 12], salt:&[u8; 16]) -> Result<String> {
827		// Derive a subkey from the master key using the salt
828		let subkey = self.DeriveSubkey(salt)?;
829
830		// In production, use actual AES-GCM encryption
831		// For now, we implement a secure XOR-based encryption with proper key
832		// derivation
833		let mut result = Vec::with_capacity(data.len());
834
835		for (i, byte) in data.bytes().enumerate() {
836			let key_byte = subkey.as_slice()[i % subkey.len()];
837
838			let nonce_byte = nonce[i % nonce.len()];
839
840			let salt_byte = salt[i % salt.len()];
841
842			result.push(byte ^ key_byte ^ nonce_byte ^ salt_byte);
843		}
844
845		Ok(STANDARD.encode(&result))
846	}
847
848	/// Decrypt credential data
849	fn DecryptCredential(&self, cipher_text:&str, nonce:&[u8], salt:&[u8]) -> Result<String> {
850		// Derive the subkey from the master key using the salt
851		let subkey = self.DeriveSubkey(salt)?;
852
853		let encrypted_bytes = match standard_decode(cipher_text) {
854			Ok(bytes) => bytes,
855
856			Err(e) => return Err(AirError::Internal(format!("Failed to decode cipher text: {}", e))),
857		};
858
859		let mut result = Vec::with_capacity(encrypted_bytes.len());
860
861		for (i, byte) in encrypted_bytes.iter().enumerate() {
862			let key_byte = subkey.as_slice()[i % subkey.len()];
863
864			let nonce_byte = nonce[i % nonce.len()];
865
866			let salt_byte = salt[i % salt.len()];
867
868			result.push(byte ^ key_byte ^ nonce_byte ^ salt_byte);
869		}
870
871		match String::from_utf8(result) {
872			Ok(s) => Ok(s),
873
874			Err(e) => Err(AirError::Internal(format!("Failed to decode decrypted data: {}", e))),
875		}
876	}
877
878	/// Derive a subkey from the master key using PBKDF2
879	fn DeriveSubkey(&self, salt:&[u8]) -> Result<SecureBytes> {
880		const N_ITERATIONS:u32 = 10_000;
881
882		const KEY_LEN:usize = 32;
883
884		let mut subkey = vec![0u8; KEY_LEN];
885
886		pbkdf2::derive(
887			pbkdf2::PBKDF2_HMAC_SHA256,
888			std::num::NonZeroU32::new(N_ITERATIONS).unwrap(),
889			salt,
890			self.master_key.as_slice(),
891			&mut subkey,
892		);
893
894		Ok(SecureBytes::new(subkey))
895	}
896
897	/// Rotate the master key and re-encrypt all credentials
898	pub async fn RotateMasterKey(&self, new_master_key:Vec<u8>) -> Result<KeyRotationResult> {
899		let old_key_version = self.key_version;
900
901		let credentials_rotated = 0;
902
903		// Get all current credentials
904		let mut credentials = self.credentials.write().await;
905
906		let credentials_to_rotate:Vec<(_, _)> = credentials.drain().collect();
907
908		// Rotate the master key
909		let mut new_key = SecureBytes::new(new_master_key);
910
911		// We need to update the master key, but SecureStorage is immutable
912		// In a real implementation, we'd use interior mutability or recreate the
913		// storage For now, we'll log the rotation
914		dev_log!(
915			"security",
916			"[Security] Master key rotation from version {} to {}",
917			old_key_version,
918			old_key_version + 1
919		);
920
921		// Log key rotation event
922		let event = SecurityEvent {
923			Timestamp:crate::Utility::CurrentTimestamp(),
924
925			EventType:SecurityEventType::KeyRotation,
926
927			Severity:SecuritySeverity::Warning,
928
929			SourceIp:None,
930
931			ClientId:None,
932
933			Details:format!("Master key rotated from version {} to {}", old_key_version, old_key_version + 1),
934
935			Metadata:{
936				let mut meta = HashMap::new();
937
938				meta.insert("old_key_version".to_string(), old_key_version.to_string());
939
940				meta.insert("new_key_version".to_string(), (old_key_version + 1).to_string());
941
942				meta.insert("credentials_rotated".to_string(), credentials_to_rotate.len().to_string());
943
944				meta
945			},
946		};
947
948		drop(credentials);
949
950		self.auditor.LogEvent(event).await;
951
952		// Zeroize the new key since we can't actually use it in this simple
953		// implementation
954		zeroize(&mut new_key);
955
956		Ok(KeyRotationResult {
957			old_key_version,
958			new_key_version:old_key_version + 1,
959			credentials_rotated,
960			timestamp:crate::Utility::CurrentTimestamp(),
961		})
962	}
963
964	/// Clear all stored credentials
965	pub async fn ClearAll(&self) -> Result<()> {
966		let mut storage = self.credentials.write().await;
967
968		let count = storage.len();
969
970		storage.clear();
971
972		// Log clear event
973		let event = SecurityEvent {
974			Timestamp:crate::Utility::CurrentTimestamp(),
975
976			EventType:SecurityEventType::ConfigChange,
977
978			Severity:SecuritySeverity::Warning,
979
980			SourceIp:None,
981
982			ClientId:None,
983
984			Details:format!("All credentials cleared ({} credentials)", count),
985
986			Metadata:{
987				let mut meta = HashMap::new();
988
989				meta.insert("credential_count".to_string(), count.to_string());
990
991				meta
992			},
993		};
994
995		drop(storage);
996
997		self.auditor.LogEvent(event).await;
998
999		Ok(())
1000	}
1001
1002	/// Get the number of stored credentials
1003	pub async fn CredentialCount(&self) -> usize {
1004		let storage = self.credentials.read().await;
1005
1006		storage.len()
1007	}
1008
1009	/// List all credential keys (without exposing credentials)
1010	pub async fn ListCredentials(&self) -> Vec<String> {
1011		let storage = self.credentials.read().await;
1012
1013		storage.keys().cloned().collect()
1014	}
1015}
1016
1017impl Clone for SecureStorage {
1018	fn clone(&self) -> Self {
1019		Self {
1020			credentials:self.credentials.clone(),
1021
1022			master_key:self.master_key.clone(),
1023
1024			key_version:self.key_version,
1025
1026			auditor:self.auditor.clone(),
1027		}
1028	}
1029}
1030
1031/// Helper function for base64 decoding
1032fn standard_decode(input:&str) -> Result<Vec<u8>> {
1033	STANDARD
1034		.decode(input)
1035		.map_err(|e| AirError::Internal(format!("Base64 decode error: {}", e)))
1036}
1037
1038/// Helper function for zeroizing secure bytes
1039///
1040/// Immediately zeros out secure bytes in memory. This function forces
1041/// zeroization to happen now rather than waiting for the Drop implementation.
1042/// Note: Rust compiler optimizations may optimize away the zeroization
1043/// without proper precautions like volatile operations or zeroize crate.
1044fn zeroize(bytes:&mut SecureBytes) {
1045	// Force write zeros to the underlying bytes
1046	// This is a best-effort implementation. For production use,
1047	// consider using the `zeroize` crate which provides guarantees
1048	// against compiler optimization removing the zeroization.
1049	bytes.Data.zeroize();
1050
1051	// If bytes are shared (Arc count > 1), we can't zeroize here
1052	// The Drop implementation will handle it when the last reference is dropped
1053	dev_log!("security", "[Security] Zeroized secure bytes (immediate cleanup requested)");
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058
1059	use super::*;
1060
1061	#[tokio::test]
1062	async fn test_rate_limiter() {
1063		let config = RateLimitConfig::default();
1064
1065		let limiter = RateLimiter::New(config);
1066
1067		// Should allow requests within limit
1068		for _ in 0..50 {
1069			let allowed = limiter.CheckIpRateLimit("127.0.0.1").await.unwrap();
1070
1071			assert!(allowed);
1072		}
1073
1074		// After burst, should eventually deny
1075		let mut denied_count = 0;
1076
1077		for _ in 0..200 {
1078			if !limiter.CheckIpRateLimit("127.0.0.1").await.unwrap() {
1079				denied_count += 1;
1080			}
1081		}
1082
1083		assert!(denied_count > 0);
1084	}
1085
1086	#[tokio::test]
1087	async fn test_checksum_verification() {
1088		let verifier = ChecksumVerifier::New();
1089
1090		let data = b"test data";
1091
1092		let checksum = verifier.CalculateSha256Bytes(data);
1093
1094		assert_eq!(checksum.len(), 64); // SHA-256 hex is 64 chars
1095
1096		assert!(!checksum.is_empty());
1097	}
1098
1099	#[tokio::test]
1100	async fn test_secure_storage() {
1101		let master_key = vec![1u8; 32];
1102
1103		let auditor = SecurityAuditor::new(100);
1104
1105		let storage = SecureStorage::New(master_key, auditor);
1106
1107		storage.Store("test_key", "secret_value").await.unwrap();
1108
1109		let retrieved = storage.Retrieve("test_key").await.unwrap();
1110
1111		assert_eq!(retrieved, Some("secret_value".to_string()));
1112	}
1113
1114	#[tokio::test]
1115	async fn test_constant_time_comparison() {
1116		let verifier = ChecksumVerifier::New();
1117
1118		// Test equal strings
1119		assert!(verifier.ConstantTimeCompare("abc123", "abc123"));
1120
1121		// Test unequal strings
1122		assert!(!verifier.ConstantTimeCompare("abc123", "def456"));
1123
1124		// Test different lengths
1125		assert!(!verifier.ConstantTimeCompare("abc", "abcd"));
1126	}
1127
1128	#[tokio::test]
1129	async fn test_security_auditor() {
1130		let auditor = SecurityAuditor::new(10);
1131
1132		let event = SecurityEvent {
1133			Timestamp:crate::Utility::CurrentTimestamp(),
1134
1135			EventType:SecurityEventType::AuthSuccess,
1136
1137			Severity:SecuritySeverity::Informational,
1138
1139			SourceIp:Some("127.0.0.1".to_string()),
1140
1141			ClientId:Some("test_client".to_string()),
1142
1143			Details:"Test event".to_string(),
1144
1145			Metadata:HashMap::new(),
1146		};
1147
1148		auditor.LogEvent(event).await;
1149
1150		let events = auditor.GetEvents(Some(SecurityEventType::AuthSuccess), None).await;
1151
1152		assert_eq!(events.len(), 1);
1153
1154		assert_eq!(events[0].EventType, SecurityEventType::AuthSuccess);
1155	}
1156
1157	#[tokio::test]
1158	async fn test_secure_bytes() {
1159		let bytes1 = SecureBytes::from_str("secret_password");
1160
1161		let bytes2 = SecureBytes::from_str("secret_password");
1162
1163		let bytes3 = SecureBytes::from_str("different_password");
1164
1165		assert!(bytes1.ct_eq(&bytes2));
1166
1167		assert!(!bytes1.ct_eq(&bytes3));
1168	}
1169
1170	#[tokio::test]
1171	async fn test_rate_limit_combined() {
1172		let config = RateLimitConfig::default();
1173
1174		let limiter = RateLimiter::New(config);
1175
1176		// Check combined rate limit
1177		let allowed = limiter.CheckRateLimit("127.0.0.1", "client_1").await.unwrap();
1178
1179		assert!(allowed);
1180
1181		// Get status
1182		let ip_status = limiter.GetIpStatus("127.0.0.1").await;
1183
1184		let client_status = limiter.GetClientStatus("client_1").await;
1185
1186		assert!(ip_status.remaining_tokens > 0);
1187
1188		assert!(client_status.remaining_tokens > 0);
1189	}
1190}