| 1 |
//! Security module for ZephyrFS |
| 2 |
//! |
| 3 |
//! Provides comprehensive security features including chunk isolation, |
| 4 |
//! malicious content detection, and military-grade security boundaries. |
| 5 |
|
| 6 |
pub mod chunk_isolation; |
| 7 |
pub mod malicious_detection; |
| 8 |
|
| 9 |
pub use chunk_isolation::{ |
| 10 |
ChunkSecurityManager, IsolatedChunk, IsolationLevel, ChunkAccessFlags, SecurityEvent, |
| 11 |
IsolationConfig, ChunkStatus, ThreatLevel |
| 12 |
}; |
| 13 |
pub use malicious_detection::{ |
| 14 |
MaliciousContentDetector, ThreatAnalysisResult, QuarantineManager, QuarantineStats, |
| 15 |
ThreatIndicator, DetectionConfig, QuarantineStatus |
| 16 |
}; |
| 17 |
|
| 18 |
use anyhow::Result; |
| 19 |
use serde::{Deserialize, Serialize}; |
| 20 |
use std::collections::HashMap; |
| 21 |
use uuid::Uuid; |
| 22 |
use crate::crypto::EncryptedData; |
| 23 |
|
| 24 |
/// Security configuration for the entire system |
| 25 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 26 |
pub struct SecurityConfig { |
| 27 |
/// Chunk isolation configuration |
| 28 |
pub isolation_config: chunk_isolation::IsolationConfig, |
| 29 |
/// Malicious content detection configuration |
| 30 |
pub detection_config: malicious_detection::DetectionConfig, |
| 31 |
/// Global security policies |
| 32 |
pub global_policies: GlobalSecurityPolicies, |
| 33 |
} |
| 34 |
|
| 35 |
/// Global security policies |
| 36 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 37 |
pub struct GlobalSecurityPolicies { |
| 38 |
/// Minimum isolation level for new chunks |
| 39 |
pub minimum_isolation_level: IsolationLevel, |
| 40 |
/// Automatic quarantine on threat detection |
| 41 |
pub auto_quarantine_enabled: bool, |
| 42 |
/// Maximum threat level before immediate quarantine |
| 43 |
pub quarantine_threshold: ThreatLevel, |
| 44 |
/// Security audit logging enabled |
| 45 |
pub audit_logging_enabled: bool, |
| 46 |
/// Zero-knowledge enforcement |
| 47 |
pub zero_knowledge_enforced: bool, |
| 48 |
} |
| 49 |
|
| 50 |
impl Default for SecurityConfig { |
| 51 |
fn default() -> Self { |
| 52 |
Self { |
| 53 |
isolation_config: chunk_isolation::IsolationConfig::default(), |
| 54 |
detection_config: malicious_detection::DetectionConfig::default(), |
| 55 |
global_policies: GlobalSecurityPolicies { |
| 56 |
minimum_isolation_level: IsolationLevel::Standard, |
| 57 |
auto_quarantine_enabled: true, |
| 58 |
quarantine_threshold: ThreatLevel::Medium, |
| 59 |
audit_logging_enabled: true, |
| 60 |
zero_knowledge_enforced: true, |
| 61 |
}, |
| 62 |
} |
| 63 |
} |
| 64 |
} |
| 65 |
|
| 66 |
/// Unified security manager combining all security systems |
| 67 |
pub struct UnifiedSecurityManager { |
| 68 |
chunk_security: ChunkSecurityManager, |
| 69 |
threat_detector: MaliciousContentDetector, |
| 70 |
quarantine_manager: QuarantineManager, |
| 71 |
config: SecurityConfig, |
| 72 |
} |
| 73 |
|
| 74 |
impl UnifiedSecurityManager { |
| 75 |
/// Create new unified security manager |
| 76 |
pub fn new(config: SecurityConfig) -> Result<Self> { |
| 77 |
let chunk_security = ChunkSecurityManager::new(); |
| 78 |
let threat_detector = MaliciousContentDetector::new(); |
| 79 |
let quarantine_manager = QuarantineManager::new(); |
| 80 |
|
| 81 |
Ok(Self { |
| 82 |
chunk_security, |
| 83 |
threat_detector, |
| 84 |
quarantine_manager, |
| 85 |
config, |
| 86 |
}) |
| 87 |
} |
| 88 |
|
| 89 |
/// Process new chunk with comprehensive security analysis |
| 90 |
pub async fn process_new_chunk( |
| 91 |
&mut self, |
| 92 |
chunk_id: Uuid, |
| 93 |
encrypted_data: &[u8], |
| 94 |
metadata: HashMap<String, String>, |
| 95 |
) -> Result<ChunkSecurityDecision> { |
| 96 |
// Step 1: Create encrypted data struct with production-ready crypto |
| 97 |
use rand::RngCore; |
| 98 |
let mut rng = rand::thread_rng(); |
| 99 |
let mut nonce = [0u8; 12]; |
| 100 |
rng.fill_bytes(&mut nonce); |
| 101 |
|
| 102 |
let encrypted_data_struct = EncryptedData { |
| 103 |
segment_index: 0, |
| 104 |
ciphertext: encrypted_data.to_vec(), |
| 105 |
nonce, // Cryptographically secure random nonce |
| 106 |
aad: Vec::new(), |
| 107 |
key_path: vec![chunk_id.as_u128() as u32], // Derive from chunk ID |
| 108 |
}; |
| 109 |
|
| 110 |
// Step 2: Initial threat analysis |
| 111 |
let threat_analysis = self.threat_detector |
| 112 |
.analyze_content(&encrypted_data_struct, &metadata) |
| 113 |
.await?; |
| 114 |
|
| 115 |
// Step 3: Determine isolation level based on threat analysis |
| 116 |
let isolation_level = self.determine_isolation_level(&threat_analysis); |
| 117 |
|
| 118 |
// Step 4: Create isolated chunk |
| 119 |
let isolated_chunk = self.chunk_security.create_isolated_chunk( |
| 120 |
encrypted_data_struct, |
| 121 |
isolation_level, |
| 122 |
).await?; |
| 123 |
|
| 124 |
// Step 4: Check if quarantine is needed |
| 125 |
let quarantine_decision = if threat_analysis.overall_threat_level >= self.config.global_policies.quarantine_threshold { |
| 126 |
self.quarantine_manager.quarantine_chunk( |
| 127 |
chunk_id, |
| 128 |
format!("Threat detected: {:?}", threat_analysis.recommended_actions), |
| 129 |
threat_analysis.overall_threat_level, |
| 130 |
true, // automated |
| 131 |
).await?; |
| 132 |
QuarantineDecision::Quarantined |
| 133 |
} else { |
| 134 |
QuarantineDecision::Allowed |
| 135 |
}; |
| 136 |
|
| 137 |
Ok(ChunkSecurityDecision { |
| 138 |
chunk_id, |
| 139 |
isolation_level, |
| 140 |
threat_analysis, |
| 141 |
quarantine_decision, |
| 142 |
security_clearance: self.calculate_security_clearance(&threat_analysis, isolation_level), |
| 143 |
}) |
| 144 |
} |
| 145 |
|
| 146 |
/// Verify chunk access with security checks |
| 147 |
pub async fn verify_chunk_access( |
| 148 |
&self, |
| 149 |
chunk_id: Uuid, |
| 150 |
requested_access: ChunkAccessFlags, |
| 151 |
requester_context: AccessContext, |
| 152 |
) -> Result<AccessDecision> { |
| 153 |
// Get chunk security status |
| 154 |
let chunk_status = self.chunk_security.get_chunk_status(chunk_id).await?; |
| 155 |
|
| 156 |
// Check if chunk is quarantined |
| 157 |
if self.quarantine_manager.is_quarantined(chunk_id).await? { |
| 158 |
return Ok(AccessDecision::Denied { |
| 159 |
reason: "Chunk is quarantined".to_string(), |
| 160 |
}); |
| 161 |
} |
| 162 |
|
| 163 |
// Convert ChunkAccessFlags to ChunkAccessType based on flags |
| 164 |
let access_type = if requested_access.writable { |
| 165 |
chunk_isolation::ChunkAccessType::Write |
| 166 |
} else if requested_access.transmittable { |
| 167 |
chunk_isolation::ChunkAccessType::Transmit |
| 168 |
} else { |
| 169 |
chunk_isolation::ChunkAccessType::Read // Default for readable or other cases |
| 170 |
}; |
| 171 |
|
| 172 |
// Verify access permissions based on isolation level |
| 173 |
let access_allowed = self.chunk_security.verify_access( |
| 174 |
chunk_id, |
| 175 |
access_type, |
| 176 |
).await?; |
| 177 |
|
| 178 |
if access_allowed { |
| 179 |
Ok(AccessDecision::Granted { |
| 180 |
conditions: self.get_access_conditions(&chunk_status, &requester_context), |
| 181 |
}) |
| 182 |
} else { |
| 183 |
Ok(AccessDecision::Denied { |
| 184 |
reason: "Insufficient security clearance".to_string(), |
| 185 |
}) |
| 186 |
} |
| 187 |
} |
| 188 |
|
| 189 |
/// Get comprehensive security status for a chunk |
| 190 |
pub async fn get_chunk_security_status(&self, chunk_id: Uuid) -> Result<ChunkSecurityStatus> { |
| 191 |
let chunk_status = self.chunk_security.get_chunk_status(chunk_id).await?; |
| 192 |
let quarantine_status = self.quarantine_manager.get_quarantine_status(chunk_id).await?; |
| 193 |
let threat_history = self.threat_detector.get_threat_history(chunk_id).await; |
| 194 |
|
| 195 |
Ok(ChunkSecurityStatus { |
| 196 |
chunk_id, |
| 197 |
isolation_level: chunk_status.isolation_level, |
| 198 |
access_flags: chunk_status.access_flags, |
| 199 |
quarantine_status, |
| 200 |
threat_history, |
| 201 |
last_security_scan: chunk_status.last_update, |
| 202 |
security_score: self.calculate_security_score(&chunk_status, &quarantine_status), |
| 203 |
}) |
| 204 |
} |
| 205 |
|
| 206 |
/// Update security policies |
| 207 |
pub fn update_policies(&mut self, new_policies: GlobalSecurityPolicies) -> Result<()> { |
| 208 |
self.config.global_policies = new_policies; |
| 209 |
self.chunk_security.update_config(self.config.isolation_config.clone())?; |
| 210 |
self.threat_detector.update_config(self.config.detection_config.clone())?; |
| 211 |
Ok(()) |
| 212 |
} |
| 213 |
|
| 214 |
/// Determine appropriate isolation level based on threat analysis |
| 215 |
fn determine_isolation_level(&self, analysis: &ThreatAnalysisResult) -> IsolationLevel { |
| 216 |
match analysis.overall_threat_level { |
| 217 |
ThreatLevel::None | ThreatLevel::Low => { |
| 218 |
if self.config.global_policies.minimum_isolation_level > IsolationLevel::Standard { |
| 219 |
self.config.global_policies.minimum_isolation_level |
| 220 |
} else { |
| 221 |
IsolationLevel::Standard |
| 222 |
} |
| 223 |
} |
| 224 |
ThreatLevel::Medium => IsolationLevel::Enhanced, |
| 225 |
ThreatLevel::High | ThreatLevel::Critical => IsolationLevel::Quarantined, |
| 226 |
} |
| 227 |
} |
| 228 |
|
| 229 |
/// Calculate security clearance level |
| 230 |
fn calculate_security_clearance( |
| 231 |
&self, |
| 232 |
analysis: &ThreatAnalysisResult, |
| 233 |
isolation_level: IsolationLevel, |
| 234 |
) -> SecurityClearance { |
| 235 |
match (analysis.overall_threat_level, isolation_level) { |
| 236 |
(ThreatLevel::None, IsolationLevel::Standard) => SecurityClearance::Public, |
| 237 |
(ThreatLevel::Low, IsolationLevel::Standard) | |
| 238 |
(ThreatLevel::None, IsolationLevel::Enhanced) => SecurityClearance::Internal, |
| 239 |
(ThreatLevel::Medium, _) | |
| 240 |
(ThreatLevel::Low, IsolationLevel::Enhanced) => SecurityClearance::Restricted, |
| 241 |
(ThreatLevel::High, _) => SecurityClearance::Confidential, |
| 242 |
(ThreatLevel::Critical, _) => SecurityClearance::TopSecret, |
| 243 |
_ => SecurityClearance::Internal, |
| 244 |
} |
| 245 |
} |
| 246 |
|
| 247 |
/// Get access conditions based on security status |
| 248 |
fn get_access_conditions( |
| 249 |
&self, |
| 250 |
_chunk_status: &chunk_isolation::ChunkStatus, |
| 251 |
_requester_context: &AccessContext, |
| 252 |
) -> Vec<AccessCondition> { |
| 253 |
// Return appropriate access conditions |
| 254 |
vec![AccessCondition::AuditLogging, AccessCondition::RateLimit] |
| 255 |
} |
| 256 |
|
| 257 |
/// Calculate overall security score for a chunk |
| 258 |
fn calculate_security_score( |
| 259 |
&self, |
| 260 |
chunk_status: &chunk_isolation::ChunkStatus, |
| 261 |
quarantine_status: &malicious_detection::QuarantineStatus, |
| 262 |
) -> f64 { |
| 263 |
let isolation_score = match chunk_status.isolation_level { |
| 264 |
IsolationLevel::Standard => 0.6, |
| 265 |
IsolationLevel::Enhanced => 0.8, |
| 266 |
IsolationLevel::Quarantined => 0.4, // Lower because it indicates detected threats |
| 267 |
}; |
| 268 |
|
| 269 |
let quarantine_penalty = if quarantine_status.is_quarantined { 0.2 } else { 0.0 }; |
| 270 |
|
| 271 |
(isolation_score - quarantine_penalty).max(0.0).min(1.0) |
| 272 |
} |
| 273 |
} |
| 274 |
|
| 275 |
/// Decision result for chunk security processing |
| 276 |
#[derive(Debug, Clone)] |
| 277 |
pub struct ChunkSecurityDecision { |
| 278 |
pub chunk_id: Uuid, |
| 279 |
pub isolation_level: IsolationLevel, |
| 280 |
pub threat_analysis: ThreatAnalysisResult, |
| 281 |
pub quarantine_decision: QuarantineDecision, |
| 282 |
pub security_clearance: SecurityClearance, |
| 283 |
} |
| 284 |
|
| 285 |
/// Quarantine decision |
| 286 |
#[derive(Debug, Clone)] |
| 287 |
pub enum QuarantineDecision { |
| 288 |
Allowed, |
| 289 |
Quarantined, |
| 290 |
PendingReview, |
| 291 |
} |
| 292 |
|
| 293 |
/// Security clearance levels |
| 294 |
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
| 295 |
pub enum SecurityClearance { |
| 296 |
Public, |
| 297 |
Internal, |
| 298 |
Restricted, |
| 299 |
Confidential, |
| 300 |
Secret, |
| 301 |
TopSecret, |
| 302 |
} |
| 303 |
|
| 304 |
/// Access decision result |
| 305 |
#[derive(Debug, Clone)] |
| 306 |
pub enum AccessDecision { |
| 307 |
Granted { conditions: Vec<AccessCondition> }, |
| 308 |
Denied { reason: String }, |
| 309 |
} |
| 310 |
|
| 311 |
/// Conditions for chunk access |
| 312 |
#[derive(Debug, Clone)] |
| 313 |
pub enum AccessCondition { |
| 314 |
AuditLogging, |
| 315 |
RateLimit, |
| 316 |
TimeRestriction, |
| 317 |
LocationRestriction, |
| 318 |
AdditionalAuthentication, |
| 319 |
} |
| 320 |
|
| 321 |
/// Context for access requests |
| 322 |
#[derive(Debug, Clone)] |
| 323 |
pub struct AccessContext { |
| 324 |
pub requester_id: String, |
| 325 |
pub security_clearance: SecurityClearance, |
| 326 |
pub request_timestamp: u64, |
| 327 |
pub request_origin: String, |
| 328 |
pub additional_context: HashMap<String, String>, |
| 329 |
} |
| 330 |
|
| 331 |
/// Comprehensive security status for a chunk |
| 332 |
#[derive(Debug, Clone)] |
| 333 |
pub struct ChunkSecurityStatus { |
| 334 |
pub chunk_id: Uuid, |
| 335 |
pub isolation_level: IsolationLevel, |
| 336 |
pub access_flags: ChunkAccessFlags, |
| 337 |
pub quarantine_status: malicious_detection::QuarantineStatus, |
| 338 |
pub threat_history: Vec<ThreatAnalysisResult>, |
| 339 |
pub last_security_scan: u64, |
| 340 |
pub security_score: f64, |
| 341 |
} |
| 342 |
|
| 343 |
#[cfg(test)] |
| 344 |
mod tests { |
| 345 |
use super::*; |
| 346 |
|
| 347 |
#[tokio::test] |
| 348 |
async fn test_unified_security_manager() -> Result<()> { |
| 349 |
let config = SecurityConfig::default(); |
| 350 |
let mut security_manager = UnifiedSecurityManager::new(config)?; |
| 351 |
|
| 352 |
let chunk_id = Uuid::new_v4(); |
| 353 |
let test_data = b"Test encrypted chunk data"; |
| 354 |
let metadata = HashMap::new(); |
| 355 |
|
| 356 |
let decision = security_manager |
| 357 |
.process_new_chunk(chunk_id, test_data, metadata) |
| 358 |
.await?; |
| 359 |
|
| 360 |
assert_eq!(decision.chunk_id, chunk_id); |
| 361 |
assert!(matches!(decision.quarantine_decision, QuarantineDecision::Allowed)); |
| 362 |
|
| 363 |
Ok(()) |
| 364 |
} |
| 365 |
|
| 366 |
#[tokio::test] |
| 367 |
async fn test_chunk_access_verification() -> Result<()> { |
| 368 |
let config = SecurityConfig::default(); |
| 369 |
let security_manager = UnifiedSecurityManager::new(config)?; |
| 370 |
|
| 371 |
let chunk_id = Uuid::new_v4(); |
| 372 |
let access_context = AccessContext { |
| 373 |
requester_id: "test-user".to_string(), |
| 374 |
security_clearance: SecurityClearance::Internal, |
| 375 |
request_timestamp: 1234567890, |
| 376 |
request_origin: "localhost".to_string(), |
| 377 |
additional_context: HashMap::new(), |
| 378 |
}; |
| 379 |
|
| 380 |
let access_decision = security_manager |
| 381 |
.verify_chunk_access(chunk_id, ChunkAccessFlags::READ, access_context) |
| 382 |
.await?; |
| 383 |
|
| 384 |
// Should deny access for non-existent chunk |
| 385 |
assert!(matches!(access_decision, AccessDecision::Denied { .. })); |
| 386 |
|
| 387 |
Ok(()) |
| 388 |
} |
| 389 |
} |