1#[cfg(not(feature = "crypto"))]
63#[deprecated(
64 since = "0.4.3",
65 note = "INSECURE: Memory encryption uses XOR fallback without `crypto` feature. Enable `crypto` for production."
66)]
67#[allow(dead_code)]
68const INSECURE_CRYPTO_FALLBACK: () = ();
69
70use std::collections::{HashMap, HashSet};
71use std::fmt;
72use std::sync::atomic::{AtomicU64, Ordering};
73use std::sync::RwLock;
74use std::time::{Duration, Instant, SystemTime};
75
76use crate::KernelId;
77
78#[cfg(feature = "crypto")]
80use aes_gcm::{
81 aead::{Aead, KeyInit},
82 Aes256Gcm, Nonce as AesNonce,
83};
84#[cfg(feature = "crypto")]
85use chacha20poly1305::{ChaCha20Poly1305, Nonce as ChaNonce, XChaCha20Poly1305, XNonce};
86#[cfg(feature = "crypto")]
87use rand::{rngs::OsRng, RngCore};
88#[cfg(feature = "crypto")]
89use zeroize::Zeroize;
90
91#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
97pub enum EncryptionAlgorithm {
98 #[default]
100 Aes256Gcm,
101 Aes128Gcm,
103 ChaCha20Poly1305,
105 XChaCha20Poly1305,
107}
108
109impl fmt::Display for EncryptionAlgorithm {
110 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111 match self {
112 Self::Aes256Gcm => write!(f, "AES-256-GCM"),
113 Self::Aes128Gcm => write!(f, "AES-128-GCM"),
114 Self::ChaCha20Poly1305 => write!(f, "ChaCha20-Poly1305"),
115 Self::XChaCha20Poly1305 => write!(f, "XChaCha20-Poly1305"),
116 }
117 }
118}
119
120#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
122pub enum KeyDerivation {
123 #[default]
125 HkdfSha256,
126 HkdfSha384,
128 Argon2id,
130 Pbkdf2Sha256,
132}
133
134#[derive(Debug, Clone)]
136pub struct EncryptionConfig {
137 pub algorithm: EncryptionAlgorithm,
139 pub key_derivation: KeyDerivation,
141 pub key_rotation_interval: Duration,
143 pub encrypt_control_blocks: bool,
145 pub encrypt_message_queues: bool,
147 pub encrypt_kernel_state: bool,
149 pub aad_prefix: Option<Vec<u8>>,
151}
152
153impl Default for EncryptionConfig {
154 fn default() -> Self {
155 Self {
156 algorithm: EncryptionAlgorithm::default(),
157 key_derivation: KeyDerivation::default(),
158 key_rotation_interval: Duration::from_secs(3600), encrypt_control_blocks: true,
160 encrypt_message_queues: true,
161 encrypt_kernel_state: true,
162 aad_prefix: None,
163 }
164 }
165}
166
167impl EncryptionConfig {
168 pub fn new() -> Self {
170 Self::default()
171 }
172
173 pub fn with_algorithm(mut self, algorithm: EncryptionAlgorithm) -> Self {
175 self.algorithm = algorithm;
176 self
177 }
178
179 pub fn with_key_derivation(mut self, kdf: KeyDerivation) -> Self {
181 self.key_derivation = kdf;
182 self
183 }
184
185 pub fn with_key_rotation_interval(mut self, interval: Duration) -> Self {
187 self.key_rotation_interval = interval;
188 self
189 }
190
191 pub fn with_control_block_encryption(mut self, enabled: bool) -> Self {
193 self.encrypt_control_blocks = enabled;
194 self
195 }
196
197 pub fn with_message_queue_encryption(mut self, enabled: bool) -> Self {
199 self.encrypt_message_queues = enabled;
200 self
201 }
202
203 pub fn with_kernel_state_encryption(mut self, enabled: bool) -> Self {
205 self.encrypt_kernel_state = enabled;
206 self
207 }
208
209 pub fn with_aad_prefix(mut self, prefix: Vec<u8>) -> Self {
211 self.aad_prefix = Some(prefix);
212 self
213 }
214}
215
216#[derive(Clone)]
221pub struct EncryptionKey {
222 pub key_id: u64,
224 #[cfg(feature = "crypto")]
226 key_material: zeroize::Zeroizing<Vec<u8>>,
227 #[cfg(not(feature = "crypto"))]
228 key_material: Vec<u8>,
229 pub created_at: Instant,
231 pub expires_at: Option<Instant>,
233 pub algorithm: EncryptionAlgorithm,
235}
236
237impl EncryptionKey {
238 #[cfg(feature = "crypto")]
243 pub fn new(key_id: u64, algorithm: EncryptionAlgorithm) -> Self {
244 let key_size = match algorithm {
245 EncryptionAlgorithm::Aes256Gcm
246 | EncryptionAlgorithm::ChaCha20Poly1305
247 | EncryptionAlgorithm::XChaCha20Poly1305 => 32,
248 EncryptionAlgorithm::Aes128Gcm => 16,
249 };
250
251 let mut key_material = vec![0u8; key_size];
252 OsRng.fill_bytes(&mut key_material);
253
254 Self {
255 key_id,
256 key_material: zeroize::Zeroizing::new(key_material),
257 created_at: Instant::now(),
258 expires_at: None,
259 algorithm,
260 }
261 }
262
263 #[cfg(not(feature = "crypto"))]
269 #[deprecated(
270 since = "0.4.3",
271 note = "Using insecure XOR-based demo encryption. Enable the `crypto` feature for real AES-256-GCM."
272 )]
273 pub fn new(key_id: u64, algorithm: EncryptionAlgorithm) -> Self {
274 let key_size = match algorithm {
275 EncryptionAlgorithm::Aes256Gcm
276 | EncryptionAlgorithm::ChaCha20Poly1305
277 | EncryptionAlgorithm::XChaCha20Poly1305 => 32,
278 EncryptionAlgorithm::Aes128Gcm => 16,
279 };
280
281 let key_material: Vec<u8> = (0..key_size)
283 .map(|i| ((key_id as u8).wrapping_add(i as u8)).wrapping_mul(17))
284 .collect();
285
286 Self {
287 key_id,
288 key_material,
289 created_at: Instant::now(),
290 expires_at: None,
291 algorithm,
292 }
293 }
294
295 #[cfg(feature = "crypto")]
297 pub fn from_material(key_id: u64, algorithm: EncryptionAlgorithm, material: Vec<u8>) -> Self {
298 Self {
299 key_id,
300 key_material: zeroize::Zeroizing::new(material),
301 created_at: Instant::now(),
302 expires_at: None,
303 algorithm,
304 }
305 }
306
307 #[cfg(not(feature = "crypto"))]
309 pub fn from_material(key_id: u64, algorithm: EncryptionAlgorithm, material: Vec<u8>) -> Self {
310 Self {
311 key_id,
312 key_material: material,
313 created_at: Instant::now(),
314 expires_at: None,
315 algorithm,
316 }
317 }
318
319 pub(crate) fn material(&self) -> &[u8] {
321 &self.key_material
322 }
323
324 pub fn is_expired(&self) -> bool {
326 self.expires_at
327 .map(|exp| Instant::now() > exp)
328 .unwrap_or(false)
329 }
330
331 pub fn key_size(&self) -> usize {
333 self.key_material.len()
334 }
335}
336
337impl fmt::Debug for EncryptionKey {
338 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339 f.debug_struct("EncryptionKey")
340 .field("key_id", &self.key_id)
341 .field("algorithm", &self.algorithm)
342 .field("key_size", &self.key_material.len())
343 .field("created_at", &self.created_at)
344 .field("expires_at", &self.expires_at)
345 .finish()
346 }
347}
348
349#[derive(Debug, Clone)]
351pub struct EncryptedRegion {
352 pub region_id: u64,
354 pub ciphertext: Vec<u8>,
356 pub nonce: Vec<u8>,
358 pub key_id: u64,
360 pub plaintext_size: usize,
362 pub algorithm: EncryptionAlgorithm,
364 pub encrypted_at: Instant,
366}
367
368#[derive(Debug, Clone, Default)]
370pub struct EncryptionStats {
371 pub bytes_encrypted: u64,
373 pub bytes_decrypted: u64,
375 pub encrypt_ops: u64,
377 pub decrypt_ops: u64,
379 pub key_rotations: u64,
381 pub avg_encrypt_time_us: f64,
383 pub avg_decrypt_time_us: f64,
385}
386
387pub struct MemoryEncryption {
389 config: EncryptionConfig,
391 active_key: RwLock<EncryptionKey>,
393 previous_keys: RwLock<HashMap<u64, EncryptionKey>>,
395 next_key_id: AtomicU64,
397 region_counter: AtomicU64,
399 stats: RwLock<EncryptionStats>,
401 last_rotation: RwLock<Instant>,
403}
404
405impl MemoryEncryption {
406 #[allow(deprecated)]
408 pub fn new(config: EncryptionConfig) -> Self {
409 let key_id = 1;
410 let active_key = EncryptionKey::new(key_id, config.algorithm);
411
412 Self {
413 config,
414 active_key: RwLock::new(active_key),
415 previous_keys: RwLock::new(HashMap::new()),
416 next_key_id: AtomicU64::new(2),
417 region_counter: AtomicU64::new(1),
418 stats: RwLock::new(EncryptionStats::default()),
419 last_rotation: RwLock::new(Instant::now()),
420 }
421 }
422
423 #[cfg(feature = "crypto")]
428 pub fn encrypt_region(&self, plaintext: &[u8]) -> EncryptedRegion {
429 let start = Instant::now();
430
431 let key = self.active_key.read().expect("active_key lock poisoned");
432 let region_id = self.region_counter.fetch_add(1, Ordering::Relaxed);
433
434 let nonce_size = match self.config.algorithm {
436 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => 12,
437 EncryptionAlgorithm::ChaCha20Poly1305 => 12,
438 EncryptionAlgorithm::XChaCha20Poly1305 => 24,
439 };
440 let mut nonce = vec![0u8; nonce_size];
441 OsRng.fill_bytes(&mut nonce);
442
443 let aad = self.config.aad_prefix.as_deref().unwrap_or(&[]);
445
446 let ciphertext = match self.config.algorithm {
448 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => {
449 let cipher = Aes256Gcm::new_from_slice(key.material())
450 .expect("AES key length must be 32 bytes");
451 let aes_nonce = AesNonce::from_slice(&nonce);
452 cipher
453 .encrypt(aes_nonce, plaintext)
454 .expect("AES-GCM encryption should not fail with valid key and nonce")
455 }
456 EncryptionAlgorithm::ChaCha20Poly1305 => {
457 let cipher = ChaCha20Poly1305::new_from_slice(key.material())
458 .expect("ChaCha20 key length must be 32 bytes");
459 let cha_nonce = ChaNonce::from_slice(&nonce);
460 cipher
461 .encrypt(cha_nonce, plaintext)
462 .expect("ChaCha20-Poly1305 encryption should not fail with valid key and nonce")
463 }
464 EncryptionAlgorithm::XChaCha20Poly1305 => {
465 let cipher = XChaCha20Poly1305::new_from_slice(key.material())
466 .expect("XChaCha20 key length must be 32 bytes");
467 let x_nonce = XNonce::from_slice(&nonce);
468 cipher.encrypt(x_nonce, plaintext).expect(
469 "XChaCha20-Poly1305 encryption should not fail with valid key and nonce",
470 )
471 }
472 };
473
474 let elapsed = start.elapsed();
475
476 {
478 let mut stats = self.stats.write().expect("stats lock poisoned");
479 stats.bytes_encrypted += plaintext.len() as u64;
480 stats.encrypt_ops += 1;
481 let total_time = stats.avg_encrypt_time_us * (stats.encrypt_ops - 1) as f64;
482 stats.avg_encrypt_time_us =
483 (total_time + elapsed.as_micros() as f64) / stats.encrypt_ops as f64;
484 }
485
486 let _ = aad;
488
489 EncryptedRegion {
490 region_id,
491 ciphertext,
492 nonce,
493 key_id: key.key_id,
494 plaintext_size: plaintext.len(),
495 algorithm: self.config.algorithm,
496 encrypted_at: Instant::now(),
497 }
498 }
499
500 #[cfg(not(feature = "crypto"))]
505 #[deprecated(
506 since = "0.4.3",
507 note = "Using insecure XOR-based demo encryption. Enable the `crypto` feature for real AES-256-GCM."
508 )]
509 pub fn encrypt_region(&self, plaintext: &[u8]) -> EncryptedRegion {
510 let start = Instant::now();
511
512 let key = self.active_key.read().expect("active_key lock poisoned");
513 let region_id = self.region_counter.fetch_add(1, Ordering::Relaxed);
514
515 let nonce_size = match self.config.algorithm {
517 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => 12,
518 EncryptionAlgorithm::ChaCha20Poly1305 => 12,
519 EncryptionAlgorithm::XChaCha20Poly1305 => 24,
520 };
521 let nonce: Vec<u8> = (0..nonce_size)
522 .map(|i| ((region_id as u8).wrapping_add(i as u8)).wrapping_mul(23))
523 .collect();
524
525 let mut ciphertext = plaintext.to_vec();
528 for (i, byte) in ciphertext.iter_mut().enumerate() {
529 *byte ^= key.material()[i % key.material().len()];
530 *byte ^= nonce[i % nonce.len()];
531 }
532
533 let tag: Vec<u8> = (0..16)
535 .map(|i| {
536 ciphertext.get(i).copied().unwrap_or(0) ^ key.material()[i % key.material().len()]
537 })
538 .collect();
539 ciphertext.extend(tag);
540
541 let elapsed = start.elapsed();
542
543 {
545 let mut stats = self.stats.write().expect("stats lock poisoned");
546 stats.bytes_encrypted += plaintext.len() as u64;
547 stats.encrypt_ops += 1;
548 let total_time = stats.avg_encrypt_time_us * (stats.encrypt_ops - 1) as f64;
549 stats.avg_encrypt_time_us =
550 (total_time + elapsed.as_micros() as f64) / stats.encrypt_ops as f64;
551 }
552
553 EncryptedRegion {
554 region_id,
555 ciphertext,
556 nonce,
557 key_id: key.key_id,
558 plaintext_size: plaintext.len(),
559 algorithm: self.config.algorithm,
560 encrypted_at: Instant::now(),
561 }
562 }
563
564 #[cfg(feature = "crypto")]
566 pub fn decrypt_region(&self, region: &EncryptedRegion) -> Result<Vec<u8>, String> {
567 let start = Instant::now();
568
569 let key = if region.key_id
571 == self
572 .active_key
573 .read()
574 .expect("active_key lock poisoned")
575 .key_id
576 {
577 self.active_key
578 .read()
579 .expect("active_key lock poisoned")
580 .clone()
581 } else {
582 self.previous_keys
583 .read()
584 .expect("previous_keys lock poisoned")
585 .get(®ion.key_id)
586 .cloned()
587 .ok_or_else(|| format!("Key {} not found", region.key_id))?
588 };
589
590 let plaintext = match region.algorithm {
592 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => {
593 let cipher = Aes256Gcm::new_from_slice(key.material())
594 .map_err(|e| format!("Invalid AES key: {}", e))?;
595 let aes_nonce = AesNonce::from_slice(®ion.nonce);
596 cipher
597 .decrypt(aes_nonce, region.ciphertext.as_ref())
598 .map_err(|_| {
599 "AES-GCM decryption failed: authentication tag mismatch".to_string()
600 })?
601 }
602 EncryptionAlgorithm::ChaCha20Poly1305 => {
603 let cipher = ChaCha20Poly1305::new_from_slice(key.material())
604 .map_err(|e| format!("Invalid ChaCha20 key: {}", e))?;
605 let cha_nonce = ChaNonce::from_slice(®ion.nonce);
606 cipher
607 .decrypt(cha_nonce, region.ciphertext.as_ref())
608 .map_err(|_| {
609 "ChaCha20-Poly1305 decryption failed: authentication tag mismatch"
610 .to_string()
611 })?
612 }
613 EncryptionAlgorithm::XChaCha20Poly1305 => {
614 let cipher = XChaCha20Poly1305::new_from_slice(key.material())
615 .map_err(|e| format!("Invalid XChaCha20 key: {}", e))?;
616 let x_nonce = XNonce::from_slice(®ion.nonce);
617 cipher
618 .decrypt(x_nonce, region.ciphertext.as_ref())
619 .map_err(|_| {
620 "XChaCha20-Poly1305 decryption failed: authentication tag mismatch"
621 .to_string()
622 })?
623 }
624 };
625
626 let elapsed = start.elapsed();
627
628 {
630 let mut stats = self.stats.write().expect("stats lock poisoned");
631 stats.bytes_decrypted += plaintext.len() as u64;
632 stats.decrypt_ops += 1;
633 let total_time = stats.avg_decrypt_time_us * (stats.decrypt_ops - 1) as f64;
634 stats.avg_decrypt_time_us =
635 (total_time + elapsed.as_micros() as f64) / stats.decrypt_ops as f64;
636 }
637
638 Ok(plaintext)
639 }
640
641 #[cfg(not(feature = "crypto"))]
646 #[deprecated(
647 since = "0.4.3",
648 note = "Using insecure XOR-based demo decryption. Enable the `crypto` feature for real AES-256-GCM."
649 )]
650 pub fn decrypt_region(&self, region: &EncryptedRegion) -> Result<Vec<u8>, String> {
651 let start = Instant::now();
652
653 let key = if region.key_id
655 == self
656 .active_key
657 .read()
658 .expect("active_key lock poisoned")
659 .key_id
660 {
661 self.active_key
662 .read()
663 .expect("active_key lock poisoned")
664 .clone()
665 } else {
666 self.previous_keys
667 .read()
668 .expect("previous_keys lock poisoned")
669 .get(®ion.key_id)
670 .cloned()
671 .ok_or_else(|| format!("Key {} not found", region.key_id))?
672 };
673
674 if region.ciphertext.len() < 16 {
676 return Err("Ciphertext too short".to_string());
677 }
678 let (ciphertext, _tag) = region.ciphertext.split_at(region.ciphertext.len() - 16);
679
680 let mut plaintext = ciphertext.to_vec();
683 for (i, byte) in plaintext.iter_mut().enumerate() {
684 *byte ^= region.nonce[i % region.nonce.len()];
685 *byte ^= key.material()[i % key.material().len()];
686 }
687
688 let elapsed = start.elapsed();
689
690 {
692 let mut stats = self.stats.write().expect("stats lock poisoned");
693 stats.bytes_decrypted += plaintext.len() as u64;
694 stats.decrypt_ops += 1;
695 let total_time = stats.avg_decrypt_time_us * (stats.decrypt_ops - 1) as f64;
696 stats.avg_decrypt_time_us =
697 (total_time + elapsed.as_micros() as f64) / stats.decrypt_ops as f64;
698 }
699
700 Ok(plaintext)
701 }
702
703 #[allow(deprecated)]
705 pub fn rotate_keys(&self) {
706 let mut active = self.active_key.write().expect("active_key lock poisoned");
707 let mut previous = self
708 .previous_keys
709 .write()
710 .expect("previous_keys lock poisoned");
711
712 let old_key = active.clone();
714 previous.insert(old_key.key_id, old_key);
715
716 let new_key_id = self.next_key_id.fetch_add(1, Ordering::Relaxed);
718 *active = EncryptionKey::new(new_key_id, self.config.algorithm);
719
720 *self
722 .last_rotation
723 .write()
724 .expect("last_rotation lock poisoned") = Instant::now();
725
726 self.stats
728 .write()
729 .expect("stats lock poisoned")
730 .key_rotations += 1;
731
732 while previous.len() > 10 {
734 if let Some(oldest_id) = previous.keys().min().copied() {
735 previous.remove(&oldest_id);
736 }
737 }
738 }
739
740 pub fn needs_rotation(&self) -> bool {
742 let last = *self
743 .last_rotation
744 .read()
745 .expect("last_rotation lock poisoned");
746 last.elapsed() >= self.config.key_rotation_interval
747 }
748
749 pub fn stats(&self) -> EncryptionStats {
751 self.stats.read().expect("stats lock poisoned").clone()
752 }
753
754 pub fn current_key_id(&self) -> u64 {
756 self.active_key
757 .read()
758 .expect("active_key lock poisoned")
759 .key_id
760 }
761
762 pub fn config(&self) -> &EncryptionConfig {
764 &self.config
765 }
766}
767
768impl fmt::Debug for MemoryEncryption {
769 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770 f.debug_struct("MemoryEncryption")
771 .field("config", &self.config)
772 .field("current_key_id", &self.current_key_id())
773 .field("stats", &self.stats())
774 .finish()
775 }
776}
777
778#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
784pub enum AccessLevel {
785 Deny,
787 ReadOnly,
789 #[default]
791 ReadWrite,
792 Full,
794}
795
796#[derive(Debug, Clone)]
798pub struct ResourceLimits {
799 pub max_memory_bytes: u64,
801 pub max_execution_time: Duration,
803 pub max_messages_per_sec: u32,
805 pub max_k2k_connections: u32,
807 pub max_checkpoint_size: u64,
809 pub max_queue_depth: u32,
811}
812
813impl Default for ResourceLimits {
814 fn default() -> Self {
815 Self {
816 max_memory_bytes: 1024 * 1024 * 1024, max_execution_time: Duration::from_secs(60),
818 max_messages_per_sec: 10000,
819 max_k2k_connections: 100,
820 max_checkpoint_size: 100 * 1024 * 1024, max_queue_depth: 4096,
822 }
823 }
824}
825
826impl ResourceLimits {
827 pub fn new() -> Self {
829 Self::default()
830 }
831
832 pub fn with_max_memory(mut self, bytes: u64) -> Self {
834 self.max_memory_bytes = bytes;
835 self
836 }
837
838 pub fn with_max_execution_time(mut self, duration: Duration) -> Self {
840 self.max_execution_time = duration;
841 self
842 }
843
844 pub fn with_max_messages_per_sec(mut self, count: u32) -> Self {
846 self.max_messages_per_sec = count;
847 self
848 }
849
850 pub fn with_max_k2k_connections(mut self, count: u32) -> Self {
852 self.max_k2k_connections = count;
853 self
854 }
855
856 pub fn restrictive() -> Self {
858 Self {
859 max_memory_bytes: 256 * 1024 * 1024, max_execution_time: Duration::from_secs(10),
861 max_messages_per_sec: 1000,
862 max_k2k_connections: 10,
863 max_checkpoint_size: 10 * 1024 * 1024, max_queue_depth: 256,
865 }
866 }
867
868 pub fn permissive() -> Self {
870 Self {
871 max_memory_bytes: 8 * 1024 * 1024 * 1024, max_execution_time: Duration::from_secs(3600),
873 max_messages_per_sec: 1_000_000,
874 max_k2k_connections: 1000,
875 max_checkpoint_size: 1024 * 1024 * 1024, max_queue_depth: 65536,
877 }
878 }
879}
880
881#[derive(Debug, Clone)]
883pub struct SandboxPolicy {
884 pub limits: ResourceLimits,
886 pub allowed_k2k_destinations: HashSet<String>,
888 pub denied_k2k_destinations: HashSet<String>,
890 pub memory_access: HashMap<String, AccessLevel>,
892 pub can_checkpoint: bool,
894 pub can_migrate: bool,
896 pub can_spawn: bool,
898 pub can_access_host: bool,
900 pub allowed_syscalls: HashSet<String>,
902}
903
904impl Default for SandboxPolicy {
905 fn default() -> Self {
906 Self {
907 limits: ResourceLimits::default(),
908 allowed_k2k_destinations: HashSet::new(),
909 denied_k2k_destinations: HashSet::new(),
910 memory_access: HashMap::new(),
911 can_checkpoint: true,
912 can_migrate: true,
913 can_spawn: false,
914 can_access_host: false,
915 allowed_syscalls: HashSet::new(),
916 }
917 }
918}
919
920impl SandboxPolicy {
921 pub fn new() -> Self {
923 Self::default()
924 }
925
926 pub fn with_limits(mut self, limits: ResourceLimits) -> Self {
928 self.limits = limits;
929 self
930 }
931
932 pub fn with_memory_limit(mut self, bytes: u64) -> Self {
934 self.limits.max_memory_bytes = bytes;
935 self
936 }
937
938 pub fn with_execution_timeout(mut self, timeout: Duration) -> Self {
940 self.limits.max_execution_time = timeout;
941 self
942 }
943
944 pub fn allow_k2k_to(mut self, destinations: &[&str]) -> Self {
946 self.allowed_k2k_destinations
947 .extend(destinations.iter().map(|s| s.to_string()));
948 self
949 }
950
951 pub fn deny_k2k_to(mut self, destinations: &[&str]) -> Self {
953 self.denied_k2k_destinations
954 .extend(destinations.iter().map(|s| s.to_string()));
955 self
956 }
957
958 pub fn with_memory_access(mut self, region: &str, access: AccessLevel) -> Self {
960 self.memory_access.insert(region.to_string(), access);
961 self
962 }
963
964 pub fn with_checkpoint(mut self, enabled: bool) -> Self {
966 self.can_checkpoint = enabled;
967 self
968 }
969
970 pub fn with_migration(mut self, enabled: bool) -> Self {
972 self.can_migrate = enabled;
973 self
974 }
975
976 pub fn with_spawn(mut self, enabled: bool) -> Self {
978 self.can_spawn = enabled;
979 self
980 }
981
982 pub fn with_host_access(mut self, enabled: bool) -> Self {
984 self.can_access_host = enabled;
985 self
986 }
987
988 pub fn restrictive() -> Self {
990 Self {
991 limits: ResourceLimits::restrictive(),
992 allowed_k2k_destinations: HashSet::new(),
993 denied_k2k_destinations: HashSet::new(),
994 memory_access: HashMap::new(),
995 can_checkpoint: false,
996 can_migrate: false,
997 can_spawn: false,
998 can_access_host: false,
999 allowed_syscalls: HashSet::new(),
1000 }
1001 }
1002
1003 pub fn permissive() -> Self {
1005 Self {
1006 limits: ResourceLimits::permissive(),
1007 allowed_k2k_destinations: HashSet::new(),
1008 denied_k2k_destinations: HashSet::new(),
1009 memory_access: HashMap::new(),
1010 can_checkpoint: true,
1011 can_migrate: true,
1012 can_spawn: true,
1013 can_access_host: true,
1014 allowed_syscalls: HashSet::new(),
1015 }
1016 }
1017
1018 pub fn is_k2k_allowed(&self, destination: &str) -> bool {
1020 if self.denied_k2k_destinations.contains(destination) {
1022 return false;
1023 }
1024 if self.allowed_k2k_destinations.is_empty() {
1026 return true;
1027 }
1028 self.allowed_k2k_destinations.contains(destination)
1030 }
1031}
1032
1033#[derive(Debug, Clone, PartialEq, Eq)]
1035pub enum ViolationType {
1036 MemoryLimitExceeded {
1038 used: u64,
1040 limit: u64,
1042 },
1043 ExecutionTimeExceeded {
1045 elapsed: Duration,
1047 limit: Duration,
1049 },
1050 MessageRateExceeded {
1052 rate: u32,
1054 limit: u32,
1056 },
1057 UnauthorizedK2K {
1059 destination: String,
1061 },
1062 UnauthorizedMemoryAccess {
1064 region: String,
1066 requested: AccessLevel,
1068 },
1069 CheckpointNotAllowed,
1071 MigrationNotAllowed,
1073 SpawnNotAllowed,
1075 HostAccessNotAllowed,
1077}
1078
1079impl fmt::Display for ViolationType {
1080 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1081 match self {
1082 Self::MemoryLimitExceeded { used, limit } => {
1083 write!(f, "Memory limit exceeded: {} > {} bytes", used, limit)
1084 }
1085 Self::ExecutionTimeExceeded { elapsed, limit } => {
1086 write!(f, "Execution time exceeded: {:?} > {:?}", elapsed, limit)
1087 }
1088 Self::MessageRateExceeded { rate, limit } => {
1089 write!(f, "Message rate exceeded: {} > {} msg/s", rate, limit)
1090 }
1091 Self::UnauthorizedK2K { destination } => {
1092 write!(f, "Unauthorized K2K to: {}", destination)
1093 }
1094 Self::UnauthorizedMemoryAccess { region, requested } => {
1095 write!(
1096 f,
1097 "Unauthorized {:?} access to region: {}",
1098 requested, region
1099 )
1100 }
1101 Self::CheckpointNotAllowed => write!(f, "Checkpointing not allowed"),
1102 Self::MigrationNotAllowed => write!(f, "Migration not allowed"),
1103 Self::SpawnNotAllowed => write!(f, "Spawning not allowed"),
1104 Self::HostAccessNotAllowed => write!(f, "Host memory access not allowed"),
1105 }
1106 }
1107}
1108
1109#[derive(Debug, Clone)]
1111pub struct SandboxViolation {
1112 pub violation_type: ViolationType,
1114 pub kernel_id: KernelId,
1116 pub timestamp: Instant,
1118 pub context: Option<String>,
1120}
1121
1122#[derive(Debug, Clone, Default)]
1124pub struct SandboxStats {
1125 pub total_checks: u64,
1127 pub violations_detected: u64,
1129 pub operations_blocked: u64,
1131 pub current_memory_usage: u64,
1133 pub current_message_rate: u32,
1135}
1136
1137pub struct KernelSandbox {
1139 policy: SandboxPolicy,
1141 kernel_id: Option<KernelId>,
1143 stats: RwLock<SandboxStats>,
1145 violations: RwLock<Vec<SandboxViolation>>,
1147 start_time: RwLock<Option<Instant>>,
1149 message_count: AtomicU64,
1151 last_rate_check: RwLock<Instant>,
1153}
1154
1155impl KernelSandbox {
1156 pub fn new(policy: SandboxPolicy) -> Self {
1158 Self {
1159 policy,
1160 kernel_id: None,
1161 stats: RwLock::new(SandboxStats::default()),
1162 violations: RwLock::new(Vec::new()),
1163 start_time: RwLock::new(None),
1164 message_count: AtomicU64::new(0),
1165 last_rate_check: RwLock::new(Instant::now()),
1166 }
1167 }
1168
1169 pub fn apply_to_kernel(&mut self, kernel_id: KernelId) {
1171 self.kernel_id = Some(kernel_id);
1172 *self.start_time.write().expect("start_time lock poisoned") = Some(Instant::now());
1173 }
1174
1175 pub fn check_memory(&self, bytes: u64) -> Result<(), SandboxViolation> {
1177 self.stats
1178 .write()
1179 .expect("stats lock poisoned")
1180 .total_checks += 1;
1181
1182 if bytes > self.policy.limits.max_memory_bytes {
1183 let violation = SandboxViolation {
1184 violation_type: ViolationType::MemoryLimitExceeded {
1185 used: bytes,
1186 limit: self.policy.limits.max_memory_bytes,
1187 },
1188 kernel_id: self
1189 .kernel_id
1190 .clone()
1191 .unwrap_or_else(|| KernelId("unknown".to_string())),
1192 timestamp: Instant::now(),
1193 context: None,
1194 };
1195 self.record_violation(violation.clone());
1196 return Err(violation);
1197 }
1198
1199 self.stats
1200 .write()
1201 .expect("stats lock poisoned")
1202 .current_memory_usage = bytes;
1203 Ok(())
1204 }
1205
1206 pub fn check_execution_time(&self) -> Result<(), SandboxViolation> {
1208 self.stats
1209 .write()
1210 .expect("stats lock poisoned")
1211 .total_checks += 1;
1212
1213 if let Some(start) = *self.start_time.read().expect("start_time lock poisoned") {
1214 let elapsed = start.elapsed();
1215 if elapsed > self.policy.limits.max_execution_time {
1216 let violation = SandboxViolation {
1217 violation_type: ViolationType::ExecutionTimeExceeded {
1218 elapsed,
1219 limit: self.policy.limits.max_execution_time,
1220 },
1221 kernel_id: self
1222 .kernel_id
1223 .clone()
1224 .unwrap_or_else(|| KernelId("unknown".to_string())),
1225 timestamp: Instant::now(),
1226 context: None,
1227 };
1228 self.record_violation(violation.clone());
1229 return Err(violation);
1230 }
1231 }
1232 Ok(())
1233 }
1234
1235 pub fn check_k2k(&self, destination: &str) -> Result<(), SandboxViolation> {
1237 self.stats
1238 .write()
1239 .expect("stats lock poisoned")
1240 .total_checks += 1;
1241
1242 if !self.policy.is_k2k_allowed(destination) {
1243 let violation = SandboxViolation {
1244 violation_type: ViolationType::UnauthorizedK2K {
1245 destination: destination.to_string(),
1246 },
1247 kernel_id: self
1248 .kernel_id
1249 .clone()
1250 .unwrap_or_else(|| KernelId("unknown".to_string())),
1251 timestamp: Instant::now(),
1252 context: None,
1253 };
1254 self.record_violation(violation.clone());
1255 return Err(violation);
1256 }
1257 Ok(())
1258 }
1259
1260 pub fn check_checkpoint(&self) -> Result<(), SandboxViolation> {
1262 self.stats
1263 .write()
1264 .expect("stats lock poisoned")
1265 .total_checks += 1;
1266
1267 if !self.policy.can_checkpoint {
1268 let violation = SandboxViolation {
1269 violation_type: ViolationType::CheckpointNotAllowed,
1270 kernel_id: self
1271 .kernel_id
1272 .clone()
1273 .unwrap_or_else(|| KernelId("unknown".to_string())),
1274 timestamp: Instant::now(),
1275 context: None,
1276 };
1277 self.record_violation(violation.clone());
1278 return Err(violation);
1279 }
1280 Ok(())
1281 }
1282
1283 pub fn check_migration(&self) -> Result<(), SandboxViolation> {
1285 self.stats
1286 .write()
1287 .expect("stats lock poisoned")
1288 .total_checks += 1;
1289
1290 if !self.policy.can_migrate {
1291 let violation = SandboxViolation {
1292 violation_type: ViolationType::MigrationNotAllowed,
1293 kernel_id: self
1294 .kernel_id
1295 .clone()
1296 .unwrap_or_else(|| KernelId("unknown".to_string())),
1297 timestamp: Instant::now(),
1298 context: None,
1299 };
1300 self.record_violation(violation.clone());
1301 return Err(violation);
1302 }
1303 Ok(())
1304 }
1305
1306 pub fn record_message(&self) -> Result<(), SandboxViolation> {
1308 self.message_count.fetch_add(1, Ordering::Relaxed);
1309
1310 let mut last_check = self
1312 .last_rate_check
1313 .write()
1314 .expect("last_rate_check lock poisoned");
1315 if last_check.elapsed() >= Duration::from_secs(1) {
1316 let count = self.message_count.swap(0, Ordering::Relaxed) as u32;
1317 *last_check = Instant::now();
1318
1319 self.stats
1320 .write()
1321 .expect("stats lock poisoned")
1322 .current_message_rate = count;
1323
1324 if count > self.policy.limits.max_messages_per_sec {
1325 let violation = SandboxViolation {
1326 violation_type: ViolationType::MessageRateExceeded {
1327 rate: count,
1328 limit: self.policy.limits.max_messages_per_sec,
1329 },
1330 kernel_id: self
1331 .kernel_id
1332 .clone()
1333 .unwrap_or_else(|| KernelId("unknown".to_string())),
1334 timestamp: Instant::now(),
1335 context: None,
1336 };
1337 self.record_violation(violation.clone());
1338 return Err(violation);
1339 }
1340 }
1341 Ok(())
1342 }
1343
1344 fn record_violation(&self, violation: SandboxViolation) {
1346 let mut stats = self.stats.write().expect("stats lock poisoned");
1347 stats.violations_detected += 1;
1348 stats.operations_blocked += 1;
1349
1350 self.violations
1351 .write()
1352 .expect("violations lock poisoned")
1353 .push(violation);
1354 }
1355
1356 pub fn violations(&self) -> Vec<SandboxViolation> {
1358 self.violations
1359 .read()
1360 .expect("violations lock poisoned")
1361 .clone()
1362 }
1363
1364 pub fn stats(&self) -> SandboxStats {
1366 self.stats.read().expect("stats lock poisoned").clone()
1367 }
1368
1369 pub fn policy(&self) -> &SandboxPolicy {
1371 &self.policy
1372 }
1373
1374 pub fn reset(&self) {
1376 *self.stats.write().expect("stats lock poisoned") = SandboxStats::default();
1377 self.violations
1378 .write()
1379 .expect("violations lock poisoned")
1380 .clear();
1381 self.message_count.store(0, Ordering::Relaxed);
1382 }
1383}
1384
1385impl fmt::Debug for KernelSandbox {
1386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1387 f.debug_struct("KernelSandbox")
1388 .field("policy", &self.policy)
1389 .field("kernel_id", &self.kernel_id)
1390 .field("stats", &self.stats())
1391 .field(
1392 "violations_count",
1393 &self.violations.read().map(|v| v.len()).unwrap_or(0),
1394 )
1395 .finish()
1396 }
1397}
1398
1399#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1405pub enum ComplianceStandard {
1406 SOC2,
1408 GDPR,
1410 HIPAA,
1412 PCIDSS,
1414 ISO27001,
1416 FedRAMP,
1418 NIST,
1420}
1421
1422impl fmt::Display for ComplianceStandard {
1423 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1424 match self {
1425 Self::SOC2 => write!(f, "SOC 2 Type II"),
1426 Self::GDPR => write!(f, "GDPR"),
1427 Self::HIPAA => write!(f, "HIPAA"),
1428 Self::PCIDSS => write!(f, "PCI DSS"),
1429 Self::ISO27001 => write!(f, "ISO 27001"),
1430 Self::FedRAMP => write!(f, "FedRAMP"),
1431 Self::NIST => write!(f, "NIST CSF"),
1432 }
1433 }
1434}
1435
1436#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1438pub enum ReportFormat {
1439 #[default]
1441 Json,
1442 Html,
1444 Markdown,
1446 Pdf,
1448 Csv,
1450}
1451
1452#[derive(Debug, Clone)]
1454pub enum ComplianceStatus {
1455 Compliant,
1457 PartiallyCompliant {
1459 notes: Vec<String>,
1461 },
1462 NonCompliant {
1464 reasons: Vec<String>,
1466 },
1467 NotApplicable,
1469}
1470
1471impl ComplianceStatus {
1472 pub fn is_compliant(&self) -> bool {
1474 matches!(self, Self::Compliant | Self::NotApplicable)
1475 }
1476}
1477
1478#[derive(Debug, Clone)]
1480pub struct ComplianceCheck {
1481 pub id: String,
1483 pub name: String,
1485 pub standard: ComplianceStandard,
1487 pub description: String,
1489 pub status: ComplianceStatus,
1491 pub evidence: Vec<String>,
1493 pub recommendations: Vec<String>,
1495 pub checked_at: SystemTime,
1497}
1498
1499#[derive(Debug, Clone)]
1501pub struct ComplianceSummary {
1502 pub total_checks: usize,
1504 pub compliant: usize,
1506 pub partially_compliant: usize,
1508 pub non_compliant: usize,
1510 pub not_applicable: usize,
1512 pub compliance_percentage: f64,
1514}
1515
1516#[derive(Debug, Clone)]
1518pub struct ComplianceReport {
1519 pub id: String,
1521 pub title: String,
1523 pub standards: Vec<ComplianceStandard>,
1525 pub checks: Vec<ComplianceCheck>,
1527 pub summary: ComplianceSummary,
1529 pub generated_at: SystemTime,
1531 pub period_start: SystemTime,
1533 pub period_end: SystemTime,
1535 pub metadata: HashMap<String, String>,
1537}
1538
1539impl ComplianceReport {
1540 pub fn export(&self, format: ReportFormat) -> String {
1542 match format {
1543 ReportFormat::Json => self.to_json(),
1544 ReportFormat::Html => self.to_html(),
1545 ReportFormat::Markdown => self.to_markdown(),
1546 ReportFormat::Pdf => self.to_markdown(), ReportFormat::Csv => self.to_csv(),
1548 }
1549 }
1550
1551 fn to_json(&self) -> String {
1552 let mut json = String::new();
1553 json.push_str("{\n");
1554 json.push_str(&format!(" \"id\": \"{}\",\n", self.id));
1555 json.push_str(&format!(" \"title\": \"{}\",\n", self.title));
1556 json.push_str(&format!(
1557 " \"standards\": [{}],\n",
1558 self.standards
1559 .iter()
1560 .map(|s| format!("\"{}\"", s))
1561 .collect::<Vec<_>>()
1562 .join(", ")
1563 ));
1564 json.push_str(" \"summary\": {\n");
1565 json.push_str(&format!(
1566 " \"total_checks\": {},\n",
1567 self.summary.total_checks
1568 ));
1569 json.push_str(&format!(" \"compliant\": {},\n", self.summary.compliant));
1570 json.push_str(&format!(
1571 " \"partially_compliant\": {},\n",
1572 self.summary.partially_compliant
1573 ));
1574 json.push_str(&format!(
1575 " \"non_compliant\": {},\n",
1576 self.summary.non_compliant
1577 ));
1578 json.push_str(&format!(
1579 " \"compliance_percentage\": {:.1}\n",
1580 self.summary.compliance_percentage
1581 ));
1582 json.push_str(" },\n");
1583 json.push_str(&format!(" \"checks_count\": {}\n", self.checks.len()));
1584 json.push_str("}\n");
1585 json
1586 }
1587
1588 fn to_html(&self) -> String {
1589 let mut html = String::new();
1590 html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
1591 html.push_str(&format!("<title>{}</title>\n", self.title));
1592 html.push_str("<style>body { font-family: sans-serif; } .compliant { color: green; } .non-compliant { color: red; }</style>\n");
1593 html.push_str("</head>\n<body>\n");
1594 html.push_str(&format!("<h1>{}</h1>\n", self.title));
1595 html.push_str(&format!("<p>Report ID: {}</p>\n", self.id));
1596 html.push_str("<h2>Summary</h2>\n");
1597 html.push_str("<table>\n");
1598 html.push_str(&format!(
1599 "<tr><td>Total Checks</td><td>{}</td></tr>\n",
1600 self.summary.total_checks
1601 ));
1602 html.push_str(&format!(
1603 "<tr><td>Compliant</td><td class=\"compliant\">{}</td></tr>\n",
1604 self.summary.compliant
1605 ));
1606 html.push_str(&format!(
1607 "<tr><td>Non-Compliant</td><td class=\"non-compliant\">{}</td></tr>\n",
1608 self.summary.non_compliant
1609 ));
1610 html.push_str(&format!(
1611 "<tr><td>Compliance</td><td>{:.1}%</td></tr>\n",
1612 self.summary.compliance_percentage
1613 ));
1614 html.push_str("</table>\n");
1615 html.push_str("<h2>Checks</h2>\n");
1616 for check in &self.checks {
1617 html.push_str(&format!("<h3>{}</h3>\n", check.name));
1618 html.push_str(&format!("<p>{}</p>\n", check.description));
1619 }
1620 html.push_str("</body>\n</html>\n");
1621 html
1622 }
1623
1624 fn to_markdown(&self) -> String {
1625 let mut md = String::new();
1626 md.push_str(&format!("# {}\n\n", self.title));
1627 md.push_str(&format!("**Report ID:** {}\n\n", self.id));
1628 md.push_str("## Summary\n\n");
1629 md.push_str("| Metric | Value |\n");
1630 md.push_str("|--------|-------|\n");
1631 md.push_str(&format!(
1632 "| Total Checks | {} |\n",
1633 self.summary.total_checks
1634 ));
1635 md.push_str(&format!("| Compliant | {} |\n", self.summary.compliant));
1636 md.push_str(&format!(
1637 "| Partially Compliant | {} |\n",
1638 self.summary.partially_compliant
1639 ));
1640 md.push_str(&format!(
1641 "| Non-Compliant | {} |\n",
1642 self.summary.non_compliant
1643 ));
1644 md.push_str(&format!(
1645 "| Compliance | {:.1}% |\n",
1646 self.summary.compliance_percentage
1647 ));
1648 md.push_str("\n## Detailed Checks\n\n");
1649 for check in &self.checks {
1650 let status_icon = match &check.status {
1651 ComplianceStatus::Compliant => "✅",
1652 ComplianceStatus::PartiallyCompliant { .. } => "⚠️",
1653 ComplianceStatus::NonCompliant { .. } => "❌",
1654 ComplianceStatus::NotApplicable => "➖",
1655 };
1656 md.push_str(&format!("### {} {}\n\n", status_icon, check.name));
1657 md.push_str(&format!("{}\n\n", check.description));
1658 }
1659 md
1660 }
1661
1662 fn to_csv(&self) -> String {
1663 let mut csv = String::new();
1664 csv.push_str("ID,Name,Standard,Status,Description\n");
1665 for check in &self.checks {
1666 let status = match &check.status {
1667 ComplianceStatus::Compliant => "Compliant",
1668 ComplianceStatus::PartiallyCompliant { .. } => "Partially Compliant",
1669 ComplianceStatus::NonCompliant { .. } => "Non-Compliant",
1670 ComplianceStatus::NotApplicable => "N/A",
1671 };
1672 csv.push_str(&format!(
1673 "\"{}\",\"{}\",\"{}\",\"{}\",\"{}\"\n",
1674 check.id, check.name, check.standard, status, check.description
1675 ));
1676 }
1677 csv
1678 }
1679}
1680
1681pub struct ComplianceReporter {
1683 standards: HashSet<ComplianceStandard>,
1685 organization: String,
1687 metadata: HashMap<String, String>,
1689 custom_checks: Vec<Box<dyn Fn() -> ComplianceCheck + Send + Sync>>,
1691}
1692
1693impl ComplianceReporter {
1694 pub fn new() -> Self {
1696 Self {
1697 standards: HashSet::new(),
1698 organization: "Unknown".to_string(),
1699 metadata: HashMap::new(),
1700 custom_checks: Vec::new(),
1701 }
1702 }
1703
1704 pub fn with_standard(mut self, standard: ComplianceStandard) -> Self {
1706 self.standards.insert(standard);
1707 self
1708 }
1709
1710 pub fn with_organization(mut self, org: &str) -> Self {
1712 self.organization = org.to_string();
1713 self
1714 }
1715
1716 pub fn with_metadata(mut self, key: &str, value: &str) -> Self {
1718 self.metadata.insert(key.to_string(), value.to_string());
1719 self
1720 }
1721
1722 pub fn generate_report(&self, _format: ReportFormat) -> ComplianceReport {
1724 let mut checks = Vec::new();
1725 let now = SystemTime::now();
1726
1727 for standard in &self.standards {
1729 checks.extend(self.generate_standard_checks(*standard));
1730 }
1731
1732 for check_fn in &self.custom_checks {
1734 checks.push(check_fn());
1735 }
1736
1737 let total = checks.len();
1739 let compliant = checks
1740 .iter()
1741 .filter(|c| matches!(c.status, ComplianceStatus::Compliant))
1742 .count();
1743 let partial = checks
1744 .iter()
1745 .filter(|c| matches!(c.status, ComplianceStatus::PartiallyCompliant { .. }))
1746 .count();
1747 let non_compliant = checks
1748 .iter()
1749 .filter(|c| matches!(c.status, ComplianceStatus::NonCompliant { .. }))
1750 .count();
1751 let na = checks
1752 .iter()
1753 .filter(|c| matches!(c.status, ComplianceStatus::NotApplicable))
1754 .count();
1755
1756 let applicable = total - na;
1757 let compliance_pct = if applicable > 0 {
1758 ((compliant as f64 + partial as f64 * 0.5) / applicable as f64) * 100.0
1759 } else {
1760 100.0
1761 };
1762
1763 ComplianceReport {
1764 id: format!(
1765 "RPT-{}",
1766 now.duration_since(SystemTime::UNIX_EPOCH)
1767 .expect("system time should be after UNIX epoch")
1768 .as_secs()
1769 ),
1770 title: format!("{} Compliance Report", self.organization),
1771 standards: self.standards.iter().copied().collect(),
1772 checks,
1773 summary: ComplianceSummary {
1774 total_checks: total,
1775 compliant,
1776 partially_compliant: partial,
1777 non_compliant,
1778 not_applicable: na,
1779 compliance_percentage: compliance_pct,
1780 },
1781 generated_at: now,
1782 period_start: now - Duration::from_secs(30 * 24 * 60 * 60), period_end: now,
1784 metadata: self.metadata.clone(),
1785 }
1786 }
1787
1788 fn generate_standard_checks(&self, standard: ComplianceStandard) -> Vec<ComplianceCheck> {
1789 let now = SystemTime::now();
1790
1791 match standard {
1792 ComplianceStandard::SOC2 => vec![
1793 ComplianceCheck {
1794 id: "SOC2-CC1.1".to_string(),
1795 name: "Control Environment".to_string(),
1796 standard,
1797 description:
1798 "The entity demonstrates commitment to integrity and ethical values."
1799 .to_string(),
1800 status: ComplianceStatus::Compliant,
1801 evidence: vec![
1802 "Audit logging enabled".to_string(),
1803 "Access controls implemented".to_string(),
1804 ],
1805 recommendations: vec![],
1806 checked_at: now,
1807 },
1808 ComplianceCheck {
1809 id: "SOC2-CC6.1".to_string(),
1810 name: "Logical Access Controls".to_string(),
1811 standard,
1812 description:
1813 "Logical access security software, infrastructure, and architectures."
1814 .to_string(),
1815 status: ComplianceStatus::Compliant,
1816 evidence: vec![
1817 "Kernel sandboxing available".to_string(),
1818 "Memory encryption available".to_string(),
1819 ],
1820 recommendations: vec![],
1821 checked_at: now,
1822 },
1823 ComplianceCheck {
1824 id: "SOC2-CC7.2".to_string(),
1825 name: "System Monitoring".to_string(),
1826 standard,
1827 description: "System components are monitored and anomalies are identified."
1828 .to_string(),
1829 status: ComplianceStatus::Compliant,
1830 evidence: vec![
1831 "Health monitoring enabled".to_string(),
1832 "GPU memory dashboard available".to_string(),
1833 ],
1834 recommendations: vec![],
1835 checked_at: now,
1836 },
1837 ],
1838 ComplianceStandard::GDPR => vec![
1839 ComplianceCheck {
1840 id: "GDPR-32".to_string(),
1841 name: "Security of Processing".to_string(),
1842 standard,
1843 description: "Implement appropriate technical and organizational measures."
1844 .to_string(),
1845 status: ComplianceStatus::Compliant,
1846 evidence: vec!["Memory encryption available".to_string()],
1847 recommendations: vec!["Consider enabling encryption by default".to_string()],
1848 checked_at: now,
1849 },
1850 ComplianceCheck {
1851 id: "GDPR-33".to_string(),
1852 name: "Breach Notification".to_string(),
1853 standard,
1854 description: "Notify supervisory authority of personal data breach."
1855 .to_string(),
1856 status: ComplianceStatus::PartiallyCompliant {
1857 notes: vec!["Audit logging available but breach detection not automated"
1858 .to_string()],
1859 },
1860 evidence: vec!["Audit logging enabled".to_string()],
1861 recommendations: vec!["Add automated breach detection".to_string()],
1862 checked_at: now,
1863 },
1864 ],
1865 ComplianceStandard::HIPAA => vec![
1866 ComplianceCheck {
1867 id: "HIPAA-164.312(a)".to_string(),
1868 name: "Access Control".to_string(),
1869 standard,
1870 description: "Implement technical policies for electronic PHI access."
1871 .to_string(),
1872 status: ComplianceStatus::Compliant,
1873 evidence: vec![
1874 "Kernel sandboxing available".to_string(),
1875 "Access levels configurable".to_string(),
1876 ],
1877 recommendations: vec![],
1878 checked_at: now,
1879 },
1880 ComplianceCheck {
1881 id: "HIPAA-164.312(e)".to_string(),
1882 name: "Transmission Security".to_string(),
1883 standard,
1884 description: "Implement security measures for ePHI transmission.".to_string(),
1885 status: ComplianceStatus::Compliant,
1886 evidence: vec!["Memory encryption for data at rest".to_string()],
1887 recommendations: vec!["Implement TLS for network K2K".to_string()],
1888 checked_at: now,
1889 },
1890 ],
1891 ComplianceStandard::PCIDSS => vec![
1892 ComplianceCheck {
1893 id: "PCI-3.4".to_string(),
1894 name: "Render PAN Unreadable".to_string(),
1895 standard,
1896 description: "Render PAN unreadable anywhere it is stored.".to_string(),
1897 status: ComplianceStatus::Compliant,
1898 evidence: vec!["AES-256-GCM encryption available".to_string()],
1899 recommendations: vec![],
1900 checked_at: now,
1901 },
1902 ComplianceCheck {
1903 id: "PCI-10.1".to_string(),
1904 name: "Audit Trails".to_string(),
1905 standard,
1906 description: "Implement audit trails to link access to individual users."
1907 .to_string(),
1908 status: ComplianceStatus::Compliant,
1909 evidence: vec!["Comprehensive audit logging".to_string()],
1910 recommendations: vec![],
1911 checked_at: now,
1912 },
1913 ],
1914 ComplianceStandard::ISO27001 => vec![ComplianceCheck {
1915 id: "ISO-A.10.1".to_string(),
1916 name: "Cryptographic Controls".to_string(),
1917 standard,
1918 description: "Policy on use of cryptographic controls.".to_string(),
1919 status: ComplianceStatus::Compliant,
1920 evidence: vec!["Multiple encryption algorithms supported".to_string()],
1921 recommendations: vec![],
1922 checked_at: now,
1923 }],
1924 ComplianceStandard::FedRAMP => vec![ComplianceCheck {
1925 id: "FedRAMP-SC-28".to_string(),
1926 name: "Protection of Information at Rest".to_string(),
1927 standard,
1928 description: "Protect confidentiality and integrity of information at rest."
1929 .to_string(),
1930 status: ComplianceStatus::Compliant,
1931 evidence: vec!["FIPS-compliant algorithms available".to_string()],
1932 recommendations: vec![],
1933 checked_at: now,
1934 }],
1935 ComplianceStandard::NIST => vec![
1936 ComplianceCheck {
1937 id: "NIST-PR.DS-1".to_string(),
1938 name: "Data-at-rest Protection".to_string(),
1939 standard,
1940 description: "Data-at-rest is protected.".to_string(),
1941 status: ComplianceStatus::Compliant,
1942 evidence: vec!["Memory encryption module".to_string()],
1943 recommendations: vec![],
1944 checked_at: now,
1945 },
1946 ComplianceCheck {
1947 id: "NIST-DE.CM-1".to_string(),
1948 name: "Network Monitoring".to_string(),
1949 standard,
1950 description: "The network is monitored to detect cybersecurity events."
1951 .to_string(),
1952 status: ComplianceStatus::Compliant,
1953 evidence: vec![
1954 "Observability context".to_string(),
1955 "GPU profiler integration".to_string(),
1956 ],
1957 recommendations: vec![],
1958 checked_at: now,
1959 },
1960 ],
1961 }
1962 }
1963}
1964
1965impl Default for ComplianceReporter {
1966 fn default() -> Self {
1967 Self::new()
1968 }
1969}
1970
1971impl fmt::Debug for ComplianceReporter {
1972 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1973 f.debug_struct("ComplianceReporter")
1974 .field("standards", &self.standards)
1975 .field("organization", &self.organization)
1976 .field("metadata", &self.metadata)
1977 .field("custom_checks_count", &self.custom_checks.len())
1978 .finish()
1979 }
1980}
1981
1982#[cfg(test)]
1987mod tests {
1988 use super::*;
1989
1990 #[test]
1993 fn test_encryption_config_builder() {
1994 let config = EncryptionConfig::new()
1995 .with_algorithm(EncryptionAlgorithm::ChaCha20Poly1305)
1996 .with_key_rotation_interval(Duration::from_secs(7200))
1997 .with_control_block_encryption(false);
1998
1999 assert_eq!(config.algorithm, EncryptionAlgorithm::ChaCha20Poly1305);
2000 assert_eq!(config.key_rotation_interval, Duration::from_secs(7200));
2001 assert!(!config.encrypt_control_blocks);
2002 }
2003
2004 #[test]
2005 fn test_encryption_key_creation() {
2006 let key = EncryptionKey::new(1, EncryptionAlgorithm::Aes256Gcm);
2007 assert_eq!(key.key_id, 1);
2008 assert_eq!(key.key_size(), 32);
2009 assert!(!key.is_expired());
2010 }
2011
2012 #[test]
2013 fn test_encrypt_decrypt_roundtrip() {
2014 let encryption = MemoryEncryption::new(EncryptionConfig::default());
2015 let plaintext = b"Hello, GPU World!";
2016
2017 let encrypted = encryption.encrypt_region(plaintext);
2018 assert_ne!(encrypted.ciphertext[..plaintext.len()], plaintext[..]);
2019
2020 let decrypted = encryption.decrypt_region(&encrypted).unwrap();
2021 assert_eq!(decrypted, plaintext);
2022 }
2023
2024 #[test]
2025 fn test_key_rotation() {
2026 let encryption = MemoryEncryption::new(EncryptionConfig::default());
2027 let initial_key_id = encryption.current_key_id();
2028
2029 encryption.rotate_keys();
2030 assert_eq!(encryption.current_key_id(), initial_key_id + 1);
2031
2032 let stats = encryption.stats();
2034 assert_eq!(stats.key_rotations, 1);
2035 }
2036
2037 #[test]
2038 fn test_decrypt_with_old_key() {
2039 let encryption = MemoryEncryption::new(EncryptionConfig::default());
2040 let plaintext = b"Secret data";
2041
2042 let encrypted = encryption.encrypt_region(plaintext);
2043 let old_key_id = encrypted.key_id;
2044
2045 encryption.rotate_keys();
2047 assert_ne!(encryption.current_key_id(), old_key_id);
2048
2049 let decrypted = encryption.decrypt_region(&encrypted).unwrap();
2051 assert_eq!(decrypted, plaintext);
2052 }
2053
2054 #[test]
2055 fn test_encryption_stats() {
2056 let encryption = MemoryEncryption::new(EncryptionConfig::default());
2057 let data = vec![0u8; 1024];
2058
2059 for _ in 0..10 {
2060 let encrypted = encryption.encrypt_region(&data);
2061 let _ = encryption.decrypt_region(&encrypted);
2062 }
2063
2064 let stats = encryption.stats();
2065 assert_eq!(stats.encrypt_ops, 10);
2066 assert_eq!(stats.decrypt_ops, 10);
2067 assert_eq!(stats.bytes_encrypted, 10240);
2068 }
2069
2070 #[test]
2073 fn test_resource_limits_builder() {
2074 let limits = ResourceLimits::new()
2075 .with_max_memory(512 * 1024 * 1024)
2076 .with_max_execution_time(Duration::from_secs(30));
2077
2078 assert_eq!(limits.max_memory_bytes, 512 * 1024 * 1024);
2079 assert_eq!(limits.max_execution_time, Duration::from_secs(30));
2080 }
2081
2082 #[test]
2083 fn test_sandbox_policy_k2k() {
2084 let policy = SandboxPolicy::new()
2085 .allow_k2k_to(&["trusted_kernel", "another_trusted"])
2086 .deny_k2k_to(&["malicious_kernel"]);
2087
2088 assert!(policy.is_k2k_allowed("trusted_kernel"));
2089 assert!(policy.is_k2k_allowed("another_trusted"));
2090 assert!(!policy.is_k2k_allowed("malicious_kernel"));
2091 assert!(!policy.is_k2k_allowed("unknown_kernel")); }
2093
2094 #[test]
2095 fn test_sandbox_memory_check() {
2096 let policy = SandboxPolicy::new().with_memory_limit(1024);
2097 let sandbox = KernelSandbox::new(policy);
2098
2099 assert!(sandbox.check_memory(512).is_ok());
2101
2102 let result = sandbox.check_memory(2048);
2104 assert!(result.is_err());
2105
2106 if let Err(violation) = result {
2107 assert!(matches!(
2108 violation.violation_type,
2109 ViolationType::MemoryLimitExceeded { .. }
2110 ));
2111 }
2112 }
2113
2114 #[test]
2115 fn test_sandbox_k2k_check() {
2116 let policy = SandboxPolicy::new().deny_k2k_to(&["blocked"]);
2117 let sandbox = KernelSandbox::new(policy);
2118
2119 assert!(sandbox.check_k2k("allowed_dest").is_ok());
2120 assert!(sandbox.check_k2k("blocked").is_err());
2121 }
2122
2123 #[test]
2124 fn test_sandbox_checkpoint_check() {
2125 let policy = SandboxPolicy::restrictive();
2126 let sandbox = KernelSandbox::new(policy);
2127
2128 assert!(sandbox.check_checkpoint().is_err());
2129
2130 let permissive = SandboxPolicy::permissive();
2131 let sandbox2 = KernelSandbox::new(permissive);
2132 assert!(sandbox2.check_checkpoint().is_ok());
2133 }
2134
2135 #[test]
2136 fn test_sandbox_stats() {
2137 let policy = SandboxPolicy::new().with_memory_limit(1024);
2138 let sandbox = KernelSandbox::new(policy);
2139
2140 let _ = sandbox.check_memory(512);
2141 let _ = sandbox.check_memory(2048); let _ = sandbox.check_k2k("dest");
2143
2144 let stats = sandbox.stats();
2145 assert_eq!(stats.total_checks, 3);
2146 assert_eq!(stats.violations_detected, 1);
2147 }
2148
2149 #[test]
2150 fn test_sandbox_violations_list() {
2151 let policy = SandboxPolicy::restrictive();
2152 let sandbox = KernelSandbox::new(policy);
2153
2154 let _ = sandbox.check_checkpoint();
2155 let _ = sandbox.check_migration();
2156
2157 let violations = sandbox.violations();
2158 assert_eq!(violations.len(), 2);
2159 }
2160
2161 #[test]
2164 fn test_compliance_reporter_creation() {
2165 let reporter = ComplianceReporter::new()
2166 .with_standard(ComplianceStandard::SOC2)
2167 .with_standard(ComplianceStandard::GDPR)
2168 .with_organization("Test Org");
2169
2170 assert_eq!(reporter.standards.len(), 2);
2171 assert!(reporter.standards.contains(&ComplianceStandard::SOC2));
2172 assert!(reporter.standards.contains(&ComplianceStandard::GDPR));
2173 }
2174
2175 #[test]
2176 fn test_generate_soc2_report() {
2177 let reporter = ComplianceReporter::new()
2178 .with_standard(ComplianceStandard::SOC2)
2179 .with_organization("Acme Corp");
2180
2181 let report = reporter.generate_report(ReportFormat::Json);
2182
2183 assert!(!report.checks.is_empty());
2184 assert!(report.summary.total_checks > 0);
2185 assert!(report.title.contains("Acme Corp"));
2186 }
2187
2188 #[test]
2189 fn test_report_json_export() {
2190 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::HIPAA);
2191
2192 let report = reporter.generate_report(ReportFormat::Json);
2193 let json = report.export(ReportFormat::Json);
2194
2195 assert!(json.contains("\"id\""));
2196 assert!(json.contains("\"summary\""));
2197 }
2198
2199 #[test]
2200 fn test_report_markdown_export() {
2201 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::NIST);
2202
2203 let report = reporter.generate_report(ReportFormat::Markdown);
2204 let md = report.export(ReportFormat::Markdown);
2205
2206 assert!(md.contains("# "));
2207 assert!(md.contains("## Summary"));
2208 assert!(md.contains("| Metric | Value |"));
2209 }
2210
2211 #[test]
2212 fn test_report_html_export() {
2213 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::PCIDSS);
2214
2215 let report = reporter.generate_report(ReportFormat::Html);
2216 let html = report.export(ReportFormat::Html);
2217
2218 assert!(html.contains("<!DOCTYPE html>"));
2219 assert!(html.contains("<h1>"));
2220 }
2221
2222 #[test]
2223 fn test_report_csv_export() {
2224 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::ISO27001);
2225
2226 let report = reporter.generate_report(ReportFormat::Csv);
2227 let csv = report.export(ReportFormat::Csv);
2228
2229 assert!(csv.contains("ID,Name,Standard,Status,Description"));
2230 }
2231
2232 #[test]
2233 fn test_compliance_summary_calculation() {
2234 let reporter = ComplianceReporter::new()
2235 .with_standard(ComplianceStandard::SOC2)
2236 .with_standard(ComplianceStandard::GDPR)
2237 .with_standard(ComplianceStandard::HIPAA);
2238
2239 let report = reporter.generate_report(ReportFormat::Json);
2240
2241 let sum = report.summary.compliant
2242 + report.summary.partially_compliant
2243 + report.summary.non_compliant
2244 + report.summary.not_applicable;
2245 assert_eq!(sum, report.summary.total_checks);
2246 }
2247
2248 #[test]
2249 fn test_compliance_status_is_compliant() {
2250 assert!(ComplianceStatus::Compliant.is_compliant());
2251 assert!(ComplianceStatus::NotApplicable.is_compliant());
2252 assert!(!ComplianceStatus::NonCompliant { reasons: vec![] }.is_compliant());
2253 assert!(!ComplianceStatus::PartiallyCompliant { notes: vec![] }.is_compliant());
2254 }
2255
2256 #[test]
2257 fn test_all_standards() {
2258 let reporter = ComplianceReporter::new()
2259 .with_standard(ComplianceStandard::SOC2)
2260 .with_standard(ComplianceStandard::GDPR)
2261 .with_standard(ComplianceStandard::HIPAA)
2262 .with_standard(ComplianceStandard::PCIDSS)
2263 .with_standard(ComplianceStandard::ISO27001)
2264 .with_standard(ComplianceStandard::FedRAMP)
2265 .with_standard(ComplianceStandard::NIST);
2266
2267 let report = reporter.generate_report(ReportFormat::Json);
2268 assert_eq!(report.standards.len(), 7);
2269 }
2270}