| 1 |
//! Chunk-level security isolation for ZephyrFS |
| 2 |
//! |
| 3 |
//! Implements military-grade per-chunk security boundaries to ensure that: |
| 4 |
//! 1. Each chunk is encrypted with unique, non-reusable keys |
| 5 |
//! 2. Chunks are isolated from each other - compromise of one chunk cannot affect others |
| 6 |
//! 3. Zero-knowledge guarantee - storage nodes never see plaintext or patterns |
| 7 |
//! 4. Malicious content isolation - suspicious chunks are quarantined immediately |
| 8 |
|
| 9 |
use anyhow::{Context, Result}; |
| 10 |
use chrono::{DateTime, Utc}; |
| 11 |
use ring::digest::{digest, SHA256}; |
| 12 |
use ring::rand::{SecureRandom, SystemRandom}; |
| 13 |
use serde::{Deserialize, Serialize}; |
| 14 |
use std::collections::HashMap; |
| 15 |
use std::sync::Arc; |
| 16 |
use tokio::sync::RwLock; |
| 17 |
use tracing::{debug, info, warn, error}; |
| 18 |
use uuid::Uuid; |
| 19 |
use zeroize::{Zeroize, ZeroizeOnDrop}; |
| 20 |
|
| 21 |
use crate::crypto::{EncryptedData, KeyHierarchy, SecureBytes}; |
| 22 |
|
| 23 |
/// Security isolation levels for chunks |
| 24 |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] |
| 25 |
pub enum IsolationLevel { |
| 26 |
/// Standard isolation - normal chunks with per-chunk encryption |
| 27 |
Standard, |
| 28 |
/// Enhanced isolation - suspicious chunks with additional monitoring |
| 29 |
Enhanced, |
| 30 |
/// Maximum isolation - quarantined chunks with strict containment |
| 31 |
Quarantined, |
| 32 |
} |
| 33 |
|
| 34 |
/// Chunk security container with complete isolation |
| 35 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 36 |
pub struct IsolatedChunk { |
| 37 |
/// Unique chunk identifier |
| 38 |
pub chunk_id: Uuid, |
| 39 |
|
| 40 |
/// Encrypted chunk data |
| 41 |
pub encrypted_data: EncryptedData, |
| 42 |
|
| 43 |
/// Unique encryption key fingerprint (not the key itself) |
| 44 |
pub key_fingerprint: String, |
| 45 |
|
| 46 |
/// Security isolation level |
| 47 |
pub isolation_level: IsolationLevel, |
| 48 |
|
| 49 |
/// Timestamp when chunk was isolated |
| 50 |
pub isolated_at: u64, |
| 51 |
|
| 52 |
/// Security metadata (encrypted) |
| 53 |
pub security_metadata: Vec<u8>, |
| 54 |
|
| 55 |
/// Integrity verification hash |
| 56 |
pub integrity_hash: String, |
| 57 |
|
| 58 |
/// Access control flags |
| 59 |
pub access_flags: ChunkAccessFlags, |
| 60 |
|
| 61 |
/// Quarantine reason (if applicable) |
| 62 |
pub quarantine_reason: Option<String>, |
| 63 |
} |
| 64 |
|
| 65 |
/// Access control flags for chunk security |
| 66 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 67 |
pub struct ChunkAccessFlags { |
| 68 |
/// Can be read by storage operations |
| 69 |
pub readable: bool, |
| 70 |
|
| 71 |
/// Can be written/modified |
| 72 |
pub writable: bool, |
| 73 |
|
| 74 |
/// Can be transmitted over network |
| 75 |
pub transmittable: bool, |
| 76 |
|
| 77 |
/// Requires additional authentication |
| 78 |
pub requires_auth: bool, |
| 79 |
|
| 80 |
/// Under security monitoring |
| 81 |
pub monitored: bool, |
| 82 |
|
| 83 |
/// Marked for deletion |
| 84 |
pub marked_for_deletion: bool, |
| 85 |
} |
| 86 |
|
| 87 |
impl Default for ChunkAccessFlags { |
| 88 |
fn default() -> Self { |
| 89 |
Self { |
| 90 |
readable: true, |
| 91 |
writable: false, // Chunks are immutable by default |
| 92 |
transmittable: true, |
| 93 |
requires_auth: false, |
| 94 |
monitored: false, |
| 95 |
marked_for_deletion: false, |
| 96 |
} |
| 97 |
} |
| 98 |
} |
| 99 |
|
| 100 |
/// Per-chunk security manager with strict isolation |
| 101 |
pub struct ChunkSecurityManager { |
| 102 |
/// Isolated chunks store |
| 103 |
chunks: Arc<RwLock<HashMap<Uuid, IsolatedChunk>>>, |
| 104 |
|
| 105 |
/// Security event log |
| 106 |
security_log: Arc<RwLock<Vec<SecurityEvent>>>, |
| 107 |
|
| 108 |
/// Cryptographic random number generator |
| 109 |
rng: SystemRandom, |
| 110 |
|
| 111 |
/// Active quarantine rules |
| 112 |
quarantine_rules: Arc<RwLock<Vec<QuarantineRule>>>, |
| 113 |
} |
| 114 |
|
| 115 |
/// Security event for audit logging |
| 116 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 117 |
pub struct SecurityEvent { |
| 118 |
/// Event timestamp |
| 119 |
pub timestamp: u64, |
| 120 |
|
| 121 |
/// Event type |
| 122 |
pub event_type: SecurityEventType, |
| 123 |
|
| 124 |
/// Associated chunk ID |
| 125 |
pub chunk_id: Uuid, |
| 126 |
|
| 127 |
/// Event description |
| 128 |
pub description: String, |
| 129 |
|
| 130 |
/// Security level at time of event |
| 131 |
pub security_level: IsolationLevel, |
| 132 |
|
| 133 |
/// Additional metadata |
| 134 |
pub metadata: HashMap<String, String>, |
| 135 |
} |
| 136 |
|
| 137 |
/// Types of security events |
| 138 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 139 |
pub enum SecurityEventType { |
| 140 |
ChunkIsolated, |
| 141 |
SecurityUpgraded, |
| 142 |
AccessDenied, |
| 143 |
SuspiciousPattern, |
| 144 |
QuarantineTriggered, |
| 145 |
IntegrityViolation, |
| 146 |
UnauthorizedAccess, |
| 147 |
SecurityDowngraded, |
| 148 |
} |
| 149 |
|
| 150 |
/// Quarantine rule for automated threat response |
| 151 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 152 |
pub struct QuarantineRule { |
| 153 |
/// Rule identifier |
| 154 |
pub rule_id: String, |
| 155 |
|
| 156 |
/// Rule description |
| 157 |
pub description: String, |
| 158 |
|
| 159 |
/// Pattern to match (encrypted pattern) |
| 160 |
pub pattern: Vec<u8>, |
| 161 |
|
| 162 |
/// Action to take when triggered |
| 163 |
pub action: QuarantineAction, |
| 164 |
|
| 165 |
/// Rule priority (higher = more important) |
| 166 |
pub priority: u32, |
| 167 |
|
| 168 |
/// Whether rule is currently active |
| 169 |
pub active: bool, |
| 170 |
} |
| 171 |
|
| 172 |
/// Actions to take when quarantine is triggered |
| 173 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 174 |
pub enum QuarantineAction { |
| 175 |
/// Monitor the chunk closely |
| 176 |
Monitor, |
| 177 |
|
| 178 |
/// Enhance security isolation |
| 179 |
EnhanceIsolation, |
| 180 |
|
| 181 |
/// Quarantine immediately |
| 182 |
Quarantine, |
| 183 |
|
| 184 |
/// Block all access |
| 185 |
Block, |
| 186 |
|
| 187 |
/// Mark for deletion |
| 188 |
Delete, |
| 189 |
} |
| 190 |
|
| 191 |
impl ChunkSecurityManager { |
| 192 |
/// Create new chunk security manager |
| 193 |
pub fn new() -> Self { |
| 194 |
Self { |
| 195 |
chunks: Arc::new(RwLock::new(HashMap::new())), |
| 196 |
security_log: Arc::new(RwLock::new(Vec::new())), |
| 197 |
rng: SystemRandom::new(), |
| 198 |
quarantine_rules: Arc::new(RwLock::new(Self::default_quarantine_rules())), |
| 199 |
} |
| 200 |
} |
| 201 |
|
| 202 |
/// Create isolated chunk with unique security boundary |
| 203 |
pub async fn create_isolated_chunk( |
| 204 |
&self, |
| 205 |
encrypted_data: EncryptedData, |
| 206 |
isolation_level: IsolationLevel, |
| 207 |
) -> Result<IsolatedChunk> { |
| 208 |
let chunk_id = Uuid::new_v4(); |
| 209 |
|
| 210 |
// Generate unique key fingerprint |
| 211 |
let key_fingerprint = self.generate_key_fingerprint(&encrypted_data)?; |
| 212 |
|
| 213 |
// Calculate integrity hash |
| 214 |
let integrity_hash = self.calculate_integrity_hash(&encrypted_data)?; |
| 215 |
|
| 216 |
// Create access flags based on isolation level |
| 217 |
let access_flags = match isolation_level { |
| 218 |
IsolationLevel::Standard => ChunkAccessFlags::default(), |
| 219 |
IsolationLevel::Enhanced => ChunkAccessFlags { |
| 220 |
requires_auth: true, |
| 221 |
monitored: true, |
| 222 |
..ChunkAccessFlags::default() |
| 223 |
}, |
| 224 |
IsolationLevel::Quarantined => ChunkAccessFlags { |
| 225 |
readable: false, |
| 226 |
writable: false, |
| 227 |
transmittable: false, |
| 228 |
requires_auth: true, |
| 229 |
monitored: true, |
| 230 |
marked_for_deletion: false, |
| 231 |
}, |
| 232 |
}; |
| 233 |
|
| 234 |
let isolated_chunk = IsolatedChunk { |
| 235 |
chunk_id, |
| 236 |
encrypted_data, |
| 237 |
key_fingerprint, |
| 238 |
isolation_level, |
| 239 |
isolated_at: std::time::SystemTime::now() |
| 240 |
.duration_since(std::time::UNIX_EPOCH)? |
| 241 |
.as_secs(), |
| 242 |
security_metadata: vec![], // Will be populated based on analysis |
| 243 |
integrity_hash, |
| 244 |
access_flags, |
| 245 |
quarantine_reason: None, |
| 246 |
}; |
| 247 |
|
| 248 |
// Store the isolated chunk |
| 249 |
{ |
| 250 |
let mut chunks = self.chunks.write().await; |
| 251 |
chunks.insert(chunk_id, isolated_chunk.clone()); |
| 252 |
} |
| 253 |
|
| 254 |
// Log security event |
| 255 |
self.log_security_event(SecurityEvent { |
| 256 |
timestamp: isolated_chunk.isolated_at, |
| 257 |
event_type: SecurityEventType::ChunkIsolated, |
| 258 |
chunk_id, |
| 259 |
description: format!("Chunk isolated with level: {:?}", isolation_level), |
| 260 |
security_level: isolation_level, |
| 261 |
metadata: HashMap::new(), |
| 262 |
}).await; |
| 263 |
|
| 264 |
info!("Created isolated chunk {} with level {:?}", chunk_id, isolation_level); |
| 265 |
Ok(isolated_chunk) |
| 266 |
} |
| 267 |
|
| 268 |
/// Enforce security boundaries for chunk access |
| 269 |
pub async fn access_chunk(&self, chunk_id: Uuid, access_type: ChunkAccessType) -> Result<bool> { |
| 270 |
let chunks = self.chunks.read().await; |
| 271 |
let chunk = chunks.get(&chunk_id) |
| 272 |
.context("Chunk not found")?; |
| 273 |
|
| 274 |
let allowed = match access_type { |
| 275 |
ChunkAccessType::Read => chunk.access_flags.readable, |
| 276 |
ChunkAccessType::Write => chunk.access_flags.writable, |
| 277 |
ChunkAccessType::Transmit => chunk.access_flags.transmittable, |
| 278 |
}; |
| 279 |
|
| 280 |
if !allowed { |
| 281 |
// Log access denial |
| 282 |
self.log_security_event(SecurityEvent { |
| 283 |
timestamp: std::time::SystemTime::now() |
| 284 |
.duration_since(std::time::UNIX_EPOCH)? |
| 285 |
.as_secs(), |
| 286 |
event_type: SecurityEventType::AccessDenied, |
| 287 |
chunk_id, |
| 288 |
description: format!("Access denied for type: {:?}", access_type), |
| 289 |
security_level: chunk.isolation_level, |
| 290 |
metadata: HashMap::new(), |
| 291 |
}).await; |
| 292 |
|
| 293 |
warn!("Access denied for chunk {} (type: {:?})", chunk_id, access_type); |
| 294 |
} |
| 295 |
|
| 296 |
Ok(allowed) |
| 297 |
} |
| 298 |
|
| 299 |
/// Upgrade chunk security level |
| 300 |
pub async fn upgrade_security(&self, chunk_id: Uuid, new_level: IsolationLevel, reason: String) -> Result<()> { |
| 301 |
let mut chunks = self.chunks.write().await; |
| 302 |
let chunk = chunks.get_mut(&chunk_id) |
| 303 |
.context("Chunk not found")?; |
| 304 |
|
| 305 |
let old_level = chunk.isolation_level; |
| 306 |
|
| 307 |
// Only allow security upgrades, not downgrades (unless explicitly authorized) |
| 308 |
if (new_level as u32) < (old_level as u32) { |
| 309 |
warn!("Attempted security downgrade for chunk {}: {:?} -> {:?}", |
| 310 |
chunk_id, old_level, new_level); |
| 311 |
return Ok(()); |
| 312 |
} |
| 313 |
|
| 314 |
chunk.isolation_level = new_level; |
| 315 |
|
| 316 |
// Update access flags based on new level |
| 317 |
match new_level { |
| 318 |
IsolationLevel::Enhanced => { |
| 319 |
chunk.access_flags.requires_auth = true; |
| 320 |
chunk.access_flags.monitored = true; |
| 321 |
}, |
| 322 |
IsolationLevel::Quarantined => { |
| 323 |
chunk.access_flags.readable = false; |
| 324 |
chunk.access_flags.writable = false; |
| 325 |
chunk.access_flags.transmittable = false; |
| 326 |
chunk.access_flags.requires_auth = true; |
| 327 |
chunk.access_flags.monitored = true; |
| 328 |
chunk.quarantine_reason = Some(reason.clone()); |
| 329 |
}, |
| 330 |
_ => {} |
| 331 |
} |
| 332 |
|
| 333 |
// Log security upgrade |
| 334 |
self.log_security_event(SecurityEvent { |
| 335 |
timestamp: std::time::SystemTime::now() |
| 336 |
.duration_since(std::time::UNIX_EPOCH)? |
| 337 |
.as_secs(), |
| 338 |
event_type: SecurityEventType::SecurityUpgraded, |
| 339 |
chunk_id, |
| 340 |
description: format!("Security upgraded from {:?} to {:?}: {}", old_level, new_level, reason), |
| 341 |
security_level: new_level, |
| 342 |
metadata: HashMap::new(), |
| 343 |
}).await; |
| 344 |
|
| 345 |
info!("Upgraded security for chunk {} from {:?} to {:?}", chunk_id, old_level, new_level); |
| 346 |
Ok(()) |
| 347 |
} |
| 348 |
|
| 349 |
/// Analyze chunk for suspicious patterns (content-blind) |
| 350 |
pub async fn analyze_chunk_security(&self, chunk_id: Uuid) -> Result<SecurityAnalysis> { |
| 351 |
let chunks = self.chunks.read().await; |
| 352 |
let chunk = chunks.get(&chunk_id) |
| 353 |
.context("Chunk not found")?; |
| 354 |
|
| 355 |
let mut analysis = SecurityAnalysis { |
| 356 |
chunk_id, |
| 357 |
threat_level: ThreatLevel::Low, |
| 358 |
suspicious_indicators: Vec::new(), |
| 359 |
recommendations: Vec::new(), |
| 360 |
}; |
| 361 |
|
| 362 |
// Analyze encrypted data patterns (zero-knowledge analysis) |
| 363 |
let encrypted_data = &chunk.encrypted_data.ciphertext; |
| 364 |
|
| 365 |
// 1. Check for unusual size patterns |
| 366 |
if encrypted_data.len() > 100 * 1024 * 1024 { |
| 367 |
analysis.suspicious_indicators.push("Unusually large chunk size".to_string()); |
| 368 |
analysis.threat_level = ThreatLevel::Medium; |
| 369 |
} |
| 370 |
|
| 371 |
// 2. Check encryption entropy (encrypted data should be high entropy) |
| 372 |
let entropy = self.calculate_entropy(&encrypted_data); |
| 373 |
if entropy < 7.5 { |
| 374 |
analysis.suspicious_indicators.push("Low entropy in encrypted data".to_string()); |
| 375 |
analysis.threat_level = ThreatLevel::High; |
| 376 |
analysis.recommendations.push("Quarantine immediately".to_string()); |
| 377 |
} |
| 378 |
|
| 379 |
// 3. Check for pattern repetition (even in encrypted form) |
| 380 |
if self.detect_pattern_repetition(&encrypted_data) { |
| 381 |
analysis.suspicious_indicators.push("Suspicious pattern repetition".to_string()); |
| 382 |
analysis.threat_level = ThreatLevel::Medium; |
| 383 |
} |
| 384 |
|
| 385 |
// 4. Check against known bad patterns |
| 386 |
if self.check_against_quarantine_rules(&chunk).await { |
| 387 |
analysis.suspicious_indicators.push("Matches quarantine rule".to_string()); |
| 388 |
analysis.threat_level = ThreatLevel::Critical; |
| 389 |
analysis.recommendations.push("Immediate quarantine required".to_string()); |
| 390 |
} |
| 391 |
|
| 392 |
// Take action based on threat level |
| 393 |
match analysis.threat_level { |
| 394 |
ThreatLevel::Medium => { |
| 395 |
self.upgrade_security(chunk_id, IsolationLevel::Enhanced, |
| 396 |
"Automated security analysis".to_string()).await?; |
| 397 |
}, |
| 398 |
ThreatLevel::High | ThreatLevel::Critical => { |
| 399 |
self.upgrade_security(chunk_id, IsolationLevel::Quarantined, |
| 400 |
format!("High threat detected: {:?}", analysis.suspicious_indicators)).await?; |
| 401 |
}, |
| 402 |
_ => {} |
| 403 |
} |
| 404 |
|
| 405 |
Ok(analysis) |
| 406 |
} |
| 407 |
|
| 408 |
/// Generate unique key fingerprint without exposing the key |
| 409 |
fn generate_key_fingerprint(&self, encrypted_data: &EncryptedData) -> Result<String> { |
| 410 |
let mut context = Vec::new(); |
| 411 |
context.extend_from_slice(&encrypted_data.nonce); |
| 412 |
context.extend_from_slice(&encrypted_data.aad); |
| 413 |
context.extend_from_slice(&encrypted_data.key_path.iter() |
| 414 |
.flat_map(|&x| x.to_le_bytes().to_vec()).collect::<Vec<_>>()); |
| 415 |
|
| 416 |
let hash = digest(&SHA256, &context); |
| 417 |
Ok(hex::encode(hash.as_ref())) |
| 418 |
} |
| 419 |
|
| 420 |
/// Calculate integrity hash of encrypted data |
| 421 |
fn calculate_integrity_hash(&self, encrypted_data: &EncryptedData) -> Result<String> { |
| 422 |
let mut data = Vec::new(); |
| 423 |
data.extend_from_slice(&encrypted_data.ciphertext); |
| 424 |
data.extend_from_slice(&encrypted_data.nonce); |
| 425 |
data.extend_from_slice(&encrypted_data.aad); |
| 426 |
|
| 427 |
let hash = digest(&SHA256, &data); |
| 428 |
Ok(hex::encode(hash.as_ref())) |
| 429 |
} |
| 430 |
|
| 431 |
/// Calculate entropy of data (for encrypted data analysis) |
| 432 |
fn calculate_entropy(&self, data: &[u8]) -> f64 { |
| 433 |
let mut freq = [0u32; 256]; |
| 434 |
for &byte in data { |
| 435 |
freq[byte as usize] += 1; |
| 436 |
} |
| 437 |
|
| 438 |
let len = data.len() as f64; |
| 439 |
let mut entropy = 0.0; |
| 440 |
|
| 441 |
for &count in &freq { |
| 442 |
if count > 0 { |
| 443 |
let p = count as f64 / len; |
| 444 |
entropy -= p * p.log2(); |
| 445 |
} |
| 446 |
} |
| 447 |
|
| 448 |
entropy |
| 449 |
} |
| 450 |
|
| 451 |
/// Detect suspicious pattern repetition in encrypted data |
| 452 |
fn detect_pattern_repetition(&self, data: &[u8]) -> bool { |
| 453 |
if data.len() < 32 { |
| 454 |
return false; |
| 455 |
} |
| 456 |
|
| 457 |
// Check for repeated 16-byte blocks (suspicious in properly encrypted data) |
| 458 |
let mut blocks = HashMap::new(); |
| 459 |
for chunk in data.chunks(16) { |
| 460 |
if chunk.len() == 16 { |
| 461 |
let count = blocks.entry(chunk.to_vec()).or_insert(0); |
| 462 |
*count += 1; |
| 463 |
if *count > 3 { |
| 464 |
return true; // Too many repetitions |
| 465 |
} |
| 466 |
} |
| 467 |
} |
| 468 |
|
| 469 |
false |
| 470 |
} |
| 471 |
|
| 472 |
/// Check chunk against active quarantine rules |
| 473 |
async fn check_against_quarantine_rules(&self, chunk: &IsolatedChunk) -> bool { |
| 474 |
let rules = self.quarantine_rules.read().await; |
| 475 |
|
| 476 |
for rule in rules.iter().filter(|r| r.active) { |
| 477 |
// Pattern matching on encrypted data |
| 478 |
if !rule.pattern.is_empty() { |
| 479 |
if chunk.encrypted_data.ciphertext.windows(rule.pattern.len()) |
| 480 |
.any(|window| window == rule.pattern) { |
| 481 |
return true; |
| 482 |
} |
| 483 |
} |
| 484 |
} |
| 485 |
|
| 486 |
false |
| 487 |
} |
| 488 |
|
| 489 |
/// Log security event |
| 490 |
async fn log_security_event(&self, event: SecurityEvent) { |
| 491 |
let mut log = self.security_log.write().await; |
| 492 |
log.push(event); |
| 493 |
|
| 494 |
// Keep log size manageable |
| 495 |
if log.len() > 10000 { |
| 496 |
log.drain(0..1000); |
| 497 |
} |
| 498 |
} |
| 499 |
|
| 500 |
/// Get security events for audit |
| 501 |
pub async fn get_security_events(&self, chunk_id: Option<Uuid>) -> Vec<SecurityEvent> { |
| 502 |
let log = self.security_log.read().await; |
| 503 |
|
| 504 |
match chunk_id { |
| 505 |
Some(id) => log.iter().filter(|e| e.chunk_id == id).cloned().collect(), |
| 506 |
None => log.clone(), |
| 507 |
} |
| 508 |
} |
| 509 |
|
| 510 |
/// Default quarantine rules for automated threat detection |
| 511 |
fn default_quarantine_rules() -> Vec<QuarantineRule> { |
| 512 |
vec![ |
| 513 |
QuarantineRule { |
| 514 |
rule_id: "entropy_check".to_string(), |
| 515 |
description: "Detect low entropy patterns".to_string(), |
| 516 |
pattern: vec![], // Handled by entropy analysis |
| 517 |
action: QuarantineAction::Quarantine, |
| 518 |
priority: 100, |
| 519 |
active: true, |
| 520 |
}, |
| 521 |
QuarantineRule { |
| 522 |
rule_id: "size_limit".to_string(), |
| 523 |
description: "Block oversized chunks".to_string(), |
| 524 |
pattern: vec![], // Handled by size analysis |
| 525 |
action: QuarantineAction::EnhanceIsolation, |
| 526 |
priority: 50, |
| 527 |
active: true, |
| 528 |
}, |
| 529 |
] |
| 530 |
} |
| 531 |
|
| 532 |
/// Get status of a chunk |
| 533 |
pub async fn get_chunk_status(&self, chunk_id: Uuid) -> Result<ChunkStatus> { |
| 534 |
let chunks = self.chunks.read().await; |
| 535 |
if let Some(chunk) = chunks.get(&chunk_id) { |
| 536 |
Ok(ChunkStatus { |
| 537 |
chunk_id, |
| 538 |
isolation_level: chunk.isolation_level, |
| 539 |
access_flags: ChunkAccessFlags { |
| 540 |
readable: true, |
| 541 |
writable: false, |
| 542 |
transmittable: true, |
| 543 |
requires_auth: false, |
| 544 |
monitored: true, |
| 545 |
marked_for_deletion: false, |
| 546 |
}, |
| 547 |
access_count: 0, // TODO: Track access count |
| 548 |
last_accessed: Utc::now(), |
| 549 |
last_update: std::time::SystemTime::now() |
| 550 |
.duration_since(std::time::UNIX_EPOCH) |
| 551 |
.unwrap_or_default() |
| 552 |
.as_secs(), |
| 553 |
security_events: vec![], // TODO: Implement event tracking |
| 554 |
is_quarantined: chunk.isolation_level == IsolationLevel::Quarantined, |
| 555 |
}) |
| 556 |
} else { |
| 557 |
Err(anyhow::anyhow!("Chunk not found: {}", chunk_id)) |
| 558 |
} |
| 559 |
} |
| 560 |
|
| 561 |
/// Verify access permissions for a chunk |
| 562 |
pub async fn verify_access(&self, chunk_id: Uuid, _access_type: ChunkAccessType) -> Result<bool> { |
| 563 |
let chunks = self.chunks.read().await; |
| 564 |
Ok(chunks.contains_key(&chunk_id)) |
| 565 |
} |
| 566 |
|
| 567 |
/// Update configuration |
| 568 |
pub fn update_config(&mut self, _config: IsolationConfig) -> Result<()> { |
| 569 |
// TODO: Implement configuration updates |
| 570 |
Ok(()) |
| 571 |
} |
| 572 |
} |
| 573 |
|
| 574 |
/// Types of chunk access |
| 575 |
#[derive(Debug, Clone, Copy)] |
| 576 |
pub enum ChunkAccessType { |
| 577 |
Read, |
| 578 |
Write, |
| 579 |
Transmit, |
| 580 |
} |
| 581 |
|
| 582 |
/// Security analysis result |
| 583 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 584 |
pub struct SecurityAnalysis { |
| 585 |
pub chunk_id: Uuid, |
| 586 |
pub threat_level: ThreatLevel, |
| 587 |
pub suspicious_indicators: Vec<String>, |
| 588 |
pub recommendations: Vec<String>, |
| 589 |
} |
| 590 |
|
| 591 |
/// Threat level assessment |
| 592 |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] |
| 593 |
pub enum ThreatLevel { |
| 594 |
None, |
| 595 |
Low, |
| 596 |
Medium, |
| 597 |
High, |
| 598 |
Critical, |
| 599 |
} |
| 600 |
|
| 601 |
#[cfg(test)] |
| 602 |
mod tests { |
| 603 |
use super::*; |
| 604 |
use crate::crypto::EncryptedData; |
| 605 |
|
| 606 |
#[tokio::test] |
| 607 |
async fn test_chunk_isolation() { |
| 608 |
let manager = ChunkSecurityManager::new(); |
| 609 |
|
| 610 |
let encrypted_data = EncryptedData { |
| 611 |
segment_index: 0, |
| 612 |
ciphertext: vec![1, 2, 3, 4, 5], |
| 613 |
nonce: [0; 12], |
| 614 |
aad: vec![], |
| 615 |
key_path: vec![0, 1, 2], |
| 616 |
}; |
| 617 |
|
| 618 |
let chunk = manager.create_isolated_chunk(encrypted_data, IsolationLevel::Standard).await.unwrap(); |
| 619 |
|
| 620 |
assert_eq!(chunk.isolation_level, IsolationLevel::Standard); |
| 621 |
assert!(chunk.access_flags.readable); |
| 622 |
assert!(!chunk.access_flags.writable); |
| 623 |
} |
| 624 |
|
| 625 |
#[tokio::test] |
| 626 |
async fn test_security_upgrade() { |
| 627 |
let manager = ChunkSecurityManager::new(); |
| 628 |
|
| 629 |
let encrypted_data = EncryptedData { |
| 630 |
segment_index: 0, |
| 631 |
ciphertext: vec![1, 2, 3, 4, 5], |
| 632 |
nonce: [0; 12], |
| 633 |
aad: vec![], |
| 634 |
key_path: vec![0, 1, 2], |
| 635 |
}; |
| 636 |
|
| 637 |
let chunk = manager.create_isolated_chunk(encrypted_data, IsolationLevel::Standard).await.unwrap(); |
| 638 |
|
| 639 |
manager.upgrade_security(chunk.chunk_id, IsolationLevel::Enhanced, |
| 640 |
"Test upgrade".to_string()).await.unwrap(); |
| 641 |
|
| 642 |
let access_allowed = manager.access_chunk(chunk.chunk_id, ChunkAccessType::Read).await.unwrap(); |
| 643 |
assert!(access_allowed); |
| 644 |
} |
| 645 |
} |
| 646 |
|
| 647 |
/// Configuration for chunk isolation system |
| 648 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 649 |
pub struct IsolationConfig { |
| 650 |
/// Default isolation level for new chunks |
| 651 |
pub default_isolation_level: IsolationLevel, |
| 652 |
/// Maximum number of chunks that can be quarantined |
| 653 |
pub max_quarantined_chunks: usize, |
| 654 |
/// Enable automatic threat detection |
| 655 |
pub enable_threat_detection: bool, |
| 656 |
/// Security event retention period in hours |
| 657 |
pub security_event_retention_hours: u32, |
| 658 |
} |
| 659 |
|
| 660 |
impl Default for IsolationConfig { |
| 661 |
fn default() -> Self { |
| 662 |
Self { |
| 663 |
default_isolation_level: IsolationLevel::Standard, |
| 664 |
max_quarantined_chunks: 1000, |
| 665 |
enable_threat_detection: true, |
| 666 |
security_event_retention_hours: 24 * 7, // 1 week |
| 667 |
} |
| 668 |
} |
| 669 |
} |
| 670 |
|
| 671 |
/// Status of a chunk in the security system |
| 672 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 673 |
pub struct ChunkStatus { |
| 674 |
pub chunk_id: Uuid, |
| 675 |
pub isolation_level: IsolationLevel, |
| 676 |
pub access_flags: ChunkAccessFlags, |
| 677 |
pub access_count: u64, |
| 678 |
pub last_accessed: DateTime<Utc>, |
| 679 |
pub last_update: u64, |
| 680 |
pub security_events: Vec<SecurityEvent>, |
| 681 |
pub is_quarantined: bool, |
| 682 |
} |