use crate::error::Result;
#[cfg(feature = "encrypt")]
use crate::fips::self_test::{Alert, AlertCategory, AlertHandler, AlertSeverity};
use crate::i18n::translate_with_args;
use chrono::Utc;
use std::collections::VecDeque;
use std::sync::{Arc, Mutex, RwLock};
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct RngHealthMetrics {
pub entropy_bits: f64,
pub failure_rate: f64,
pub consecutive_failures: u32,
pub total_tests: u64,
pub failed_tests: u64,
pub last_test_timestamp: chrono::DateTime<Utc>,
pub health_score: f64, }
impl Default for RngHealthMetrics {
fn default() -> Self {
Self {
entropy_bits: 8.0,
failure_rate: 0.0,
consecutive_failures: 0,
total_tests: 0,
failed_tests: 0,
last_test_timestamp: Utc::now(),
health_score: 1.0,
}
}
}
#[derive(Debug, Clone)]
pub struct RngMonitorConfig {
pub sample_size: usize, pub test_interval: Duration, pub entropy_threshold: f64, pub max_consecutive_failures: u32, pub failure_rate_threshold: f64, pub enable_real_time_monitoring: bool, }
impl Default for RngMonitorConfig {
fn default() -> Self {
Self {
sample_size: 25000, test_interval: Duration::from_secs(300), entropy_threshold: 7.5, max_consecutive_failures: 3, failure_rate_threshold: 0.1, enable_real_time_monitoring: true, }
}
}
pub struct RngMonitor {
config: RngMonitorConfig,
metrics: Arc<RwLock<RngHealthMetrics>>,
#[cfg(feature = "encrypt")]
alert_handler: Arc<Mutex<Option<Arc<dyn AlertHandler + Send + Sync>>>>,
test_history: Arc<Mutex<VecDeque<bool>>>,
last_test_time: Arc<Mutex<Instant>>,
}
impl RngMonitor {
pub fn new(config: RngMonitorConfig) -> Self {
Self {
config,
metrics: Arc::new(RwLock::new(RngHealthMetrics::default())),
#[cfg(feature = "encrypt")]
alert_handler: Arc::new(Mutex::new(None)),
test_history: Arc::new(Mutex::new(VecDeque::with_capacity(100))),
last_test_time: Arc::new(Mutex::new(Instant::now())),
}
}
#[cfg(feature = "encrypt")]
#[allow(dead_code)]
pub fn set_alert_handler(&self, handler: Arc<dyn AlertHandler + Send + Sync>) {
if let Ok(mut handler_guard) = self.alert_handler.lock() {
*handler_guard = Some(handler);
}
}
pub fn get_health_metrics(&self) -> RngHealthMetrics {
self.metrics
.read()
.map(|guard| guard.clone())
.unwrap_or_default()
}
pub fn perform_health_check(&self) -> Result<bool> {
let mut random_bytes = vec![0u8; self.config.sample_size];
if let Err(_e) =
crate::random::SecureRandom::new().and_then(|rng| rng.fill(&mut random_bytes))
{
self.record_test_result(false);
#[cfg(feature = "encrypt")]
self.trigger_alert(
AlertSeverity::Critical,
AlertCategory::SystemMalfunction,
translate_with_args("log.rng_generation_failed", &[("error", &_e.to_string())]),
Some("rng_generation".to_string()),
);
return Ok(false);
}
let all_zeros = random_bytes.iter().all(|&b| b == 0);
let all_ones = random_bytes.iter().all(|&b| b == 0xFF);
if all_zeros || all_ones {
self.record_test_result(false);
#[cfg(feature = "encrypt")]
self.trigger_alert(
AlertSeverity::Critical,
AlertCategory::TestFailure,
translate_with_args("log.rng_not_random", &[]),
Some("basic_randomness_check".to_string()),
);
return Ok(false);
}
#[cfg(feature = "encrypt")]
{
use crate::fips::self_test::FipsSelfTestEngine;
let test_engine = FipsSelfTestEngine::new();
let nist_result = test_engine.nist_randomness_tests(&random_bytes);
let passed = nist_result.passed;
self.record_test_result(passed);
{
let mut metrics = self.metrics.write().unwrap();
metrics.entropy_bits = nist_result.entropy_bits;
metrics.last_test_timestamp = Utc::now();
let history = self.test_history.lock().unwrap();
let recent_tests: Vec<bool> = history.iter().cloned().collect();
let recent_failures = recent_tests.iter().filter(|&&x| !x).count();
metrics.failure_rate = recent_failures as f64 / recent_tests.len() as f64;
let entropy_score = (metrics.entropy_bits / 8.0).min(1.0);
let failure_rate_score = (1.0 - metrics.failure_rate).max(0.0);
let consecutive_failures_score = if metrics.consecutive_failures == 0 {
1.0
} else {
0.5_f64.powi(metrics.consecutive_failures as i32)
};
metrics.health_score = (entropy_score * 0.4
+ failure_rate_score * 0.4
+ consecutive_failures_score * 0.2)
.min(1.0);
}
if !passed {
self.trigger_alert(
AlertSeverity::Warning,
AlertCategory::TestFailure,
translate_with_args(
"log.nist_test_failed",
&[("error", &nist_result.error_message.unwrap_or_default())],
),
Some("nist_randomness_test".to_string()),
);
}
if nist_result.entropy_bits < self.config.entropy_threshold {
self.trigger_alert(
AlertSeverity::Warning,
AlertCategory::EntropyDegradation,
translate_with_args(
"log.low_entropy_detected",
&[("entropy", &format!("{:.2}", nist_result.entropy_bits))],
),
Some("entropy_check".to_string()),
);
}
let metrics = self.metrics.read().unwrap();
if metrics.consecutive_failures >= self.config.max_consecutive_failures {
self.trigger_alert(
AlertSeverity::Critical,
AlertCategory::SystemMalfunction,
translate_with_args(
"log.consecutive_failures",
&[("count", &metrics.consecutive_failures.to_string())],
),
Some("consecutive_failures".to_string()),
);
}
if metrics.failure_rate > self.config.failure_rate_threshold {
self.trigger_alert(
AlertSeverity::Warning,
AlertCategory::TestFailure,
translate_with_args(
"log.failure_rate_high",
&[("rate", &format!("{:.2}", metrics.failure_rate * 100.0))],
),
Some("failure_rate".to_string()),
);
}
Ok(passed)
}
#[cfg(not(feature = "encrypt"))]
{
self.record_test_result(true);
Ok(true)
}
}
fn record_test_result(&self, passed: bool) {
let mut metrics = self.metrics.write().unwrap();
metrics.total_tests += 1;
if !passed {
metrics.failed_tests += 1;
metrics.consecutive_failures += 1;
} else {
metrics.consecutive_failures = 0;
}
let mut history = self.test_history.lock().unwrap();
history.push_back(passed);
if history.len() > 100 {
history.pop_front();
}
*self.last_test_time.lock().unwrap() = Instant::now();
}
#[cfg(feature = "encrypt")]
fn trigger_alert(
&self,
severity: AlertSeverity,
category: AlertCategory,
message: String,
test_name: Option<String>,
) {
crate::audit::AuditLogger::log(
"RNG_HEALTH_ALERT",
None,
None,
Err(crate::CryptoError::FipsError(format!(
"[{:?}] Category: {:?}, Message: {}",
severity, category, message
))),
);
if let Some(handler) = self.alert_handler.lock().unwrap().as_ref() {
let alert = Alert {
severity,
category: match category {
AlertCategory::EntropyDegradation => AlertCategory::EntropyDegradation,
AlertCategory::TestFailure => AlertCategory::TestFailure,
AlertCategory::SystemMalfunction => AlertCategory::SystemMalfunction,
},
message,
timestamp: Utc::now(),
test_name,
};
handler.handle_alert(&alert);
}
}
pub fn record_external_test_result(&self, passed: bool, test_type: &str) {
let _ = test_type;
self.record_test_result(passed);
#[cfg(feature = "encrypt")]
if !passed {
self.trigger_alert(
AlertSeverity::Warning,
AlertCategory::TestFailure,
translate_with_args("log.external_test_failed", &[("test_type", test_type)]),
Some(test_type.to_string()),
);
}
}
pub fn should_run_test(&self) -> bool {
let last_test = *self.last_test_time.lock().unwrap();
Instant::now().duration_since(last_test) >= self.config.test_interval
}
pub fn start_real_time_monitoring(self: Arc<Self>) {
if !self.config.enable_real_time_monitoring {
return;
}
std::thread::spawn(move || {
loop {
if self.should_run_test() {
if let Err(e) = self.perform_health_check() {
log::error!(
"{}",
translate_with_args(
"log.health_check_failed",
&[("error", &e.to_string())]
)
);
}
}
std::thread::sleep(Duration::from_secs(60)); }
});
}
}
pub struct RngMonitorManager {
monitors: Arc<Mutex<Vec<Arc<RngMonitor>>>>,
}
impl RngMonitorManager {
pub fn new() -> Self {
Self {
monitors: Arc::new(Mutex::new(Vec::new())),
}
}
pub fn add_monitor(&self, monitor: Arc<RngMonitor>) {
self.monitors.lock().unwrap().push(monitor);
}
pub fn get_first_monitor(&self) -> Option<Arc<RngMonitor>> {
self.monitors.lock().ok()?.first().cloned()
}
#[allow(dead_code)]
pub fn get_all_health_metrics(&self) -> Vec<RngHealthMetrics> {
self.monitors
.lock()
.map(|guard| {
guard
.iter()
.map(|monitor| monitor.get_health_metrics())
.collect()
})
.unwrap_or_default()
}
#[allow(dead_code)]
pub fn perform_all_health_checks(&self) -> Result<Vec<bool>> {
let monitors = self.monitors.lock().unwrap();
let mut results = Vec::with_capacity(monitors.len());
for monitor in monitors.iter() {
match monitor.perform_health_check() {
Ok(result) => results.push(result),
Err(e) => {
log::error!(
"{}",
translate_with_args(
"log.monitor_health_check_failed",
&[("error", &e.to_string())]
)
);
results.push(false);
}
}
}
Ok(results)
}
}
impl Default for RngMonitorManager {
fn default() -> Self {
Self::new()
}
}