1use std::collections::{HashMap, HashSet};
58use std::fmt;
59use std::sync::atomic::{AtomicU64, Ordering};
60use std::sync::RwLock;
61use std::time::{Duration, Instant, SystemTime};
62
63use crate::KernelId;
64
65#[cfg(feature = "crypto")]
67use aes_gcm::{
68 aead::{Aead, KeyInit},
69 Aes256Gcm, Nonce as AesNonce,
70};
71#[cfg(feature = "crypto")]
72use chacha20poly1305::{ChaCha20Poly1305, Nonce as ChaNonce, XChaCha20Poly1305, XNonce};
73#[cfg(feature = "crypto")]
74use rand::{rngs::OsRng, RngCore};
75#[cfg(feature = "crypto")]
76use zeroize::Zeroize;
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
84pub enum EncryptionAlgorithm {
85 #[default]
87 Aes256Gcm,
88 Aes128Gcm,
90 ChaCha20Poly1305,
92 XChaCha20Poly1305,
94}
95
96impl fmt::Display for EncryptionAlgorithm {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 match self {
99 Self::Aes256Gcm => write!(f, "AES-256-GCM"),
100 Self::Aes128Gcm => write!(f, "AES-128-GCM"),
101 Self::ChaCha20Poly1305 => write!(f, "ChaCha20-Poly1305"),
102 Self::XChaCha20Poly1305 => write!(f, "XChaCha20-Poly1305"),
103 }
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
109pub enum KeyDerivation {
110 #[default]
112 HkdfSha256,
113 HkdfSha384,
115 Argon2id,
117 Pbkdf2Sha256,
119}
120
121#[derive(Debug, Clone)]
123pub struct EncryptionConfig {
124 pub algorithm: EncryptionAlgorithm,
126 pub key_derivation: KeyDerivation,
128 pub key_rotation_interval: Duration,
130 pub encrypt_control_blocks: bool,
132 pub encrypt_message_queues: bool,
134 pub encrypt_kernel_state: bool,
136 pub aad_prefix: Option<Vec<u8>>,
138}
139
140impl Default for EncryptionConfig {
141 fn default() -> Self {
142 Self {
143 algorithm: EncryptionAlgorithm::default(),
144 key_derivation: KeyDerivation::default(),
145 key_rotation_interval: Duration::from_secs(3600), encrypt_control_blocks: true,
147 encrypt_message_queues: true,
148 encrypt_kernel_state: true,
149 aad_prefix: None,
150 }
151 }
152}
153
154impl EncryptionConfig {
155 pub fn new() -> Self {
157 Self::default()
158 }
159
160 pub fn with_algorithm(mut self, algorithm: EncryptionAlgorithm) -> Self {
162 self.algorithm = algorithm;
163 self
164 }
165
166 pub fn with_key_derivation(mut self, kdf: KeyDerivation) -> Self {
168 self.key_derivation = kdf;
169 self
170 }
171
172 pub fn with_key_rotation_interval(mut self, interval: Duration) -> Self {
174 self.key_rotation_interval = interval;
175 self
176 }
177
178 pub fn with_control_block_encryption(mut self, enabled: bool) -> Self {
180 self.encrypt_control_blocks = enabled;
181 self
182 }
183
184 pub fn with_message_queue_encryption(mut self, enabled: bool) -> Self {
186 self.encrypt_message_queues = enabled;
187 self
188 }
189
190 pub fn with_kernel_state_encryption(mut self, enabled: bool) -> Self {
192 self.encrypt_kernel_state = enabled;
193 self
194 }
195
196 pub fn with_aad_prefix(mut self, prefix: Vec<u8>) -> Self {
198 self.aad_prefix = Some(prefix);
199 self
200 }
201}
202
203#[derive(Clone)]
208pub struct EncryptionKey {
209 pub key_id: u64,
211 #[cfg(feature = "crypto")]
213 key_material: zeroize::Zeroizing<Vec<u8>>,
214 #[cfg(not(feature = "crypto"))]
215 key_material: Vec<u8>,
216 pub created_at: Instant,
218 pub expires_at: Option<Instant>,
220 pub algorithm: EncryptionAlgorithm,
222}
223
224impl EncryptionKey {
225 #[cfg(feature = "crypto")]
230 pub fn new(key_id: u64, algorithm: EncryptionAlgorithm) -> Self {
231 let key_size = match algorithm {
232 EncryptionAlgorithm::Aes256Gcm
233 | EncryptionAlgorithm::ChaCha20Poly1305
234 | EncryptionAlgorithm::XChaCha20Poly1305 => 32,
235 EncryptionAlgorithm::Aes128Gcm => 16,
236 };
237
238 let mut key_material = vec![0u8; key_size];
239 OsRng.fill_bytes(&mut key_material);
240
241 Self {
242 key_id,
243 key_material: zeroize::Zeroizing::new(key_material),
244 created_at: Instant::now(),
245 expires_at: None,
246 algorithm,
247 }
248 }
249
250 #[cfg(not(feature = "crypto"))]
255 pub fn new(key_id: u64, algorithm: EncryptionAlgorithm) -> Self {
256 let key_size = match algorithm {
257 EncryptionAlgorithm::Aes256Gcm
258 | EncryptionAlgorithm::ChaCha20Poly1305
259 | EncryptionAlgorithm::XChaCha20Poly1305 => 32,
260 EncryptionAlgorithm::Aes128Gcm => 16,
261 };
262
263 let key_material: Vec<u8> = (0..key_size)
265 .map(|i| ((key_id as u8).wrapping_add(i as u8)).wrapping_mul(17))
266 .collect();
267
268 Self {
269 key_id,
270 key_material,
271 created_at: Instant::now(),
272 expires_at: None,
273 algorithm,
274 }
275 }
276
277 #[cfg(feature = "crypto")]
279 pub fn from_material(key_id: u64, algorithm: EncryptionAlgorithm, material: Vec<u8>) -> Self {
280 Self {
281 key_id,
282 key_material: zeroize::Zeroizing::new(material),
283 created_at: Instant::now(),
284 expires_at: None,
285 algorithm,
286 }
287 }
288
289 #[cfg(not(feature = "crypto"))]
291 pub fn from_material(key_id: u64, algorithm: EncryptionAlgorithm, material: Vec<u8>) -> Self {
292 Self {
293 key_id,
294 key_material: material,
295 created_at: Instant::now(),
296 expires_at: None,
297 algorithm,
298 }
299 }
300
301 pub(crate) fn material(&self) -> &[u8] {
303 &self.key_material
304 }
305
306 pub fn is_expired(&self) -> bool {
308 self.expires_at
309 .map(|exp| Instant::now() > exp)
310 .unwrap_or(false)
311 }
312
313 pub fn key_size(&self) -> usize {
315 self.key_material.len()
316 }
317}
318
319impl fmt::Debug for EncryptionKey {
320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
321 f.debug_struct("EncryptionKey")
322 .field("key_id", &self.key_id)
323 .field("algorithm", &self.algorithm)
324 .field("key_size", &self.key_material.len())
325 .field("created_at", &self.created_at)
326 .field("expires_at", &self.expires_at)
327 .finish()
328 }
329}
330
331#[derive(Debug, Clone)]
333pub struct EncryptedRegion {
334 pub region_id: u64,
336 pub ciphertext: Vec<u8>,
338 pub nonce: Vec<u8>,
340 pub key_id: u64,
342 pub plaintext_size: usize,
344 pub algorithm: EncryptionAlgorithm,
346 pub encrypted_at: Instant,
348}
349
350#[derive(Debug, Clone, Default)]
352pub struct EncryptionStats {
353 pub bytes_encrypted: u64,
355 pub bytes_decrypted: u64,
357 pub encrypt_ops: u64,
359 pub decrypt_ops: u64,
361 pub key_rotations: u64,
363 pub avg_encrypt_time_us: f64,
365 pub avg_decrypt_time_us: f64,
367}
368
369pub struct MemoryEncryption {
371 config: EncryptionConfig,
373 active_key: RwLock<EncryptionKey>,
375 previous_keys: RwLock<HashMap<u64, EncryptionKey>>,
377 next_key_id: AtomicU64,
379 region_counter: AtomicU64,
381 stats: RwLock<EncryptionStats>,
383 last_rotation: RwLock<Instant>,
385}
386
387impl MemoryEncryption {
388 pub fn new(config: EncryptionConfig) -> Self {
390 let key_id = 1;
391 let active_key = EncryptionKey::new(key_id, config.algorithm);
392
393 Self {
394 config,
395 active_key: RwLock::new(active_key),
396 previous_keys: RwLock::new(HashMap::new()),
397 next_key_id: AtomicU64::new(2),
398 region_counter: AtomicU64::new(1),
399 stats: RwLock::new(EncryptionStats::default()),
400 last_rotation: RwLock::new(Instant::now()),
401 }
402 }
403
404 #[cfg(feature = "crypto")]
409 pub fn encrypt_region(&self, plaintext: &[u8]) -> EncryptedRegion {
410 let start = Instant::now();
411
412 let key = self.active_key.read().unwrap();
413 let region_id = self.region_counter.fetch_add(1, Ordering::Relaxed);
414
415 let nonce_size = match self.config.algorithm {
417 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => 12,
418 EncryptionAlgorithm::ChaCha20Poly1305 => 12,
419 EncryptionAlgorithm::XChaCha20Poly1305 => 24,
420 };
421 let mut nonce = vec![0u8; nonce_size];
422 OsRng.fill_bytes(&mut nonce);
423
424 let aad = self.config.aad_prefix.as_deref().unwrap_or(&[]);
426
427 let ciphertext = match self.config.algorithm {
429 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => {
430 let cipher =
431 Aes256Gcm::new_from_slice(key.material()).expect("Invalid AES key length");
432 let aes_nonce = AesNonce::from_slice(&nonce);
433 cipher
434 .encrypt(aes_nonce, plaintext)
435 .expect("AES-GCM encryption failed")
436 }
437 EncryptionAlgorithm::ChaCha20Poly1305 => {
438 let cipher = ChaCha20Poly1305::new_from_slice(key.material())
439 .expect("Invalid ChaCha20 key length");
440 let cha_nonce = ChaNonce::from_slice(&nonce);
441 cipher
442 .encrypt(cha_nonce, plaintext)
443 .expect("ChaCha20-Poly1305 encryption failed")
444 }
445 EncryptionAlgorithm::XChaCha20Poly1305 => {
446 let cipher = XChaCha20Poly1305::new_from_slice(key.material())
447 .expect("Invalid XChaCha20 key length");
448 let x_nonce = XNonce::from_slice(&nonce);
449 cipher
450 .encrypt(x_nonce, plaintext)
451 .expect("XChaCha20-Poly1305 encryption failed")
452 }
453 };
454
455 let elapsed = start.elapsed();
456
457 {
459 let mut stats = self.stats.write().unwrap();
460 stats.bytes_encrypted += plaintext.len() as u64;
461 stats.encrypt_ops += 1;
462 let total_time = stats.avg_encrypt_time_us * (stats.encrypt_ops - 1) as f64;
463 stats.avg_encrypt_time_us =
464 (total_time + elapsed.as_micros() as f64) / stats.encrypt_ops as f64;
465 }
466
467 let _ = aad;
469
470 EncryptedRegion {
471 region_id,
472 ciphertext,
473 nonce,
474 key_id: key.key_id,
475 plaintext_size: plaintext.len(),
476 algorithm: self.config.algorithm,
477 encrypted_at: Instant::now(),
478 }
479 }
480
481 #[cfg(not(feature = "crypto"))]
486 pub fn encrypt_region(&self, plaintext: &[u8]) -> EncryptedRegion {
487 let start = Instant::now();
488
489 let key = self.active_key.read().unwrap();
490 let region_id = self.region_counter.fetch_add(1, Ordering::Relaxed);
491
492 let nonce_size = match self.config.algorithm {
494 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => 12,
495 EncryptionAlgorithm::ChaCha20Poly1305 => 12,
496 EncryptionAlgorithm::XChaCha20Poly1305 => 24,
497 };
498 let nonce: Vec<u8> = (0..nonce_size)
499 .map(|i| ((region_id as u8).wrapping_add(i as u8)).wrapping_mul(23))
500 .collect();
501
502 let mut ciphertext = plaintext.to_vec();
505 for (i, byte) in ciphertext.iter_mut().enumerate() {
506 *byte ^= key.material()[i % key.material().len()];
507 *byte ^= nonce[i % nonce.len()];
508 }
509
510 let tag: Vec<u8> = (0..16)
512 .map(|i| {
513 ciphertext.get(i).copied().unwrap_or(0) ^ key.material()[i % key.material().len()]
514 })
515 .collect();
516 ciphertext.extend(tag);
517
518 let elapsed = start.elapsed();
519
520 {
522 let mut stats = self.stats.write().unwrap();
523 stats.bytes_encrypted += plaintext.len() as u64;
524 stats.encrypt_ops += 1;
525 let total_time = stats.avg_encrypt_time_us * (stats.encrypt_ops - 1) as f64;
526 stats.avg_encrypt_time_us =
527 (total_time + elapsed.as_micros() as f64) / stats.encrypt_ops as f64;
528 }
529
530 EncryptedRegion {
531 region_id,
532 ciphertext,
533 nonce,
534 key_id: key.key_id,
535 plaintext_size: plaintext.len(),
536 algorithm: self.config.algorithm,
537 encrypted_at: Instant::now(),
538 }
539 }
540
541 #[cfg(feature = "crypto")]
543 pub fn decrypt_region(&self, region: &EncryptedRegion) -> Result<Vec<u8>, String> {
544 let start = Instant::now();
545
546 let key = if region.key_id == self.active_key.read().unwrap().key_id {
548 self.active_key.read().unwrap().clone()
549 } else {
550 self.previous_keys
551 .read()
552 .unwrap()
553 .get(®ion.key_id)
554 .cloned()
555 .ok_or_else(|| format!("Key {} not found", region.key_id))?
556 };
557
558 let plaintext = match region.algorithm {
560 EncryptionAlgorithm::Aes256Gcm | EncryptionAlgorithm::Aes128Gcm => {
561 let cipher = Aes256Gcm::new_from_slice(key.material())
562 .map_err(|e| format!("Invalid AES key: {}", e))?;
563 let aes_nonce = AesNonce::from_slice(®ion.nonce);
564 cipher
565 .decrypt(aes_nonce, region.ciphertext.as_ref())
566 .map_err(|_| {
567 "AES-GCM decryption failed: authentication tag mismatch".to_string()
568 })?
569 }
570 EncryptionAlgorithm::ChaCha20Poly1305 => {
571 let cipher = ChaCha20Poly1305::new_from_slice(key.material())
572 .map_err(|e| format!("Invalid ChaCha20 key: {}", e))?;
573 let cha_nonce = ChaNonce::from_slice(®ion.nonce);
574 cipher
575 .decrypt(cha_nonce, region.ciphertext.as_ref())
576 .map_err(|_| {
577 "ChaCha20-Poly1305 decryption failed: authentication tag mismatch"
578 .to_string()
579 })?
580 }
581 EncryptionAlgorithm::XChaCha20Poly1305 => {
582 let cipher = XChaCha20Poly1305::new_from_slice(key.material())
583 .map_err(|e| format!("Invalid XChaCha20 key: {}", e))?;
584 let x_nonce = XNonce::from_slice(®ion.nonce);
585 cipher
586 .decrypt(x_nonce, region.ciphertext.as_ref())
587 .map_err(|_| {
588 "XChaCha20-Poly1305 decryption failed: authentication tag mismatch"
589 .to_string()
590 })?
591 }
592 };
593
594 let elapsed = start.elapsed();
595
596 {
598 let mut stats = self.stats.write().unwrap();
599 stats.bytes_decrypted += plaintext.len() as u64;
600 stats.decrypt_ops += 1;
601 let total_time = stats.avg_decrypt_time_us * (stats.decrypt_ops - 1) as f64;
602 stats.avg_decrypt_time_us =
603 (total_time + elapsed.as_micros() as f64) / stats.decrypt_ops as f64;
604 }
605
606 Ok(plaintext)
607 }
608
609 #[cfg(not(feature = "crypto"))]
613 pub fn decrypt_region(&self, region: &EncryptedRegion) -> Result<Vec<u8>, String> {
614 let start = Instant::now();
615
616 let key = if region.key_id == self.active_key.read().unwrap().key_id {
618 self.active_key.read().unwrap().clone()
619 } else {
620 self.previous_keys
621 .read()
622 .unwrap()
623 .get(®ion.key_id)
624 .cloned()
625 .ok_or_else(|| format!("Key {} not found", region.key_id))?
626 };
627
628 if region.ciphertext.len() < 16 {
630 return Err("Ciphertext too short".to_string());
631 }
632 let (ciphertext, _tag) = region.ciphertext.split_at(region.ciphertext.len() - 16);
633
634 let mut plaintext = ciphertext.to_vec();
637 for (i, byte) in plaintext.iter_mut().enumerate() {
638 *byte ^= region.nonce[i % region.nonce.len()];
639 *byte ^= key.material()[i % key.material().len()];
640 }
641
642 let elapsed = start.elapsed();
643
644 {
646 let mut stats = self.stats.write().unwrap();
647 stats.bytes_decrypted += plaintext.len() as u64;
648 stats.decrypt_ops += 1;
649 let total_time = stats.avg_decrypt_time_us * (stats.decrypt_ops - 1) as f64;
650 stats.avg_decrypt_time_us =
651 (total_time + elapsed.as_micros() as f64) / stats.decrypt_ops as f64;
652 }
653
654 Ok(plaintext)
655 }
656
657 pub fn rotate_keys(&self) {
659 let mut active = self.active_key.write().unwrap();
660 let mut previous = self.previous_keys.write().unwrap();
661
662 let old_key = active.clone();
664 previous.insert(old_key.key_id, old_key);
665
666 let new_key_id = self.next_key_id.fetch_add(1, Ordering::Relaxed);
668 *active = EncryptionKey::new(new_key_id, self.config.algorithm);
669
670 *self.last_rotation.write().unwrap() = Instant::now();
672
673 self.stats.write().unwrap().key_rotations += 1;
675
676 while previous.len() > 10 {
678 if let Some(oldest_id) = previous.keys().min().copied() {
679 previous.remove(&oldest_id);
680 }
681 }
682 }
683
684 pub fn needs_rotation(&self) -> bool {
686 let last = *self.last_rotation.read().unwrap();
687 last.elapsed() >= self.config.key_rotation_interval
688 }
689
690 pub fn stats(&self) -> EncryptionStats {
692 self.stats.read().unwrap().clone()
693 }
694
695 pub fn current_key_id(&self) -> u64 {
697 self.active_key.read().unwrap().key_id
698 }
699
700 pub fn config(&self) -> &EncryptionConfig {
702 &self.config
703 }
704}
705
706impl fmt::Debug for MemoryEncryption {
707 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
708 f.debug_struct("MemoryEncryption")
709 .field("config", &self.config)
710 .field("current_key_id", &self.current_key_id())
711 .field("stats", &self.stats())
712 .finish()
713 }
714}
715
716#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
722pub enum AccessLevel {
723 Deny,
725 ReadOnly,
727 #[default]
729 ReadWrite,
730 Full,
732}
733
734#[derive(Debug, Clone)]
736pub struct ResourceLimits {
737 pub max_memory_bytes: u64,
739 pub max_execution_time: Duration,
741 pub max_messages_per_sec: u32,
743 pub max_k2k_connections: u32,
745 pub max_checkpoint_size: u64,
747 pub max_queue_depth: u32,
749}
750
751impl Default for ResourceLimits {
752 fn default() -> Self {
753 Self {
754 max_memory_bytes: 1024 * 1024 * 1024, max_execution_time: Duration::from_secs(60),
756 max_messages_per_sec: 10000,
757 max_k2k_connections: 100,
758 max_checkpoint_size: 100 * 1024 * 1024, max_queue_depth: 4096,
760 }
761 }
762}
763
764impl ResourceLimits {
765 pub fn new() -> Self {
767 Self::default()
768 }
769
770 pub fn with_max_memory(mut self, bytes: u64) -> Self {
772 self.max_memory_bytes = bytes;
773 self
774 }
775
776 pub fn with_max_execution_time(mut self, duration: Duration) -> Self {
778 self.max_execution_time = duration;
779 self
780 }
781
782 pub fn with_max_messages_per_sec(mut self, count: u32) -> Self {
784 self.max_messages_per_sec = count;
785 self
786 }
787
788 pub fn with_max_k2k_connections(mut self, count: u32) -> Self {
790 self.max_k2k_connections = count;
791 self
792 }
793
794 pub fn restrictive() -> Self {
796 Self {
797 max_memory_bytes: 256 * 1024 * 1024, max_execution_time: Duration::from_secs(10),
799 max_messages_per_sec: 1000,
800 max_k2k_connections: 10,
801 max_checkpoint_size: 10 * 1024 * 1024, max_queue_depth: 256,
803 }
804 }
805
806 pub fn permissive() -> Self {
808 Self {
809 max_memory_bytes: 8 * 1024 * 1024 * 1024, max_execution_time: Duration::from_secs(3600),
811 max_messages_per_sec: 1_000_000,
812 max_k2k_connections: 1000,
813 max_checkpoint_size: 1024 * 1024 * 1024, max_queue_depth: 65536,
815 }
816 }
817}
818
819#[derive(Debug, Clone)]
821pub struct SandboxPolicy {
822 pub limits: ResourceLimits,
824 pub allowed_k2k_destinations: HashSet<String>,
826 pub denied_k2k_destinations: HashSet<String>,
828 pub memory_access: HashMap<String, AccessLevel>,
830 pub can_checkpoint: bool,
832 pub can_migrate: bool,
834 pub can_spawn: bool,
836 pub can_access_host: bool,
838 pub allowed_syscalls: HashSet<String>,
840}
841
842impl Default for SandboxPolicy {
843 fn default() -> Self {
844 Self {
845 limits: ResourceLimits::default(),
846 allowed_k2k_destinations: HashSet::new(),
847 denied_k2k_destinations: HashSet::new(),
848 memory_access: HashMap::new(),
849 can_checkpoint: true,
850 can_migrate: true,
851 can_spawn: false,
852 can_access_host: false,
853 allowed_syscalls: HashSet::new(),
854 }
855 }
856}
857
858impl SandboxPolicy {
859 pub fn new() -> Self {
861 Self::default()
862 }
863
864 pub fn with_limits(mut self, limits: ResourceLimits) -> Self {
866 self.limits = limits;
867 self
868 }
869
870 pub fn with_memory_limit(mut self, bytes: u64) -> Self {
872 self.limits.max_memory_bytes = bytes;
873 self
874 }
875
876 pub fn with_execution_timeout(mut self, timeout: Duration) -> Self {
878 self.limits.max_execution_time = timeout;
879 self
880 }
881
882 pub fn allow_k2k_to(mut self, destinations: &[&str]) -> Self {
884 self.allowed_k2k_destinations
885 .extend(destinations.iter().map(|s| s.to_string()));
886 self
887 }
888
889 pub fn deny_k2k_to(mut self, destinations: &[&str]) -> Self {
891 self.denied_k2k_destinations
892 .extend(destinations.iter().map(|s| s.to_string()));
893 self
894 }
895
896 pub fn with_memory_access(mut self, region: &str, access: AccessLevel) -> Self {
898 self.memory_access.insert(region.to_string(), access);
899 self
900 }
901
902 pub fn with_checkpoint(mut self, enabled: bool) -> Self {
904 self.can_checkpoint = enabled;
905 self
906 }
907
908 pub fn with_migration(mut self, enabled: bool) -> Self {
910 self.can_migrate = enabled;
911 self
912 }
913
914 pub fn with_spawn(mut self, enabled: bool) -> Self {
916 self.can_spawn = enabled;
917 self
918 }
919
920 pub fn with_host_access(mut self, enabled: bool) -> Self {
922 self.can_access_host = enabled;
923 self
924 }
925
926 pub fn restrictive() -> Self {
928 Self {
929 limits: ResourceLimits::restrictive(),
930 allowed_k2k_destinations: HashSet::new(),
931 denied_k2k_destinations: HashSet::new(),
932 memory_access: HashMap::new(),
933 can_checkpoint: false,
934 can_migrate: false,
935 can_spawn: false,
936 can_access_host: false,
937 allowed_syscalls: HashSet::new(),
938 }
939 }
940
941 pub fn permissive() -> Self {
943 Self {
944 limits: ResourceLimits::permissive(),
945 allowed_k2k_destinations: HashSet::new(),
946 denied_k2k_destinations: HashSet::new(),
947 memory_access: HashMap::new(),
948 can_checkpoint: true,
949 can_migrate: true,
950 can_spawn: true,
951 can_access_host: true,
952 allowed_syscalls: HashSet::new(),
953 }
954 }
955
956 pub fn is_k2k_allowed(&self, destination: &str) -> bool {
958 if self.denied_k2k_destinations.contains(destination) {
960 return false;
961 }
962 if self.allowed_k2k_destinations.is_empty() {
964 return true;
965 }
966 self.allowed_k2k_destinations.contains(destination)
968 }
969}
970
971#[derive(Debug, Clone, PartialEq, Eq)]
973pub enum ViolationType {
974 MemoryLimitExceeded {
976 used: u64,
978 limit: u64,
980 },
981 ExecutionTimeExceeded {
983 elapsed: Duration,
985 limit: Duration,
987 },
988 MessageRateExceeded {
990 rate: u32,
992 limit: u32,
994 },
995 UnauthorizedK2K {
997 destination: String,
999 },
1000 UnauthorizedMemoryAccess {
1002 region: String,
1004 requested: AccessLevel,
1006 },
1007 CheckpointNotAllowed,
1009 MigrationNotAllowed,
1011 SpawnNotAllowed,
1013 HostAccessNotAllowed,
1015}
1016
1017impl fmt::Display for ViolationType {
1018 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1019 match self {
1020 Self::MemoryLimitExceeded { used, limit } => {
1021 write!(f, "Memory limit exceeded: {} > {} bytes", used, limit)
1022 }
1023 Self::ExecutionTimeExceeded { elapsed, limit } => {
1024 write!(f, "Execution time exceeded: {:?} > {:?}", elapsed, limit)
1025 }
1026 Self::MessageRateExceeded { rate, limit } => {
1027 write!(f, "Message rate exceeded: {} > {} msg/s", rate, limit)
1028 }
1029 Self::UnauthorizedK2K { destination } => {
1030 write!(f, "Unauthorized K2K to: {}", destination)
1031 }
1032 Self::UnauthorizedMemoryAccess { region, requested } => {
1033 write!(
1034 f,
1035 "Unauthorized {:?} access to region: {}",
1036 requested, region
1037 )
1038 }
1039 Self::CheckpointNotAllowed => write!(f, "Checkpointing not allowed"),
1040 Self::MigrationNotAllowed => write!(f, "Migration not allowed"),
1041 Self::SpawnNotAllowed => write!(f, "Spawning not allowed"),
1042 Self::HostAccessNotAllowed => write!(f, "Host memory access not allowed"),
1043 }
1044 }
1045}
1046
1047#[derive(Debug, Clone)]
1049pub struct SandboxViolation {
1050 pub violation_type: ViolationType,
1052 pub kernel_id: KernelId,
1054 pub timestamp: Instant,
1056 pub context: Option<String>,
1058}
1059
1060#[derive(Debug, Clone, Default)]
1062pub struct SandboxStats {
1063 pub total_checks: u64,
1065 pub violations_detected: u64,
1067 pub operations_blocked: u64,
1069 pub current_memory_usage: u64,
1071 pub current_message_rate: u32,
1073}
1074
1075pub struct KernelSandbox {
1077 policy: SandboxPolicy,
1079 kernel_id: Option<KernelId>,
1081 stats: RwLock<SandboxStats>,
1083 violations: RwLock<Vec<SandboxViolation>>,
1085 start_time: RwLock<Option<Instant>>,
1087 message_count: AtomicU64,
1089 last_rate_check: RwLock<Instant>,
1091}
1092
1093impl KernelSandbox {
1094 pub fn new(policy: SandboxPolicy) -> Self {
1096 Self {
1097 policy,
1098 kernel_id: None,
1099 stats: RwLock::new(SandboxStats::default()),
1100 violations: RwLock::new(Vec::new()),
1101 start_time: RwLock::new(None),
1102 message_count: AtomicU64::new(0),
1103 last_rate_check: RwLock::new(Instant::now()),
1104 }
1105 }
1106
1107 pub fn apply_to_kernel(&mut self, kernel_id: KernelId) {
1109 self.kernel_id = Some(kernel_id);
1110 *self.start_time.write().unwrap() = Some(Instant::now());
1111 }
1112
1113 pub fn check_memory(&self, bytes: u64) -> Result<(), SandboxViolation> {
1115 self.stats.write().unwrap().total_checks += 1;
1116
1117 if bytes > self.policy.limits.max_memory_bytes {
1118 let violation = SandboxViolation {
1119 violation_type: ViolationType::MemoryLimitExceeded {
1120 used: bytes,
1121 limit: self.policy.limits.max_memory_bytes,
1122 },
1123 kernel_id: self
1124 .kernel_id
1125 .clone()
1126 .unwrap_or_else(|| KernelId("unknown".to_string())),
1127 timestamp: Instant::now(),
1128 context: None,
1129 };
1130 self.record_violation(violation.clone());
1131 return Err(violation);
1132 }
1133
1134 self.stats.write().unwrap().current_memory_usage = bytes;
1135 Ok(())
1136 }
1137
1138 pub fn check_execution_time(&self) -> Result<(), SandboxViolation> {
1140 self.stats.write().unwrap().total_checks += 1;
1141
1142 if let Some(start) = *self.start_time.read().unwrap() {
1143 let elapsed = start.elapsed();
1144 if elapsed > self.policy.limits.max_execution_time {
1145 let violation = SandboxViolation {
1146 violation_type: ViolationType::ExecutionTimeExceeded {
1147 elapsed,
1148 limit: self.policy.limits.max_execution_time,
1149 },
1150 kernel_id: self
1151 .kernel_id
1152 .clone()
1153 .unwrap_or_else(|| KernelId("unknown".to_string())),
1154 timestamp: Instant::now(),
1155 context: None,
1156 };
1157 self.record_violation(violation.clone());
1158 return Err(violation);
1159 }
1160 }
1161 Ok(())
1162 }
1163
1164 pub fn check_k2k(&self, destination: &str) -> Result<(), SandboxViolation> {
1166 self.stats.write().unwrap().total_checks += 1;
1167
1168 if !self.policy.is_k2k_allowed(destination) {
1169 let violation = SandboxViolation {
1170 violation_type: ViolationType::UnauthorizedK2K {
1171 destination: destination.to_string(),
1172 },
1173 kernel_id: self
1174 .kernel_id
1175 .clone()
1176 .unwrap_or_else(|| KernelId("unknown".to_string())),
1177 timestamp: Instant::now(),
1178 context: None,
1179 };
1180 self.record_violation(violation.clone());
1181 return Err(violation);
1182 }
1183 Ok(())
1184 }
1185
1186 pub fn check_checkpoint(&self) -> Result<(), SandboxViolation> {
1188 self.stats.write().unwrap().total_checks += 1;
1189
1190 if !self.policy.can_checkpoint {
1191 let violation = SandboxViolation {
1192 violation_type: ViolationType::CheckpointNotAllowed,
1193 kernel_id: self
1194 .kernel_id
1195 .clone()
1196 .unwrap_or_else(|| KernelId("unknown".to_string())),
1197 timestamp: Instant::now(),
1198 context: None,
1199 };
1200 self.record_violation(violation.clone());
1201 return Err(violation);
1202 }
1203 Ok(())
1204 }
1205
1206 pub fn check_migration(&self) -> Result<(), SandboxViolation> {
1208 self.stats.write().unwrap().total_checks += 1;
1209
1210 if !self.policy.can_migrate {
1211 let violation = SandboxViolation {
1212 violation_type: ViolationType::MigrationNotAllowed,
1213 kernel_id: self
1214 .kernel_id
1215 .clone()
1216 .unwrap_or_else(|| KernelId("unknown".to_string())),
1217 timestamp: Instant::now(),
1218 context: None,
1219 };
1220 self.record_violation(violation.clone());
1221 return Err(violation);
1222 }
1223 Ok(())
1224 }
1225
1226 pub fn record_message(&self) -> Result<(), SandboxViolation> {
1228 self.message_count.fetch_add(1, Ordering::Relaxed);
1229
1230 let mut last_check = self.last_rate_check.write().unwrap();
1232 if last_check.elapsed() >= Duration::from_secs(1) {
1233 let count = self.message_count.swap(0, Ordering::Relaxed) as u32;
1234 *last_check = Instant::now();
1235
1236 self.stats.write().unwrap().current_message_rate = count;
1237
1238 if count > self.policy.limits.max_messages_per_sec {
1239 let violation = SandboxViolation {
1240 violation_type: ViolationType::MessageRateExceeded {
1241 rate: count,
1242 limit: self.policy.limits.max_messages_per_sec,
1243 },
1244 kernel_id: self
1245 .kernel_id
1246 .clone()
1247 .unwrap_or_else(|| KernelId("unknown".to_string())),
1248 timestamp: Instant::now(),
1249 context: None,
1250 };
1251 self.record_violation(violation.clone());
1252 return Err(violation);
1253 }
1254 }
1255 Ok(())
1256 }
1257
1258 fn record_violation(&self, violation: SandboxViolation) {
1260 let mut stats = self.stats.write().unwrap();
1261 stats.violations_detected += 1;
1262 stats.operations_blocked += 1;
1263
1264 self.violations.write().unwrap().push(violation);
1265 }
1266
1267 pub fn violations(&self) -> Vec<SandboxViolation> {
1269 self.violations.read().unwrap().clone()
1270 }
1271
1272 pub fn stats(&self) -> SandboxStats {
1274 self.stats.read().unwrap().clone()
1275 }
1276
1277 pub fn policy(&self) -> &SandboxPolicy {
1279 &self.policy
1280 }
1281
1282 pub fn reset(&self) {
1284 *self.stats.write().unwrap() = SandboxStats::default();
1285 self.violations.write().unwrap().clear();
1286 self.message_count.store(0, Ordering::Relaxed);
1287 }
1288}
1289
1290impl fmt::Debug for KernelSandbox {
1291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1292 f.debug_struct("KernelSandbox")
1293 .field("policy", &self.policy)
1294 .field("kernel_id", &self.kernel_id)
1295 .field("stats", &self.stats())
1296 .field("violations_count", &self.violations.read().unwrap().len())
1297 .finish()
1298 }
1299}
1300
1301#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1307pub enum ComplianceStandard {
1308 SOC2,
1310 GDPR,
1312 HIPAA,
1314 PCIDSS,
1316 ISO27001,
1318 FedRAMP,
1320 NIST,
1322}
1323
1324impl fmt::Display for ComplianceStandard {
1325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1326 match self {
1327 Self::SOC2 => write!(f, "SOC 2 Type II"),
1328 Self::GDPR => write!(f, "GDPR"),
1329 Self::HIPAA => write!(f, "HIPAA"),
1330 Self::PCIDSS => write!(f, "PCI DSS"),
1331 Self::ISO27001 => write!(f, "ISO 27001"),
1332 Self::FedRAMP => write!(f, "FedRAMP"),
1333 Self::NIST => write!(f, "NIST CSF"),
1334 }
1335 }
1336}
1337
1338#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
1340pub enum ReportFormat {
1341 #[default]
1343 Json,
1344 Html,
1346 Markdown,
1348 Pdf,
1350 Csv,
1352}
1353
1354#[derive(Debug, Clone)]
1356pub enum ComplianceStatus {
1357 Compliant,
1359 PartiallyCompliant {
1361 notes: Vec<String>,
1363 },
1364 NonCompliant {
1366 reasons: Vec<String>,
1368 },
1369 NotApplicable,
1371}
1372
1373impl ComplianceStatus {
1374 pub fn is_compliant(&self) -> bool {
1376 matches!(self, Self::Compliant | Self::NotApplicable)
1377 }
1378}
1379
1380#[derive(Debug, Clone)]
1382pub struct ComplianceCheck {
1383 pub id: String,
1385 pub name: String,
1387 pub standard: ComplianceStandard,
1389 pub description: String,
1391 pub status: ComplianceStatus,
1393 pub evidence: Vec<String>,
1395 pub recommendations: Vec<String>,
1397 pub checked_at: SystemTime,
1399}
1400
1401#[derive(Debug, Clone)]
1403pub struct ComplianceSummary {
1404 pub total_checks: usize,
1406 pub compliant: usize,
1408 pub partially_compliant: usize,
1410 pub non_compliant: usize,
1412 pub not_applicable: usize,
1414 pub compliance_percentage: f64,
1416}
1417
1418#[derive(Debug, Clone)]
1420pub struct ComplianceReport {
1421 pub id: String,
1423 pub title: String,
1425 pub standards: Vec<ComplianceStandard>,
1427 pub checks: Vec<ComplianceCheck>,
1429 pub summary: ComplianceSummary,
1431 pub generated_at: SystemTime,
1433 pub period_start: SystemTime,
1435 pub period_end: SystemTime,
1437 pub metadata: HashMap<String, String>,
1439}
1440
1441impl ComplianceReport {
1442 pub fn export(&self, format: ReportFormat) -> String {
1444 match format {
1445 ReportFormat::Json => self.to_json(),
1446 ReportFormat::Html => self.to_html(),
1447 ReportFormat::Markdown => self.to_markdown(),
1448 ReportFormat::Pdf => self.to_markdown(), ReportFormat::Csv => self.to_csv(),
1450 }
1451 }
1452
1453 fn to_json(&self) -> String {
1454 let mut json = String::new();
1455 json.push_str("{\n");
1456 json.push_str(&format!(" \"id\": \"{}\",\n", self.id));
1457 json.push_str(&format!(" \"title\": \"{}\",\n", self.title));
1458 json.push_str(&format!(
1459 " \"standards\": [{}],\n",
1460 self.standards
1461 .iter()
1462 .map(|s| format!("\"{}\"", s))
1463 .collect::<Vec<_>>()
1464 .join(", ")
1465 ));
1466 json.push_str(" \"summary\": {\n");
1467 json.push_str(&format!(
1468 " \"total_checks\": {},\n",
1469 self.summary.total_checks
1470 ));
1471 json.push_str(&format!(" \"compliant\": {},\n", self.summary.compliant));
1472 json.push_str(&format!(
1473 " \"partially_compliant\": {},\n",
1474 self.summary.partially_compliant
1475 ));
1476 json.push_str(&format!(
1477 " \"non_compliant\": {},\n",
1478 self.summary.non_compliant
1479 ));
1480 json.push_str(&format!(
1481 " \"compliance_percentage\": {:.1}\n",
1482 self.summary.compliance_percentage
1483 ));
1484 json.push_str(" },\n");
1485 json.push_str(&format!(" \"checks_count\": {}\n", self.checks.len()));
1486 json.push_str("}\n");
1487 json
1488 }
1489
1490 fn to_html(&self) -> String {
1491 let mut html = String::new();
1492 html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
1493 html.push_str(&format!("<title>{}</title>\n", self.title));
1494 html.push_str("<style>body { font-family: sans-serif; } .compliant { color: green; } .non-compliant { color: red; }</style>\n");
1495 html.push_str("</head>\n<body>\n");
1496 html.push_str(&format!("<h1>{}</h1>\n", self.title));
1497 html.push_str(&format!("<p>Report ID: {}</p>\n", self.id));
1498 html.push_str("<h2>Summary</h2>\n");
1499 html.push_str("<table>\n");
1500 html.push_str(&format!(
1501 "<tr><td>Total Checks</td><td>{}</td></tr>\n",
1502 self.summary.total_checks
1503 ));
1504 html.push_str(&format!(
1505 "<tr><td>Compliant</td><td class=\"compliant\">{}</td></tr>\n",
1506 self.summary.compliant
1507 ));
1508 html.push_str(&format!(
1509 "<tr><td>Non-Compliant</td><td class=\"non-compliant\">{}</td></tr>\n",
1510 self.summary.non_compliant
1511 ));
1512 html.push_str(&format!(
1513 "<tr><td>Compliance</td><td>{:.1}%</td></tr>\n",
1514 self.summary.compliance_percentage
1515 ));
1516 html.push_str("</table>\n");
1517 html.push_str("<h2>Checks</h2>\n");
1518 for check in &self.checks {
1519 html.push_str(&format!("<h3>{}</h3>\n", check.name));
1520 html.push_str(&format!("<p>{}</p>\n", check.description));
1521 }
1522 html.push_str("</body>\n</html>\n");
1523 html
1524 }
1525
1526 fn to_markdown(&self) -> String {
1527 let mut md = String::new();
1528 md.push_str(&format!("# {}\n\n", self.title));
1529 md.push_str(&format!("**Report ID:** {}\n\n", self.id));
1530 md.push_str("## Summary\n\n");
1531 md.push_str("| Metric | Value |\n");
1532 md.push_str("|--------|-------|\n");
1533 md.push_str(&format!(
1534 "| Total Checks | {} |\n",
1535 self.summary.total_checks
1536 ));
1537 md.push_str(&format!("| Compliant | {} |\n", self.summary.compliant));
1538 md.push_str(&format!(
1539 "| Partially Compliant | {} |\n",
1540 self.summary.partially_compliant
1541 ));
1542 md.push_str(&format!(
1543 "| Non-Compliant | {} |\n",
1544 self.summary.non_compliant
1545 ));
1546 md.push_str(&format!(
1547 "| Compliance | {:.1}% |\n",
1548 self.summary.compliance_percentage
1549 ));
1550 md.push_str("\n## Detailed Checks\n\n");
1551 for check in &self.checks {
1552 let status_icon = match &check.status {
1553 ComplianceStatus::Compliant => "✅",
1554 ComplianceStatus::PartiallyCompliant { .. } => "⚠️",
1555 ComplianceStatus::NonCompliant { .. } => "❌",
1556 ComplianceStatus::NotApplicable => "➖",
1557 };
1558 md.push_str(&format!("### {} {}\n\n", status_icon, check.name));
1559 md.push_str(&format!("{}\n\n", check.description));
1560 }
1561 md
1562 }
1563
1564 fn to_csv(&self) -> String {
1565 let mut csv = String::new();
1566 csv.push_str("ID,Name,Standard,Status,Description\n");
1567 for check in &self.checks {
1568 let status = match &check.status {
1569 ComplianceStatus::Compliant => "Compliant",
1570 ComplianceStatus::PartiallyCompliant { .. } => "Partially Compliant",
1571 ComplianceStatus::NonCompliant { .. } => "Non-Compliant",
1572 ComplianceStatus::NotApplicable => "N/A",
1573 };
1574 csv.push_str(&format!(
1575 "\"{}\",\"{}\",\"{}\",\"{}\",\"{}\"\n",
1576 check.id, check.name, check.standard, status, check.description
1577 ));
1578 }
1579 csv
1580 }
1581}
1582
1583pub struct ComplianceReporter {
1585 standards: HashSet<ComplianceStandard>,
1587 organization: String,
1589 metadata: HashMap<String, String>,
1591 custom_checks: Vec<Box<dyn Fn() -> ComplianceCheck + Send + Sync>>,
1593}
1594
1595impl ComplianceReporter {
1596 pub fn new() -> Self {
1598 Self {
1599 standards: HashSet::new(),
1600 organization: "Unknown".to_string(),
1601 metadata: HashMap::new(),
1602 custom_checks: Vec::new(),
1603 }
1604 }
1605
1606 pub fn with_standard(mut self, standard: ComplianceStandard) -> Self {
1608 self.standards.insert(standard);
1609 self
1610 }
1611
1612 pub fn with_organization(mut self, org: &str) -> Self {
1614 self.organization = org.to_string();
1615 self
1616 }
1617
1618 pub fn with_metadata(mut self, key: &str, value: &str) -> Self {
1620 self.metadata.insert(key.to_string(), value.to_string());
1621 self
1622 }
1623
1624 pub fn generate_report(&self, _format: ReportFormat) -> ComplianceReport {
1626 let mut checks = Vec::new();
1627 let now = SystemTime::now();
1628
1629 for standard in &self.standards {
1631 checks.extend(self.generate_standard_checks(*standard));
1632 }
1633
1634 for check_fn in &self.custom_checks {
1636 checks.push(check_fn());
1637 }
1638
1639 let total = checks.len();
1641 let compliant = checks
1642 .iter()
1643 .filter(|c| matches!(c.status, ComplianceStatus::Compliant))
1644 .count();
1645 let partial = checks
1646 .iter()
1647 .filter(|c| matches!(c.status, ComplianceStatus::PartiallyCompliant { .. }))
1648 .count();
1649 let non_compliant = checks
1650 .iter()
1651 .filter(|c| matches!(c.status, ComplianceStatus::NonCompliant { .. }))
1652 .count();
1653 let na = checks
1654 .iter()
1655 .filter(|c| matches!(c.status, ComplianceStatus::NotApplicable))
1656 .count();
1657
1658 let applicable = total - na;
1659 let compliance_pct = if applicable > 0 {
1660 ((compliant as f64 + partial as f64 * 0.5) / applicable as f64) * 100.0
1661 } else {
1662 100.0
1663 };
1664
1665 ComplianceReport {
1666 id: format!(
1667 "RPT-{}",
1668 now.duration_since(SystemTime::UNIX_EPOCH)
1669 .unwrap()
1670 .as_secs()
1671 ),
1672 title: format!("{} Compliance Report", self.organization),
1673 standards: self.standards.iter().copied().collect(),
1674 checks,
1675 summary: ComplianceSummary {
1676 total_checks: total,
1677 compliant,
1678 partially_compliant: partial,
1679 non_compliant,
1680 not_applicable: na,
1681 compliance_percentage: compliance_pct,
1682 },
1683 generated_at: now,
1684 period_start: now - Duration::from_secs(30 * 24 * 60 * 60), period_end: now,
1686 metadata: self.metadata.clone(),
1687 }
1688 }
1689
1690 fn generate_standard_checks(&self, standard: ComplianceStandard) -> Vec<ComplianceCheck> {
1691 let now = SystemTime::now();
1692
1693 match standard {
1694 ComplianceStandard::SOC2 => vec![
1695 ComplianceCheck {
1696 id: "SOC2-CC1.1".to_string(),
1697 name: "Control Environment".to_string(),
1698 standard,
1699 description:
1700 "The entity demonstrates commitment to integrity and ethical values."
1701 .to_string(),
1702 status: ComplianceStatus::Compliant,
1703 evidence: vec![
1704 "Audit logging enabled".to_string(),
1705 "Access controls implemented".to_string(),
1706 ],
1707 recommendations: vec![],
1708 checked_at: now,
1709 },
1710 ComplianceCheck {
1711 id: "SOC2-CC6.1".to_string(),
1712 name: "Logical Access Controls".to_string(),
1713 standard,
1714 description:
1715 "Logical access security software, infrastructure, and architectures."
1716 .to_string(),
1717 status: ComplianceStatus::Compliant,
1718 evidence: vec![
1719 "Kernel sandboxing available".to_string(),
1720 "Memory encryption available".to_string(),
1721 ],
1722 recommendations: vec![],
1723 checked_at: now,
1724 },
1725 ComplianceCheck {
1726 id: "SOC2-CC7.2".to_string(),
1727 name: "System Monitoring".to_string(),
1728 standard,
1729 description: "System components are monitored and anomalies are identified."
1730 .to_string(),
1731 status: ComplianceStatus::Compliant,
1732 evidence: vec![
1733 "Health monitoring enabled".to_string(),
1734 "GPU memory dashboard available".to_string(),
1735 ],
1736 recommendations: vec![],
1737 checked_at: now,
1738 },
1739 ],
1740 ComplianceStandard::GDPR => vec![
1741 ComplianceCheck {
1742 id: "GDPR-32".to_string(),
1743 name: "Security of Processing".to_string(),
1744 standard,
1745 description: "Implement appropriate technical and organizational measures."
1746 .to_string(),
1747 status: ComplianceStatus::Compliant,
1748 evidence: vec!["Memory encryption available".to_string()],
1749 recommendations: vec!["Consider enabling encryption by default".to_string()],
1750 checked_at: now,
1751 },
1752 ComplianceCheck {
1753 id: "GDPR-33".to_string(),
1754 name: "Breach Notification".to_string(),
1755 standard,
1756 description: "Notify supervisory authority of personal data breach."
1757 .to_string(),
1758 status: ComplianceStatus::PartiallyCompliant {
1759 notes: vec!["Audit logging available but breach detection not automated"
1760 .to_string()],
1761 },
1762 evidence: vec!["Audit logging enabled".to_string()],
1763 recommendations: vec!["Add automated breach detection".to_string()],
1764 checked_at: now,
1765 },
1766 ],
1767 ComplianceStandard::HIPAA => vec![
1768 ComplianceCheck {
1769 id: "HIPAA-164.312(a)".to_string(),
1770 name: "Access Control".to_string(),
1771 standard,
1772 description: "Implement technical policies for electronic PHI access."
1773 .to_string(),
1774 status: ComplianceStatus::Compliant,
1775 evidence: vec![
1776 "Kernel sandboxing available".to_string(),
1777 "Access levels configurable".to_string(),
1778 ],
1779 recommendations: vec![],
1780 checked_at: now,
1781 },
1782 ComplianceCheck {
1783 id: "HIPAA-164.312(e)".to_string(),
1784 name: "Transmission Security".to_string(),
1785 standard,
1786 description: "Implement security measures for ePHI transmission.".to_string(),
1787 status: ComplianceStatus::Compliant,
1788 evidence: vec!["Memory encryption for data at rest".to_string()],
1789 recommendations: vec!["Implement TLS for network K2K".to_string()],
1790 checked_at: now,
1791 },
1792 ],
1793 ComplianceStandard::PCIDSS => vec![
1794 ComplianceCheck {
1795 id: "PCI-3.4".to_string(),
1796 name: "Render PAN Unreadable".to_string(),
1797 standard,
1798 description: "Render PAN unreadable anywhere it is stored.".to_string(),
1799 status: ComplianceStatus::Compliant,
1800 evidence: vec!["AES-256-GCM encryption available".to_string()],
1801 recommendations: vec![],
1802 checked_at: now,
1803 },
1804 ComplianceCheck {
1805 id: "PCI-10.1".to_string(),
1806 name: "Audit Trails".to_string(),
1807 standard,
1808 description: "Implement audit trails to link access to individual users."
1809 .to_string(),
1810 status: ComplianceStatus::Compliant,
1811 evidence: vec!["Comprehensive audit logging".to_string()],
1812 recommendations: vec![],
1813 checked_at: now,
1814 },
1815 ],
1816 ComplianceStandard::ISO27001 => vec![ComplianceCheck {
1817 id: "ISO-A.10.1".to_string(),
1818 name: "Cryptographic Controls".to_string(),
1819 standard,
1820 description: "Policy on use of cryptographic controls.".to_string(),
1821 status: ComplianceStatus::Compliant,
1822 evidence: vec!["Multiple encryption algorithms supported".to_string()],
1823 recommendations: vec![],
1824 checked_at: now,
1825 }],
1826 ComplianceStandard::FedRAMP => vec![ComplianceCheck {
1827 id: "FedRAMP-SC-28".to_string(),
1828 name: "Protection of Information at Rest".to_string(),
1829 standard,
1830 description: "Protect confidentiality and integrity of information at rest."
1831 .to_string(),
1832 status: ComplianceStatus::Compliant,
1833 evidence: vec!["FIPS-compliant algorithms available".to_string()],
1834 recommendations: vec![],
1835 checked_at: now,
1836 }],
1837 ComplianceStandard::NIST => vec![
1838 ComplianceCheck {
1839 id: "NIST-PR.DS-1".to_string(),
1840 name: "Data-at-rest Protection".to_string(),
1841 standard,
1842 description: "Data-at-rest is protected.".to_string(),
1843 status: ComplianceStatus::Compliant,
1844 evidence: vec!["Memory encryption module".to_string()],
1845 recommendations: vec![],
1846 checked_at: now,
1847 },
1848 ComplianceCheck {
1849 id: "NIST-DE.CM-1".to_string(),
1850 name: "Network Monitoring".to_string(),
1851 standard,
1852 description: "The network is monitored to detect cybersecurity events."
1853 .to_string(),
1854 status: ComplianceStatus::Compliant,
1855 evidence: vec![
1856 "Observability context".to_string(),
1857 "GPU profiler integration".to_string(),
1858 ],
1859 recommendations: vec![],
1860 checked_at: now,
1861 },
1862 ],
1863 }
1864 }
1865}
1866
1867impl Default for ComplianceReporter {
1868 fn default() -> Self {
1869 Self::new()
1870 }
1871}
1872
1873impl fmt::Debug for ComplianceReporter {
1874 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1875 f.debug_struct("ComplianceReporter")
1876 .field("standards", &self.standards)
1877 .field("organization", &self.organization)
1878 .field("metadata", &self.metadata)
1879 .field("custom_checks_count", &self.custom_checks.len())
1880 .finish()
1881 }
1882}
1883
1884#[cfg(test)]
1889mod tests {
1890 use super::*;
1891
1892 #[test]
1895 fn test_encryption_config_builder() {
1896 let config = EncryptionConfig::new()
1897 .with_algorithm(EncryptionAlgorithm::ChaCha20Poly1305)
1898 .with_key_rotation_interval(Duration::from_secs(7200))
1899 .with_control_block_encryption(false);
1900
1901 assert_eq!(config.algorithm, EncryptionAlgorithm::ChaCha20Poly1305);
1902 assert_eq!(config.key_rotation_interval, Duration::from_secs(7200));
1903 assert!(!config.encrypt_control_blocks);
1904 }
1905
1906 #[test]
1907 fn test_encryption_key_creation() {
1908 let key = EncryptionKey::new(1, EncryptionAlgorithm::Aes256Gcm);
1909 assert_eq!(key.key_id, 1);
1910 assert_eq!(key.key_size(), 32);
1911 assert!(!key.is_expired());
1912 }
1913
1914 #[test]
1915 fn test_encrypt_decrypt_roundtrip() {
1916 let encryption = MemoryEncryption::new(EncryptionConfig::default());
1917 let plaintext = b"Hello, GPU World!";
1918
1919 let encrypted = encryption.encrypt_region(plaintext);
1920 assert_ne!(encrypted.ciphertext[..plaintext.len()], plaintext[..]);
1921
1922 let decrypted = encryption.decrypt_region(&encrypted).unwrap();
1923 assert_eq!(decrypted, plaintext);
1924 }
1925
1926 #[test]
1927 fn test_key_rotation() {
1928 let encryption = MemoryEncryption::new(EncryptionConfig::default());
1929 let initial_key_id = encryption.current_key_id();
1930
1931 encryption.rotate_keys();
1932 assert_eq!(encryption.current_key_id(), initial_key_id + 1);
1933
1934 let stats = encryption.stats();
1936 assert_eq!(stats.key_rotations, 1);
1937 }
1938
1939 #[test]
1940 fn test_decrypt_with_old_key() {
1941 let encryption = MemoryEncryption::new(EncryptionConfig::default());
1942 let plaintext = b"Secret data";
1943
1944 let encrypted = encryption.encrypt_region(plaintext);
1945 let old_key_id = encrypted.key_id;
1946
1947 encryption.rotate_keys();
1949 assert_ne!(encryption.current_key_id(), old_key_id);
1950
1951 let decrypted = encryption.decrypt_region(&encrypted).unwrap();
1953 assert_eq!(decrypted, plaintext);
1954 }
1955
1956 #[test]
1957 fn test_encryption_stats() {
1958 let encryption = MemoryEncryption::new(EncryptionConfig::default());
1959 let data = vec![0u8; 1024];
1960
1961 for _ in 0..10 {
1962 let encrypted = encryption.encrypt_region(&data);
1963 let _ = encryption.decrypt_region(&encrypted);
1964 }
1965
1966 let stats = encryption.stats();
1967 assert_eq!(stats.encrypt_ops, 10);
1968 assert_eq!(stats.decrypt_ops, 10);
1969 assert_eq!(stats.bytes_encrypted, 10240);
1970 }
1971
1972 #[test]
1975 fn test_resource_limits_builder() {
1976 let limits = ResourceLimits::new()
1977 .with_max_memory(512 * 1024 * 1024)
1978 .with_max_execution_time(Duration::from_secs(30));
1979
1980 assert_eq!(limits.max_memory_bytes, 512 * 1024 * 1024);
1981 assert_eq!(limits.max_execution_time, Duration::from_secs(30));
1982 }
1983
1984 #[test]
1985 fn test_sandbox_policy_k2k() {
1986 let policy = SandboxPolicy::new()
1987 .allow_k2k_to(&["trusted_kernel", "another_trusted"])
1988 .deny_k2k_to(&["malicious_kernel"]);
1989
1990 assert!(policy.is_k2k_allowed("trusted_kernel"));
1991 assert!(policy.is_k2k_allowed("another_trusted"));
1992 assert!(!policy.is_k2k_allowed("malicious_kernel"));
1993 assert!(!policy.is_k2k_allowed("unknown_kernel")); }
1995
1996 #[test]
1997 fn test_sandbox_memory_check() {
1998 let policy = SandboxPolicy::new().with_memory_limit(1024);
1999 let sandbox = KernelSandbox::new(policy);
2000
2001 assert!(sandbox.check_memory(512).is_ok());
2003
2004 let result = sandbox.check_memory(2048);
2006 assert!(result.is_err());
2007
2008 if let Err(violation) = result {
2009 assert!(matches!(
2010 violation.violation_type,
2011 ViolationType::MemoryLimitExceeded { .. }
2012 ));
2013 }
2014 }
2015
2016 #[test]
2017 fn test_sandbox_k2k_check() {
2018 let policy = SandboxPolicy::new().deny_k2k_to(&["blocked"]);
2019 let sandbox = KernelSandbox::new(policy);
2020
2021 assert!(sandbox.check_k2k("allowed_dest").is_ok());
2022 assert!(sandbox.check_k2k("blocked").is_err());
2023 }
2024
2025 #[test]
2026 fn test_sandbox_checkpoint_check() {
2027 let policy = SandboxPolicy::restrictive();
2028 let sandbox = KernelSandbox::new(policy);
2029
2030 assert!(sandbox.check_checkpoint().is_err());
2031
2032 let permissive = SandboxPolicy::permissive();
2033 let sandbox2 = KernelSandbox::new(permissive);
2034 assert!(sandbox2.check_checkpoint().is_ok());
2035 }
2036
2037 #[test]
2038 fn test_sandbox_stats() {
2039 let policy = SandboxPolicy::new().with_memory_limit(1024);
2040 let sandbox = KernelSandbox::new(policy);
2041
2042 let _ = sandbox.check_memory(512);
2043 let _ = sandbox.check_memory(2048); let _ = sandbox.check_k2k("dest");
2045
2046 let stats = sandbox.stats();
2047 assert_eq!(stats.total_checks, 3);
2048 assert_eq!(stats.violations_detected, 1);
2049 }
2050
2051 #[test]
2052 fn test_sandbox_violations_list() {
2053 let policy = SandboxPolicy::restrictive();
2054 let sandbox = KernelSandbox::new(policy);
2055
2056 let _ = sandbox.check_checkpoint();
2057 let _ = sandbox.check_migration();
2058
2059 let violations = sandbox.violations();
2060 assert_eq!(violations.len(), 2);
2061 }
2062
2063 #[test]
2066 fn test_compliance_reporter_creation() {
2067 let reporter = ComplianceReporter::new()
2068 .with_standard(ComplianceStandard::SOC2)
2069 .with_standard(ComplianceStandard::GDPR)
2070 .with_organization("Test Org");
2071
2072 assert_eq!(reporter.standards.len(), 2);
2073 assert!(reporter.standards.contains(&ComplianceStandard::SOC2));
2074 assert!(reporter.standards.contains(&ComplianceStandard::GDPR));
2075 }
2076
2077 #[test]
2078 fn test_generate_soc2_report() {
2079 let reporter = ComplianceReporter::new()
2080 .with_standard(ComplianceStandard::SOC2)
2081 .with_organization("Acme Corp");
2082
2083 let report = reporter.generate_report(ReportFormat::Json);
2084
2085 assert!(!report.checks.is_empty());
2086 assert!(report.summary.total_checks > 0);
2087 assert!(report.title.contains("Acme Corp"));
2088 }
2089
2090 #[test]
2091 fn test_report_json_export() {
2092 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::HIPAA);
2093
2094 let report = reporter.generate_report(ReportFormat::Json);
2095 let json = report.export(ReportFormat::Json);
2096
2097 assert!(json.contains("\"id\""));
2098 assert!(json.contains("\"summary\""));
2099 }
2100
2101 #[test]
2102 fn test_report_markdown_export() {
2103 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::NIST);
2104
2105 let report = reporter.generate_report(ReportFormat::Markdown);
2106 let md = report.export(ReportFormat::Markdown);
2107
2108 assert!(md.contains("# "));
2109 assert!(md.contains("## Summary"));
2110 assert!(md.contains("| Metric | Value |"));
2111 }
2112
2113 #[test]
2114 fn test_report_html_export() {
2115 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::PCIDSS);
2116
2117 let report = reporter.generate_report(ReportFormat::Html);
2118 let html = report.export(ReportFormat::Html);
2119
2120 assert!(html.contains("<!DOCTYPE html>"));
2121 assert!(html.contains("<h1>"));
2122 }
2123
2124 #[test]
2125 fn test_report_csv_export() {
2126 let reporter = ComplianceReporter::new().with_standard(ComplianceStandard::ISO27001);
2127
2128 let report = reporter.generate_report(ReportFormat::Csv);
2129 let csv = report.export(ReportFormat::Csv);
2130
2131 assert!(csv.contains("ID,Name,Standard,Status,Description"));
2132 }
2133
2134 #[test]
2135 fn test_compliance_summary_calculation() {
2136 let reporter = ComplianceReporter::new()
2137 .with_standard(ComplianceStandard::SOC2)
2138 .with_standard(ComplianceStandard::GDPR)
2139 .with_standard(ComplianceStandard::HIPAA);
2140
2141 let report = reporter.generate_report(ReportFormat::Json);
2142
2143 let sum = report.summary.compliant
2144 + report.summary.partially_compliant
2145 + report.summary.non_compliant
2146 + report.summary.not_applicable;
2147 assert_eq!(sum, report.summary.total_checks);
2148 }
2149
2150 #[test]
2151 fn test_compliance_status_is_compliant() {
2152 assert!(ComplianceStatus::Compliant.is_compliant());
2153 assert!(ComplianceStatus::NotApplicable.is_compliant());
2154 assert!(!ComplianceStatus::NonCompliant { reasons: vec![] }.is_compliant());
2155 assert!(!ComplianceStatus::PartiallyCompliant { notes: vec![] }.is_compliant());
2156 }
2157
2158 #[test]
2159 fn test_all_standards() {
2160 let reporter = ComplianceReporter::new()
2161 .with_standard(ComplianceStandard::SOC2)
2162 .with_standard(ComplianceStandard::GDPR)
2163 .with_standard(ComplianceStandard::HIPAA)
2164 .with_standard(ComplianceStandard::PCIDSS)
2165 .with_standard(ComplianceStandard::ISO27001)
2166 .with_standard(ComplianceStandard::FedRAMP)
2167 .with_standard(ComplianceStandard::NIST);
2168
2169 let report = reporter.generate_report(ReportFormat::Json);
2170 assert_eq!(report.standards.len(), 7);
2171 }
2172}