| 1 |
//! Content integrity verification system for ZephyrFS |
| 2 |
//! |
| 3 |
//! Provides content-blind verification of data integrity using cryptographic proofs |
| 4 |
//! and mathematical verification techniques without accessing plaintext content. |
| 5 |
|
| 6 |
use anyhow::{Context, Result}; |
| 7 |
use ring::digest::{Context as DigestContext, SHA256, SHA512}; |
| 8 |
use serde::{Deserialize, Serialize}; |
| 9 |
use std::collections::HashMap; |
| 10 |
use std::time::{SystemTime, UNIX_EPOCH}; |
| 11 |
use uuid::Uuid; |
| 12 |
|
| 13 |
/// Integrity verification configuration |
| 14 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 15 |
pub struct IntegrityConfig { |
| 16 |
/// Enable cryptographic hash verification |
| 17 |
pub hash_verification: bool, |
| 18 |
/// Enable mathematical proof verification |
| 19 |
pub proof_verification: bool, |
| 20 |
/// Enable temporal integrity checking |
| 21 |
pub temporal_verification: bool, |
| 22 |
/// Maximum allowed age for integrity proofs (seconds) |
| 23 |
pub max_proof_age: u64, |
| 24 |
/// Required verification confidence level (0.0-1.0) |
| 25 |
pub confidence_threshold: f64, |
| 26 |
} |
| 27 |
|
| 28 |
impl Default for IntegrityConfig { |
| 29 |
fn default() -> Self { |
| 30 |
Self { |
| 31 |
hash_verification: true, |
| 32 |
proof_verification: true, |
| 33 |
temporal_verification: true, |
| 34 |
max_proof_age: 3600, // 1 hour |
| 35 |
confidence_threshold: 0.95, |
| 36 |
} |
| 37 |
} |
| 38 |
} |
| 39 |
|
| 40 |
/// Cryptographic integrity proof |
| 41 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 42 |
pub struct IntegrityProof { |
| 43 |
/// Unique proof identifier |
| 44 |
pub proof_id: Uuid, |
| 45 |
/// SHA-256 hash of encrypted content |
| 46 |
pub content_hash_sha256: Vec<u8>, |
| 47 |
/// SHA-512 hash of encrypted content |
| 48 |
pub content_hash_sha512: Vec<u8>, |
| 49 |
/// Merkle tree root for chunk verification |
| 50 |
pub merkle_root: Vec<u8>, |
| 51 |
/// Mathematical proof of completeness |
| 52 |
pub completeness_proof: CompletenessProof, |
| 53 |
/// Timestamp when proof was generated |
| 54 |
pub timestamp: u64, |
| 55 |
/// Content size in bytes |
| 56 |
pub content_size: u64, |
| 57 |
/// Additional metadata for verification |
| 58 |
pub metadata: IntegrityMetadata, |
| 59 |
} |
| 60 |
|
| 61 |
/// Mathematical proof of content completeness |
| 62 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 63 |
pub struct CompletenessProof { |
| 64 |
/// Polynomial coefficients for content verification |
| 65 |
pub polynomial_coefficients: Vec<u64>, |
| 66 |
/// Reed-Solomon parity data |
| 67 |
pub parity_data: Vec<u8>, |
| 68 |
/// Checksum verification values |
| 69 |
pub verification_checksums: Vec<u32>, |
| 70 |
/// Content distribution fingerprint |
| 71 |
pub distribution_fingerprint: Vec<u8>, |
| 72 |
} |
| 73 |
|
| 74 |
/// Additional integrity metadata |
| 75 |
#[derive(Debug, Clone, Serialize, Deserialize)] |
| 76 |
pub struct IntegrityMetadata { |
| 77 |
/// Chunk count in the content |
| 78 |
pub chunk_count: usize, |
| 79 |
/// Content type classification |
| 80 |
pub content_classification: String, |
| 81 |
/// Compression ratio (if applicable) |
| 82 |
pub compression_ratio: Option<f64>, |
| 83 |
/// Entropy measure of the content |
| 84 |
pub entropy_measure: f64, |
| 85 |
} |
| 86 |
|
| 87 |
/// Integrity verification result |
| 88 |
#[derive(Debug, Clone)] |
| 89 |
pub struct VerificationResult { |
| 90 |
/// Whether verification passed |
| 91 |
pub is_valid: bool, |
| 92 |
/// Confidence score (0.0-1.0) |
| 93 |
pub confidence: f64, |
| 94 |
/// Individual check results |
| 95 |
pub check_results: HashMap<String, bool>, |
| 96 |
/// Detailed verification report |
| 97 |
pub report: VerificationReport, |
| 98 |
} |
| 99 |
|
| 100 |
/// Detailed verification report |
| 101 |
#[derive(Debug, Clone)] |
| 102 |
pub struct VerificationReport { |
| 103 |
/// Hash verification details |
| 104 |
pub hash_verification: Option<HashVerificationResult>, |
| 105 |
/// Proof verification details |
| 106 |
pub proof_verification: Option<ProofVerificationResult>, |
| 107 |
/// Temporal verification details |
| 108 |
pub temporal_verification: Option<TemporalVerificationResult>, |
| 109 |
/// Overall assessment |
| 110 |
pub assessment: String, |
| 111 |
} |
| 112 |
|
| 113 |
/// Hash verification result details |
| 114 |
#[derive(Debug, Clone)] |
| 115 |
pub struct HashVerificationResult { |
| 116 |
pub sha256_match: bool, |
| 117 |
pub sha512_match: bool, |
| 118 |
pub merkle_verification: bool, |
| 119 |
pub hash_confidence: f64, |
| 120 |
} |
| 121 |
|
| 122 |
/// Proof verification result details |
| 123 |
#[derive(Debug, Clone)] |
| 124 |
pub struct ProofVerificationResult { |
| 125 |
pub polynomial_verification: bool, |
| 126 |
pub parity_verification: bool, |
| 127 |
pub checksum_verification: bool, |
| 128 |
pub distribution_verification: bool, |
| 129 |
pub proof_confidence: f64, |
| 130 |
} |
| 131 |
|
| 132 |
/// Temporal verification result details |
| 133 |
#[derive(Debug, Clone)] |
| 134 |
pub struct TemporalVerificationResult { |
| 135 |
pub proof_age_valid: bool, |
| 136 |
pub timestamp_valid: bool, |
| 137 |
pub temporal_confidence: f64, |
| 138 |
} |
| 139 |
|
| 140 |
/// Content integrity verification engine |
| 141 |
pub struct IntegrityVerifier { |
| 142 |
config: IntegrityConfig, |
| 143 |
} |
| 144 |
|
| 145 |
impl IntegrityVerifier { |
| 146 |
/// Create new integrity verifier with configuration |
| 147 |
pub fn new(config: IntegrityConfig) -> Self { |
| 148 |
Self { config } |
| 149 |
} |
| 150 |
|
| 151 |
/// Create integrity verifier with default configuration |
| 152 |
pub fn default() -> Self { |
| 153 |
Self { |
| 154 |
config: IntegrityConfig::default(), |
| 155 |
} |
| 156 |
} |
| 157 |
|
| 158 |
/// Generate integrity proof for encrypted content |
| 159 |
pub fn generate_proof(&self, encrypted_content: &[u8]) -> Result<IntegrityProof> { |
| 160 |
let proof_id = Uuid::new_v4(); |
| 161 |
let timestamp = SystemTime::now() |
| 162 |
.duration_since(UNIX_EPOCH) |
| 163 |
.context("Failed to get timestamp")? |
| 164 |
.as_secs(); |
| 165 |
|
| 166 |
// Generate cryptographic hashes |
| 167 |
let sha256_hash = self.compute_sha256(encrypted_content)?; |
| 168 |
let sha512_hash = self.compute_sha512(encrypted_content)?; |
| 169 |
let merkle_root = self.compute_merkle_root(encrypted_content)?; |
| 170 |
|
| 171 |
// Generate mathematical completeness proof |
| 172 |
let completeness_proof = self.generate_completeness_proof(encrypted_content)?; |
| 173 |
|
| 174 |
// Generate metadata |
| 175 |
let metadata = self.generate_metadata(encrypted_content)?; |
| 176 |
|
| 177 |
Ok(IntegrityProof { |
| 178 |
proof_id, |
| 179 |
content_hash_sha256: sha256_hash, |
| 180 |
content_hash_sha512: sha512_hash, |
| 181 |
merkle_root, |
| 182 |
completeness_proof, |
| 183 |
timestamp, |
| 184 |
content_size: encrypted_content.len() as u64, |
| 185 |
metadata, |
| 186 |
}) |
| 187 |
} |
| 188 |
|
| 189 |
/// Verify content integrity against proof |
| 190 |
pub fn verify_integrity( |
| 191 |
&self, |
| 192 |
encrypted_content: &[u8], |
| 193 |
proof: &IntegrityProof, |
| 194 |
) -> Result<VerificationResult> { |
| 195 |
let mut check_results = HashMap::new(); |
| 196 |
let mut confidence_scores = Vec::new(); |
| 197 |
|
| 198 |
// Initialize report components |
| 199 |
let mut hash_verification = None; |
| 200 |
let mut proof_verification = None; |
| 201 |
let mut temporal_verification = None; |
| 202 |
|
| 203 |
// Perform hash verification |
| 204 |
if self.config.hash_verification { |
| 205 |
let hash_result = self.verify_hashes(encrypted_content, proof)?; |
| 206 |
let hash_passed = hash_result.sha256_match && hash_result.sha512_match && hash_result.merkle_verification; |
| 207 |
|
| 208 |
check_results.insert("hash_verification".to_string(), hash_passed); |
| 209 |
confidence_scores.push(hash_result.hash_confidence); |
| 210 |
hash_verification = Some(hash_result); |
| 211 |
} |
| 212 |
|
| 213 |
// Perform proof verification |
| 214 |
if self.config.proof_verification { |
| 215 |
let proof_result = self.verify_mathematical_proof(encrypted_content, &proof.completeness_proof)?; |
| 216 |
let proof_passed = proof_result.polynomial_verification |
| 217 |
&& proof_result.parity_verification |
| 218 |
&& proof_result.checksum_verification |
| 219 |
&& proof_result.distribution_verification; |
| 220 |
|
| 221 |
check_results.insert("proof_verification".to_string(), proof_passed); |
| 222 |
confidence_scores.push(proof_result.proof_confidence); |
| 223 |
proof_verification = Some(proof_result); |
| 224 |
} |
| 225 |
|
| 226 |
// Perform temporal verification |
| 227 |
if self.config.temporal_verification { |
| 228 |
let temporal_result = self.verify_temporal_integrity(proof)?; |
| 229 |
let temporal_passed = temporal_result.proof_age_valid && temporal_result.timestamp_valid; |
| 230 |
|
| 231 |
check_results.insert("temporal_verification".to_string(), temporal_passed); |
| 232 |
confidence_scores.push(temporal_result.temporal_confidence); |
| 233 |
temporal_verification = Some(temporal_result); |
| 234 |
} |
| 235 |
|
| 236 |
// Calculate overall confidence and validity |
| 237 |
let overall_confidence = if confidence_scores.is_empty() { |
| 238 |
0.0 |
| 239 |
} else { |
| 240 |
confidence_scores.iter().sum::<f64>() / confidence_scores.len() as f64 |
| 241 |
}; |
| 242 |
|
| 243 |
let is_valid = check_results.values().all(|&v| v) |
| 244 |
&& overall_confidence >= self.config.confidence_threshold; |
| 245 |
|
| 246 |
let assessment = self.generate_assessment(&check_results, overall_confidence); |
| 247 |
|
| 248 |
let report = VerificationReport { |
| 249 |
hash_verification, |
| 250 |
proof_verification, |
| 251 |
temporal_verification, |
| 252 |
assessment, |
| 253 |
}; |
| 254 |
|
| 255 |
Ok(VerificationResult { |
| 256 |
is_valid, |
| 257 |
confidence: overall_confidence, |
| 258 |
check_results, |
| 259 |
report, |
| 260 |
}) |
| 261 |
} |
| 262 |
|
| 263 |
/// Compute SHA-256 hash of content |
| 264 |
fn compute_sha256(&self, content: &[u8]) -> Result<Vec<u8>> { |
| 265 |
let mut context = DigestContext::new(&SHA256); |
| 266 |
context.update(content); |
| 267 |
Ok(context.finish().as_ref().to_vec()) |
| 268 |
} |
| 269 |
|
| 270 |
/// Compute SHA-512 hash of content |
| 271 |
fn compute_sha512(&self, content: &[u8]) -> Result<Vec<u8>> { |
| 272 |
let mut context = DigestContext::new(&SHA512); |
| 273 |
context.update(content); |
| 274 |
Ok(context.finish().as_ref().to_vec()) |
| 275 |
} |
| 276 |
|
| 277 |
/// Compute Merkle tree root for chunk verification |
| 278 |
fn compute_merkle_root(&self, content: &[u8]) -> Result<Vec<u8>> { |
| 279 |
const CHUNK_SIZE: usize = 4096; |
| 280 |
let mut leaf_hashes = Vec::new(); |
| 281 |
|
| 282 |
// Generate leaf hashes for each chunk |
| 283 |
for chunk in content.chunks(CHUNK_SIZE) { |
| 284 |
let mut context = DigestContext::new(&SHA256); |
| 285 |
context.update(chunk); |
| 286 |
leaf_hashes.push(context.finish().as_ref().to_vec()); |
| 287 |
} |
| 288 |
|
| 289 |
// Build Merkle tree bottom-up |
| 290 |
let mut level = leaf_hashes; |
| 291 |
while level.len() > 1 { |
| 292 |
let mut next_level = Vec::new(); |
| 293 |
|
| 294 |
for pair in level.chunks(2) { |
| 295 |
let mut context = DigestContext::new(&SHA256); |
| 296 |
context.update(&pair[0]); |
| 297 |
if pair.len() > 1 { |
| 298 |
context.update(&pair[1]); |
| 299 |
} else { |
| 300 |
// Odd number of hashes, duplicate the last one |
| 301 |
context.update(&pair[0]); |
| 302 |
} |
| 303 |
next_level.push(context.finish().as_ref().to_vec()); |
| 304 |
} |
| 305 |
|
| 306 |
level = next_level; |
| 307 |
} |
| 308 |
|
| 309 |
level.into_iter().next().unwrap_or_else(|| { |
| 310 |
let mut context = DigestContext::new(&SHA256); |
| 311 |
context.update(b"empty"); |
| 312 |
context.finish().as_ref().to_vec() |
| 313 |
}).into() |
| 314 |
} |
| 315 |
|
| 316 |
/// Generate mathematical completeness proof |
| 317 |
fn generate_completeness_proof(&self, content: &[u8]) -> Result<CompletenessProof> { |
| 318 |
// Generate polynomial coefficients based on content distribution |
| 319 |
let polynomial_coefficients = self.compute_polynomial_coefficients(content); |
| 320 |
|
| 321 |
// Generate Reed-Solomon parity data for error detection |
| 322 |
let parity_data = self.generate_reed_solomon_parity(content)?; |
| 323 |
|
| 324 |
// Generate verification checksums |
| 325 |
let verification_checksums = self.compute_verification_checksums(content); |
| 326 |
|
| 327 |
// Generate content distribution fingerprint |
| 328 |
let distribution_fingerprint = self.compute_distribution_fingerprint(content)?; |
| 329 |
|
| 330 |
Ok(CompletenessProof { |
| 331 |
polynomial_coefficients, |
| 332 |
parity_data, |
| 333 |
verification_checksums, |
| 334 |
distribution_fingerprint, |
| 335 |
}) |
| 336 |
} |
| 337 |
|
| 338 |
/// Compute polynomial coefficients for content verification |
| 339 |
fn compute_polynomial_coefficients(&self, content: &[u8]) -> Vec<u64> { |
| 340 |
const POLY_DEGREE: usize = 8; |
| 341 |
let mut coefficients = vec![0u64; POLY_DEGREE]; |
| 342 |
|
| 343 |
for (i, &byte) in content.iter().enumerate() { |
| 344 |
let coeff_idx = i % POLY_DEGREE; |
| 345 |
coefficients[coeff_idx] = coefficients[coeff_idx] |
| 346 |
.wrapping_add(byte as u64) |
| 347 |
.wrapping_mul(i as u64 + 1); |
| 348 |
} |
| 349 |
|
| 350 |
coefficients |
| 351 |
} |
| 352 |
|
| 353 |
/// Generate Reed-Solomon parity data |
| 354 |
fn generate_reed_solomon_parity(&self, content: &[u8]) -> Result<Vec<u8>> { |
| 355 |
// Simplified Reed-Solomon implementation for demonstration |
| 356 |
// In production, use a proper Reed-Solomon library |
| 357 |
const PARITY_SIZE: usize = 16; |
| 358 |
let mut parity = vec![0u8; PARITY_SIZE]; |
| 359 |
|
| 360 |
for (i, &byte) in content.iter().enumerate() { |
| 361 |
let parity_idx = i % PARITY_SIZE; |
| 362 |
parity[parity_idx] ^= byte; |
| 363 |
} |
| 364 |
|
| 365 |
Ok(parity) |
| 366 |
} |
| 367 |
|
| 368 |
/// Compute verification checksums |
| 369 |
fn compute_verification_checksums(&self, content: &[u8]) -> Vec<u32> { |
| 370 |
let mut checksums = Vec::new(); |
| 371 |
const CHECKSUM_COUNT: usize = 4; |
| 372 |
|
| 373 |
for i in 0..CHECKSUM_COUNT { |
| 374 |
let mut checksum = 0u32; |
| 375 |
let start = (content.len() * i) / CHECKSUM_COUNT; |
| 376 |
let end = (content.len() * (i + 1)) / CHECKSUM_COUNT; |
| 377 |
|
| 378 |
for &byte in &content[start..end] { |
| 379 |
checksum = checksum.wrapping_add(byte as u32); |
| 380 |
checksum = checksum.wrapping_mul(31); |
| 381 |
} |
| 382 |
|
| 383 |
checksums.push(checksum); |
| 384 |
} |
| 385 |
|
| 386 |
checksums |
| 387 |
} |
| 388 |
|
| 389 |
/// Compute content distribution fingerprint |
| 390 |
fn compute_distribution_fingerprint(&self, content: &[u8]) -> Result<Vec<u8>> { |
| 391 |
let mut frequency = [0u32; 256]; |
| 392 |
|
| 393 |
// Count byte frequency |
| 394 |
for &byte in content { |
| 395 |
frequency[byte as usize] += 1; |
| 396 |
} |
| 397 |
|
| 398 |
// Create fingerprint from frequency distribution |
| 399 |
let mut context = DigestContext::new(&SHA256); |
| 400 |
for count in frequency { |
| 401 |
context.update(&count.to_le_bytes()); |
| 402 |
} |
| 403 |
|
| 404 |
Ok(context.finish().as_ref().to_vec()) |
| 405 |
} |
| 406 |
|
| 407 |
/// Generate integrity metadata |
| 408 |
fn generate_metadata(&self, content: &[u8]) -> Result<IntegrityMetadata> { |
| 409 |
const CHUNK_SIZE: usize = 4096; |
| 410 |
let chunk_count = (content.len() + CHUNK_SIZE - 1) / CHUNK_SIZE; |
| 411 |
|
| 412 |
// Calculate entropy |
| 413 |
let entropy_measure = self.calculate_entropy(content); |
| 414 |
|
| 415 |
Ok(IntegrityMetadata { |
| 416 |
chunk_count, |
| 417 |
content_classification: "encrypted_data".to_string(), |
| 418 |
compression_ratio: None, // Could be calculated if compression is used |
| 419 |
entropy_measure, |
| 420 |
}) |
| 421 |
} |
| 422 |
|
| 423 |
/// Calculate content entropy |
| 424 |
fn calculate_entropy(&self, content: &[u8]) -> f64 { |
| 425 |
let mut frequency = [0u32; 256]; |
| 426 |
|
| 427 |
// Count byte frequency |
| 428 |
for &byte in content { |
| 429 |
frequency[byte as usize] += 1; |
| 430 |
} |
| 431 |
|
| 432 |
// Calculate Shannon entropy |
| 433 |
let total = content.len() as f64; |
| 434 |
let mut entropy = 0.0; |
| 435 |
|
| 436 |
for count in frequency { |
| 437 |
if count > 0 { |
| 438 |
let probability = count as f64 / total; |
| 439 |
entropy -= probability * probability.log2(); |
| 440 |
} |
| 441 |
} |
| 442 |
|
| 443 |
entropy |
| 444 |
} |
| 445 |
|
| 446 |
/// Verify cryptographic hashes |
| 447 |
fn verify_hashes(&self, content: &[u8], proof: &IntegrityProof) -> Result<HashVerificationResult> { |
| 448 |
let sha256_hash = self.compute_sha256(content)?; |
| 449 |
let sha512_hash = self.compute_sha512(content)?; |
| 450 |
let merkle_root = self.compute_merkle_root(content)?; |
| 451 |
|
| 452 |
let sha256_match = sha256_hash == proof.content_hash_sha256; |
| 453 |
let sha512_match = sha512_hash == proof.content_hash_sha512; |
| 454 |
let merkle_verification = merkle_root == proof.merkle_root; |
| 455 |
|
| 456 |
let hash_confidence = match (sha256_match, sha512_match, merkle_verification) { |
| 457 |
(true, true, true) => 1.0, |
| 458 |
(true, true, false) | (true, false, true) | (false, true, true) => 0.7, |
| 459 |
(true, false, false) | (false, true, false) | (false, false, true) => 0.3, |
| 460 |
(false, false, false) => 0.0, |
| 461 |
}; |
| 462 |
|
| 463 |
Ok(HashVerificationResult { |
| 464 |
sha256_match, |
| 465 |
sha512_match, |
| 466 |
merkle_verification, |
| 467 |
hash_confidence, |
| 468 |
}) |
| 469 |
} |
| 470 |
|
| 471 |
/// Verify mathematical proof |
| 472 |
fn verify_mathematical_proof( |
| 473 |
&self, |
| 474 |
content: &[u8], |
| 475 |
proof: &CompletenessProof, |
| 476 |
) -> Result<ProofVerificationResult> { |
| 477 |
// Verify polynomial coefficients |
| 478 |
let computed_coefficients = self.compute_polynomial_coefficients(content); |
| 479 |
let polynomial_verification = computed_coefficients == proof.polynomial_coefficients; |
| 480 |
|
| 481 |
// Verify Reed-Solomon parity |
| 482 |
let computed_parity = self.generate_reed_solomon_parity(content)?; |
| 483 |
let parity_verification = computed_parity == proof.parity_data; |
| 484 |
|
| 485 |
// Verify checksums |
| 486 |
let computed_checksums = self.compute_verification_checksums(content); |
| 487 |
let checksum_verification = computed_checksums == proof.verification_checksums; |
| 488 |
|
| 489 |
// Verify distribution fingerprint |
| 490 |
let computed_fingerprint = self.compute_distribution_fingerprint(content)?; |
| 491 |
let distribution_verification = computed_fingerprint == proof.distribution_fingerprint; |
| 492 |
|
| 493 |
let verified_count = [ |
| 494 |
polynomial_verification, |
| 495 |
parity_verification, |
| 496 |
checksum_verification, |
| 497 |
distribution_verification, |
| 498 |
] |
| 499 |
.iter() |
| 500 |
.filter(|&&v| v) |
| 501 |
.count(); |
| 502 |
|
| 503 |
let proof_confidence = verified_count as f64 / 4.0; |
| 504 |
|
| 505 |
Ok(ProofVerificationResult { |
| 506 |
polynomial_verification, |
| 507 |
parity_verification, |
| 508 |
checksum_verification, |
| 509 |
distribution_verification, |
| 510 |
proof_confidence, |
| 511 |
}) |
| 512 |
} |
| 513 |
|
| 514 |
/// Verify temporal integrity |
| 515 |
fn verify_temporal_integrity(&self, proof: &IntegrityProof) -> Result<TemporalVerificationResult> { |
| 516 |
let current_time = SystemTime::now() |
| 517 |
.duration_since(UNIX_EPOCH) |
| 518 |
.context("Failed to get current timestamp")? |
| 519 |
.as_secs(); |
| 520 |
|
| 521 |
let proof_age = current_time.saturating_sub(proof.timestamp); |
| 522 |
let proof_age_valid = proof_age <= self.config.max_proof_age; |
| 523 |
|
| 524 |
// Basic timestamp validation (not too far in the future) |
| 525 |
let timestamp_valid = proof.timestamp <= current_time + 300; // Allow 5 minutes clock skew |
| 526 |
|
| 527 |
let temporal_confidence = match (proof_age_valid, timestamp_valid) { |
| 528 |
(true, true) => 1.0, |
| 529 |
(true, false) | (false, true) => 0.5, |
| 530 |
(false, false) => 0.0, |
| 531 |
}; |
| 532 |
|
| 533 |
Ok(TemporalVerificationResult { |
| 534 |
proof_age_valid, |
| 535 |
timestamp_valid, |
| 536 |
temporal_confidence, |
| 537 |
}) |
| 538 |
} |
| 539 |
|
| 540 |
/// Generate assessment report |
| 541 |
fn generate_assessment(&self, check_results: &HashMap<String, bool>, confidence: f64) -> String { |
| 542 |
let passed_checks = check_results.values().filter(|&&v| v).count(); |
| 543 |
let total_checks = check_results.len(); |
| 544 |
|
| 545 |
if passed_checks == total_checks && confidence >= self.config.confidence_threshold { |
| 546 |
format!( |
| 547 |
"VERIFIED: All integrity checks passed ({}/{}) with {:.1}% confidence", |
| 548 |
passed_checks, total_checks, confidence * 100.0 |
| 549 |
) |
| 550 |
} else if passed_checks >= total_checks / 2 { |
| 551 |
format!( |
| 552 |
"PARTIAL: {}/{} checks passed with {:.1}% confidence - review recommended", |
| 553 |
passed_checks, total_checks, confidence * 100.0 |
| 554 |
) |
| 555 |
} else { |
| 556 |
format!( |
| 557 |
"FAILED: Only {}/{} checks passed with {:.1}% confidence - integrity compromised", |
| 558 |
passed_checks, total_checks, confidence * 100.0 |
| 559 |
) |
| 560 |
} |
| 561 |
} |
| 562 |
} |
| 563 |
|
| 564 |
#[cfg(test)] |
| 565 |
mod tests { |
| 566 |
use super::*; |
| 567 |
|
| 568 |
#[test] |
| 569 |
fn test_integrity_proof_generation() -> Result<()> { |
| 570 |
let verifier = IntegrityVerifier::default(); |
| 571 |
let test_content = b"Test content for integrity verification"; |
| 572 |
|
| 573 |
let proof = verifier.generate_proof(test_content)?; |
| 574 |
|
| 575 |
assert_eq!(proof.content_size, test_content.len() as u64); |
| 576 |
assert!(!proof.content_hash_sha256.is_empty()); |
| 577 |
assert!(!proof.content_hash_sha512.is_empty()); |
| 578 |
assert!(!proof.merkle_root.is_empty()); |
| 579 |
|
| 580 |
Ok(()) |
| 581 |
} |
| 582 |
|
| 583 |
#[test] |
| 584 |
fn test_integrity_verification_success() -> Result<()> { |
| 585 |
let verifier = IntegrityVerifier::default(); |
| 586 |
let test_content = b"Test content for successful verification"; |
| 587 |
|
| 588 |
let proof = verifier.generate_proof(test_content)?; |
| 589 |
let result = verifier.verify_integrity(test_content, &proof)?; |
| 590 |
|
| 591 |
assert!(result.is_valid); |
| 592 |
assert!(result.confidence >= 0.95); |
| 593 |
|
| 594 |
Ok(()) |
| 595 |
} |
| 596 |
|
| 597 |
#[test] |
| 598 |
fn test_integrity_verification_failure() -> Result<()> { |
| 599 |
let verifier = IntegrityVerifier::default(); |
| 600 |
let original_content = b"Original test content"; |
| 601 |
let tampered_content = b"Tampered test content"; |
| 602 |
|
| 603 |
let proof = verifier.generate_proof(original_content)?; |
| 604 |
let result = verifier.verify_integrity(tampered_content, &proof)?; |
| 605 |
|
| 606 |
assert!(!result.is_valid); |
| 607 |
assert!(result.confidence < 0.95); |
| 608 |
|
| 609 |
Ok(()) |
| 610 |
} |
| 611 |
|
| 612 |
#[test] |
| 613 |
fn test_entropy_calculation() { |
| 614 |
let verifier = IntegrityVerifier::default(); |
| 615 |
|
| 616 |
// Test high entropy content (random-like) |
| 617 |
let high_entropy_content: Vec<u8> = (0..256).collect(); |
| 618 |
let high_entropy = verifier.calculate_entropy(&high_entropy_content); |
| 619 |
|
| 620 |
// Test low entropy content (repetitive) |
| 621 |
let low_entropy_content = vec![0u8; 256]; |
| 622 |
let low_entropy = verifier.calculate_entropy(&low_entropy_content); |
| 623 |
|
| 624 |
assert!(high_entropy > low_entropy); |
| 625 |
assert!(high_entropy > 7.0); // Should be close to 8.0 for uniform distribution |
| 626 |
assert!(low_entropy < 1.0); // Should be 0.0 for uniform content |
| 627 |
} |
| 628 |
|
| 629 |
#[test] |
| 630 |
fn test_merkle_tree_verification() -> Result<()> { |
| 631 |
let verifier = IntegrityVerifier::default(); |
| 632 |
let test_content = b"Test content for Merkle tree verification with multiple chunks"; |
| 633 |
|
| 634 |
let merkle_root1 = verifier.compute_merkle_root(test_content)?; |
| 635 |
let merkle_root2 = verifier.compute_merkle_root(test_content)?; |
| 636 |
|
| 637 |
// Same content should produce same Merkle root |
| 638 |
assert_eq!(merkle_root1, merkle_root2); |
| 639 |
|
| 640 |
// Different content should produce different Merkle root |
| 641 |
let different_content = b"Different test content for Merkle tree verification"; |
| 642 |
let different_root = verifier.compute_merkle_root(different_content)?; |
| 643 |
assert_ne!(merkle_root1, different_root); |
| 644 |
|
| 645 |
Ok(()) |
| 646 |
} |
| 647 |
} |