use crate::encryption::{decrypt, encrypt, generate_key, generate_nonce};
use crate::hash::hash;
use crate::signing::KeyPair;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum SecurityLevel {
Level1,
Level2,
Level3,
Level4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ComplianceAlgorithm {
AES256,
ChaCha20Poly1305,
SHA256,
SHA512,
BLAKE3,
Ed25519,
X25519,
HMACSHA256,
HKDF,
RSA2048,
RSA3072,
Kyber,
Dilithium,
}
impl ComplianceAlgorithm {
pub fn is_fips_approved(&self) -> bool {
matches!(
self,
ComplianceAlgorithm::AES256
| ComplianceAlgorithm::SHA256
| ComplianceAlgorithm::SHA512
| ComplianceAlgorithm::HMACSHA256
| ComplianceAlgorithm::HKDF
| ComplianceAlgorithm::RSA2048
| ComplianceAlgorithm::RSA3072
)
}
pub fn min_key_size(&self) -> usize {
match self {
ComplianceAlgorithm::AES256 => 256,
ComplianceAlgorithm::ChaCha20Poly1305 => 256,
ComplianceAlgorithm::Ed25519 => 256,
ComplianceAlgorithm::X25519 => 256,
ComplianceAlgorithm::RSA2048 => 2048,
ComplianceAlgorithm::RSA3072 => 3072,
ComplianceAlgorithm::Kyber => 256,
ComplianceAlgorithm::Dilithium => 256,
_ => 0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ComplianceStatus {
Compliant,
PartiallyCompliant,
NonCompliant,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelfTestResult {
pub test_name: String,
pub algorithm: ComplianceAlgorithm,
pub passed: bool,
pub error: Option<String>,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelfTestResults {
pub tests: Vec<SelfTestResult>,
pub all_passed: bool,
}
impl SelfTestResults {
pub fn all_passed(&self) -> bool {
self.all_passed
}
pub fn failed_tests(&self) -> Vec<&SelfTestResult> {
self.tests.iter().filter(|t| !t.passed).collect()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceIssue {
pub severity: IssueSeverity,
pub description: String,
pub algorithm: Option<ComplianceAlgorithm>,
pub recommendation: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum IssueSeverity {
Info,
Warning,
Error,
Critical,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceReport {
pub security_level: SecurityLevel,
pub overall_status: ComplianceStatus,
pub algorithms: Vec<ComplianceAlgorithm>,
pub fips_approved: Vec<ComplianceAlgorithm>,
pub non_approved: Vec<ComplianceAlgorithm>,
pub self_test_results: SelfTestResults,
pub issues: Vec<ComplianceIssue>,
pub timestamp: DateTime<Utc>,
}
impl ComplianceReport {
pub fn to_json(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComplianceChecker {
security_level: SecurityLevel,
algorithms: Vec<ComplianceAlgorithm>,
issues: Vec<ComplianceIssue>,
}
impl ComplianceChecker {
pub fn new(security_level: SecurityLevel) -> Self {
Self {
security_level,
algorithms: Vec::new(),
issues: Vec::new(),
}
}
pub fn register_algorithm(&mut self, algorithm: ComplianceAlgorithm) {
if !self.algorithms.contains(&algorithm) {
self.algorithms.push(algorithm);
if !algorithm.is_fips_approved() {
self.issues.push(ComplianceIssue {
severity: IssueSeverity::Warning,
description: format!("{:?} is not FIPS 140-3 approved", algorithm),
algorithm: Some(algorithm),
recommendation: "Consider using FIPS-approved alternatives".to_string(),
});
}
}
}
pub fn run_self_tests(&mut self) -> SelfTestResults {
let tests = vec![
self.test_chacha20(),
self.test_blake3(),
self.test_ed25519(),
];
let all_passed = tests.iter().all(|t| t.passed);
SelfTestResults { tests, all_passed }
}
fn test_chacha20(&self) -> SelfTestResult {
let test_name = "ChaCha20-Poly1305 KAT".to_string();
let algorithm = ComplianceAlgorithm::ChaCha20Poly1305;
let plaintext = b"Test message for KAT";
let key = generate_key();
let nonce = generate_nonce();
match encrypt(plaintext, &key, &nonce) {
Ok(ciphertext) => match decrypt(&ciphertext, &key, &nonce) {
Ok(decrypted) => {
let passed = decrypted == plaintext;
SelfTestResult {
test_name,
algorithm,
passed,
error: if passed {
None
} else {
Some("Decryption mismatch".to_string())
},
timestamp: Utc::now(),
}
}
Err(e) => SelfTestResult {
test_name,
algorithm,
passed: false,
error: Some(format!("Decryption failed: {:?}", e)),
timestamp: Utc::now(),
},
},
Err(e) => SelfTestResult {
test_name,
algorithm,
passed: false,
error: Some(format!("Encryption failed: {:?}", e)),
timestamp: Utc::now(),
},
}
}
fn test_blake3(&self) -> SelfTestResult {
let test_name = "BLAKE3 KAT".to_string();
let algorithm = ComplianceAlgorithm::BLAKE3;
let input = b"The quick brown fox jumps over the lazy dog";
let hash1 = hash(input);
let hash2 = hash(input);
let passed = hash1 == hash2 && hash1.len() == 32;
SelfTestResult {
test_name,
algorithm,
passed,
error: if passed {
None
} else {
Some("Hash mismatch or incorrect length".to_string())
},
timestamp: Utc::now(),
}
}
fn test_ed25519(&self) -> SelfTestResult {
let test_name = "Ed25519 KAT".to_string();
let algorithm = ComplianceAlgorithm::Ed25519;
let keypair = KeyPair::generate();
let message = b"Test message for signature";
let signature = keypair.sign(message);
let passed = keypair.verify(message, &signature);
SelfTestResult {
test_name,
algorithm,
passed,
error: if passed {
None
} else {
Some("Signature verification failed".to_string())
},
timestamp: Utc::now(),
}
}
pub fn validate_key_strength(
&mut self,
algorithm: ComplianceAlgorithm,
key_bits: usize,
) -> bool {
let min_bits = algorithm.min_key_size();
if key_bits < min_bits {
self.issues.push(ComplianceIssue {
severity: IssueSeverity::Error,
description: format!(
"{:?} key strength ({} bits) below minimum ({} bits)",
algorithm, key_bits, min_bits
),
algorithm: Some(algorithm),
recommendation: format!("Use at least {} bits for {:?}", min_bits, algorithm),
});
false
} else {
true
}
}
pub fn generate_report(&mut self) -> ComplianceReport {
let self_test_results = self.run_self_tests();
let fips_approved: Vec<ComplianceAlgorithm> = self
.algorithms
.iter()
.filter(|a| a.is_fips_approved())
.copied()
.collect();
let non_approved: Vec<ComplianceAlgorithm> = self
.algorithms
.iter()
.filter(|a| !a.is_fips_approved())
.copied()
.collect();
let has_errors = self
.issues
.iter()
.any(|i| matches!(i.severity, IssueSeverity::Error | IssueSeverity::Critical));
let has_warnings = self
.issues
.iter()
.any(|i| i.severity == IssueSeverity::Warning);
let overall_status = if has_errors || !self_test_results.all_passed {
ComplianceStatus::NonCompliant
} else if has_warnings || !non_approved.is_empty() {
ComplianceStatus::PartiallyCompliant
} else {
ComplianceStatus::Compliant
};
ComplianceReport {
security_level: self.security_level,
overall_status,
algorithms: self.algorithms.clone(),
fips_approved,
non_approved,
self_test_results,
issues: self.issues.clone(),
timestamp: Utc::now(),
}
}
pub fn issues(&self) -> &[ComplianceIssue] {
&self.issues
}
pub fn clear_issues(&mut self) {
self.issues.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_compliance_checker_basic() {
let checker = ComplianceChecker::new(SecurityLevel::Level1);
assert_eq!(checker.security_level, SecurityLevel::Level1);
}
#[test]
fn test_register_fips_approved_algorithm() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::AES256);
assert_eq!(checker.algorithms.len(), 1);
assert_eq!(checker.issues.len(), 0);
}
#[test]
fn test_register_non_approved_algorithm() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::ChaCha20Poly1305);
assert_eq!(checker.algorithms.len(), 1);
assert_eq!(checker.issues.len(), 1);
assert_eq!(checker.issues[0].severity, IssueSeverity::Warning);
}
#[test]
fn test_self_tests() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
let results = checker.run_self_tests();
assert!(results.all_passed());
assert_eq!(results.tests.len(), 3);
}
#[test]
fn test_key_strength_validation() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
assert!(checker.validate_key_strength(ComplianceAlgorithm::AES256, 256));
assert!(!checker.validate_key_strength(ComplianceAlgorithm::AES256, 128));
assert_eq!(checker.issues.len(), 1);
}
#[test]
fn test_generate_report_compliant() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::AES256);
checker.register_algorithm(ComplianceAlgorithm::SHA256);
let report = checker.generate_report();
assert_eq!(report.overall_status, ComplianceStatus::Compliant);
assert_eq!(report.fips_approved.len(), 2);
assert_eq!(report.non_approved.len(), 0);
}
#[test]
fn test_generate_report_partially_compliant() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::AES256);
checker.register_algorithm(ComplianceAlgorithm::ChaCha20Poly1305);
let report = checker.generate_report();
assert_eq!(report.overall_status, ComplianceStatus::PartiallyCompliant);
assert_eq!(report.fips_approved.len(), 1);
assert_eq!(report.non_approved.len(), 1);
}
#[test]
fn test_generate_report_non_compliant() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::AES256);
checker.validate_key_strength(ComplianceAlgorithm::AES256, 64);
let report = checker.generate_report();
assert_eq!(report.overall_status, ComplianceStatus::NonCompliant);
}
#[test]
fn test_algorithm_is_fips_approved() {
assert!(ComplianceAlgorithm::AES256.is_fips_approved());
assert!(ComplianceAlgorithm::SHA256.is_fips_approved());
assert!(!ComplianceAlgorithm::ChaCha20Poly1305.is_fips_approved());
assert!(!ComplianceAlgorithm::BLAKE3.is_fips_approved());
}
#[test]
fn test_security_levels() {
assert!(SecurityLevel::Level1 < SecurityLevel::Level2);
assert!(SecurityLevel::Level2 < SecurityLevel::Level3);
assert!(SecurityLevel::Level3 < SecurityLevel::Level4);
}
#[test]
fn test_report_json_export() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::AES256);
let report = checker.generate_report();
let json = report.to_json().unwrap();
assert!(json.contains("AES256"));
assert!(json.contains("security_level"));
}
#[test]
fn test_failed_tests() {
let results = SelfTestResults {
tests: vec![
SelfTestResult {
test_name: "Test1".to_string(),
algorithm: ComplianceAlgorithm::AES256,
passed: true,
error: None,
timestamp: Utc::now(),
},
SelfTestResult {
test_name: "Test2".to_string(),
algorithm: ComplianceAlgorithm::SHA256,
passed: false,
error: Some("Error".to_string()),
timestamp: Utc::now(),
},
],
all_passed: false,
};
let failed = results.failed_tests();
assert_eq!(failed.len(), 1);
assert_eq!(failed[0].test_name, "Test2");
}
#[test]
fn test_clear_issues() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::ChaCha20Poly1305);
assert_eq!(checker.issues.len(), 1);
checker.clear_issues();
assert_eq!(checker.issues.len(), 0);
}
#[test]
fn test_multiple_registrations_same_algorithm() {
let mut checker = ComplianceChecker::new(SecurityLevel::Level1);
checker.register_algorithm(ComplianceAlgorithm::AES256);
checker.register_algorithm(ComplianceAlgorithm::AES256);
checker.register_algorithm(ComplianceAlgorithm::AES256);
assert_eq!(checker.algorithms.len(), 1);
}
#[test]
fn test_min_key_sizes() {
assert_eq!(ComplianceAlgorithm::AES256.min_key_size(), 256);
assert_eq!(ComplianceAlgorithm::RSA2048.min_key_size(), 2048);
assert_eq!(ComplianceAlgorithm::RSA3072.min_key_size(), 3072);
}
#[test]
fn test_issue_severity_levels() {
let issue = ComplianceIssue {
severity: IssueSeverity::Critical,
description: "Test".to_string(),
algorithm: None,
recommendation: "Fix it".to_string(),
};
assert_eq!(issue.severity, IssueSeverity::Critical);
}
}