use crate::error::{CryptoError, Result};
use crate::i18n::{translate, translate_with_args};
use crate::types::Algorithm;
use chrono::{DateTime, Utc};
use lazy_static::lazy_static;
use prometheus::{Counter, Histogram, HistogramOpts, Registry};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::mpsc::{channel, Sender};
use std::sync::{Arc, Mutex, RwLock};
use std::thread;
lazy_static! {
pub static ref REGISTRY: Registry = Registry::new();
pub static ref CRYPTO_OPERATIONS_TOTAL: Counter = Counter::new(
"crypto_operations_total",
"Total number of cryptographic operations"
)
.expect("Failed to create CRYPTO_OPERATIONS_TOTAL metric");
pub static ref CRYPTO_OPERATION_LATENCY: Histogram = Histogram::with_opts(HistogramOpts::new(
"crypto_operation_latency_seconds",
"Latency of cryptographic operations in seconds"
))
.expect("Failed to create CRYPTO_OPERATION_LATENCY metric");
pub static ref SECURITY_ALERTS_TOTAL: Counter =
Counter::new("security_alerts_total", "Total number of security alerts")
.expect("Failed to create SECURITY_ALERTS_TOTAL metric");
}
fn sanitize_error_for_log(error: &CryptoError) -> String {
match error {
CryptoError::InvalidKeySize { .. } => "Invalid key size - operation rejected".to_string(),
CryptoError::InvalidParameter(msg) => {
if msg.contains("key") || msg.contains("secret") || msg.contains("password") {
"Invalid parameter - operation rejected".to_string()
} else {
msg.clone()
}
}
CryptoError::InvalidState(msg) => {
if msg.contains("key") || msg.contains("memory") {
"Invalid state - operation rejected".to_string()
} else {
msg.clone()
}
}
CryptoError::DecryptionFailed(_) => {
"Decryption operation failed - invalid key or corrupted data".to_string()
}
CryptoError::EncryptionFailed(_) => "Encryption operation failed".to_string(),
CryptoError::KeyNotFound(_) => "Key not found - key_id: [REDACTED]".to_string(),
CryptoError::KeyError(_) => "Key operation failed".to_string(),
CryptoError::UnsupportedAlgorithm(msg) => {
format!(
"Unsupported algorithm: {}",
msg.split_whitespace().next().unwrap_or("unknown")
)
}
CryptoError::MemoryProtectionFailed(_) => {
"Memory protection failure - security violation detected".to_string()
}
CryptoError::MemoryAllocationFailed(_) => {
"Memory allocation failed - operation aborted".to_string()
}
CryptoError::MemoryTransferFailed(_) => {
"Memory transfer failed - data corruption possible".to_string()
}
CryptoError::MemoryTampered => "Memory tampering detected - security alert".to_string(),
CryptoError::FipsError(_) => "FIPS compliance violation detected".to_string(),
CryptoError::SideChannelError(_) => "Side-channel attack detected or prevented".to_string(),
CryptoError::NotImplemented(_) => "Operation not implemented".to_string(),
CryptoError::IoError(_) => "I/O operation failed".to_string(),
CryptoError::TimeError => "System time error - operation rejected".to_string(),
CryptoError::PluginError(_) => "Plugin operation failed".to_string(),
CryptoError::InternalError(_) => "Internal error occurred".to_string(),
CryptoError::SigningFailed(_) => "Signing operation failed".to_string(),
CryptoError::VerificationFailed(_) => "Verification operation failed".to_string(),
CryptoError::UnknownError => "Unknown error occurred".to_string(),
CryptoError::InsufficientEntropy => {
"Insufficient entropy for cryptographic operation".to_string()
}
CryptoError::KeyUsageLimitExceeded { .. } => {
"Key usage limit exceeded - operation rejected".to_string()
}
CryptoError::SecurityError(_) => "Security error detected - operation rejected".to_string(),
CryptoError::InvalidKeyLength(_) => "Invalid key length - operation rejected".to_string(),
CryptoError::HardwareAccelerationUnavailable(_) => {
"Hardware acceleration unavailable - using software fallback".to_string()
}
CryptoError::AsyncOperationFailed(_) => "Async operation failed".to_string(),
CryptoError::InvalidInput(_) => "Invalid input - operation rejected".to_string(),
CryptoError::NotInitialized => "System not initialized - operation rejected".to_string(),
CryptoError::HardwareInitializationFailed(_) => {
"Hardware initialization failed - operation rejected".to_string()
}
}
}
#[allow(dead_code)]
fn register_metrics() {
if let Err(e) = REGISTRY.register(Box::new(CRYPTO_OPERATIONS_TOTAL.clone())) {
eprintln!("Failed to register CRYPTO_OPERATIONS_TOTAL: {}", e);
}
if let Err(e) = REGISTRY.register(Box::new(CRYPTO_OPERATION_LATENCY.clone())) {
eprintln!("Failed to register CRYPTO_OPERATION_LATENCY: {}", e);
}
if let Err(e) = REGISTRY.register(Box::new(SECURITY_ALERTS_TOTAL.clone())) {
eprintln!("Failed to register SECURITY_ALERTS_TOTAL: {}", e);
}
}
lazy_static! {
static ref LOGGER: AuditLogger = AuditLogger::new();
static ref PERFORMANCE_MONITOR: Arc<PerformanceMonitor> = Arc::new(PerformanceMonitor::new());
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PerformanceStats {
pub total_operations: u64,
pub avg_latency_us: f64,
pub min_latency_us: u64,
pub max_latency_us: u64,
pub avg_throughput_ops_per_sec: f64,
pub avg_cache_hit_rate: f64,
pub total_data_processed_bytes: u64,
pub performance_trend: String,
}
#[derive(Serialize, Deserialize)]
struct OperationMetrics {
total_latency_us: u64,
min_latency_us: u64,
max_latency_us: u64,
operation_count: u64,
total_data_size: u64,
cache_hits: u64,
cache_misses: u64,
}
impl Default for OperationMetrics {
fn default() -> Self {
Self {
total_latency_us: 0,
min_latency_us: u64::MAX,
max_latency_us: 0,
operation_count: 0,
total_data_size: 0,
cache_hits: 0,
cache_misses: 0,
}
}
}
impl OperationMetrics {
#[allow(dead_code)]
fn update(&mut self, latency_us: u64, data_size: usize, cache_hit: bool) {
self.total_latency_us += latency_us;
self.min_latency_us = self.min_latency_us.min(latency_us);
self.max_latency_us = self.max_latency_us.max(latency_us);
self.operation_count += 1;
self.total_data_size += data_size as u64;
if cache_hit {
self.cache_hits += 1;
} else {
self.cache_misses += 1;
}
}
#[allow(dead_code)]
fn to_stats(&self) -> PerformanceStats {
let avg_latency = if self.operation_count > 0 {
self.total_latency_us as f64 / self.operation_count as f64
} else {
0.0
};
let avg_throughput = if self.operation_count > 0 {
(self.operation_count as f64 * 1_000_000.0) / self.total_latency_us as f64
} else {
0.0
};
let avg_cache_hit_rate = if self.cache_hits + self.cache_misses > 0 {
self.cache_hits as f64 / (self.cache_hits + self.cache_misses) as f64
} else {
0.0
};
let performance_trend = if avg_throughput > 1000.0 {
"improving"
} else if avg_throughput > 500.0 {
"stable"
} else {
"degrading"
};
PerformanceStats {
total_operations: self.operation_count,
avg_latency_us: avg_latency,
min_latency_us: if self.operation_count > 0 {
self.min_latency_us
} else {
0
},
max_latency_us: self.max_latency_us,
avg_throughput_ops_per_sec: avg_throughput,
avg_cache_hit_rate,
total_data_processed_bytes: self.total_data_size,
performance_trend: performance_trend.to_string(),
}
}
}
#[derive(Clone)]
pub struct PerformanceMonitor {
metrics: Arc<RwLock<HashMap<String, OperationMetrics>>>,
}
impl Default for PerformanceMonitor {
fn default() -> Self {
Self::new()
}
}
impl PerformanceMonitor {
pub fn new() -> Self {
Self {
metrics: Arc::new(RwLock::new(HashMap::new())),
}
}
#[allow(dead_code)]
pub fn record_operation(
&self,
operation: &str,
algo: Option<Algorithm>,
latency_us: u64,
data_size: usize,
cache_hit: bool,
) {
let key = format!("{}_{:?}", operation, algo);
match self.metrics.write() {
Ok(mut metrics) => {
metrics
.entry(key)
.or_default()
.update(latency_us, data_size, cache_hit);
}
Err(poisoned) => {
log::warn!("{}", translate("log.monitor_lock_poisoned"));
let mut metrics = poisoned.into_inner();
metrics
.entry(key)
.or_default()
.update(latency_us, data_size, cache_hit);
}
}
CRYPTO_OPERATIONS_TOTAL.inc();
CRYPTO_OPERATION_LATENCY.observe(latency_us as f64 / 1_000_000.0);
}
#[allow(dead_code)]
pub fn get_stats(&self, operation: &str, algo: Option<Algorithm>) -> Option<PerformanceStats> {
let key = format!("{}_{:?}", operation, algo);
match self.metrics.read() {
Ok(metrics) => metrics.get(&key).map(|m| m.to_stats()),
Err(poisoned) => {
log::warn!("{}", translate("log.monitor_read_lock_poisoned"));
let metrics = poisoned.into_inner();
metrics.get(&key).map(|m| m.to_stats())
}
}
}
#[allow(dead_code)]
pub fn get_all_stats(&self) -> HashMap<String, PerformanceStats> {
match self.metrics.read() {
Ok(metrics) => metrics
.iter()
.map(|(k, v)| (k.clone(), v.to_stats()))
.collect(),
Err(poisoned) => {
log::warn!("{}", translate("log.monitor_read_lock_poisoned"));
let metrics = poisoned.into_inner();
metrics
.iter()
.map(|(k, v)| (k.clone(), v.to_stats()))
.collect()
}
}
}
#[allow(dead_code)]
pub fn reset_stats(&self, operation: &str, algo: Option<Algorithm>) {
let key = format!("{}_{:?}", operation, algo);
match self.metrics.write() {
Ok(mut metrics) => {
metrics.remove(&key);
}
Err(poisoned) => {
log::warn!("{}", translate("log.monitor_write_lock_poisoned"));
let mut metrics = poisoned.into_inner();
metrics.remove(&key);
}
}
}
#[allow(dead_code)]
pub fn reset_all_stats(&self) {
match self.metrics.write() {
Ok(mut metrics) => {
metrics.clear();
}
Err(poisoned) => {
log::warn!("{}", translate("log.monitor_write_lock_poisoned"));
let mut metrics = poisoned.into_inner();
metrics.clear();
}
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditLog {
pub timestamp: DateTime<Utc>,
pub operation: String,
pub algorithm: Option<Algorithm>,
pub key_id: Option<String>,
pub tenant_id: Option<String>,
pub status: String,
pub details: String,
pub access_type: String,
}
pub struct AuditLogger {
sender: Arc<Mutex<Sender<String>>>,
sync_buffer: Arc<Mutex<Vec<String>>>,
_handle: Option<thread::JoinHandle<()>>, fallback_enabled: Arc<Mutex<bool>>, }
impl Default for AuditLogger {
fn default() -> Self {
Self::new()
}
}
impl AuditLogger {
fn send_with_fallback(&self, json: String) {
match self.sender.lock() {
Ok(sender) => {
match sender.send(json.clone()) {
Ok(_) => {
if let Ok(mut fallback) = self.fallback_enabled.lock() {
*fallback = false;
}
}
Err(_) => {
log::warn!("{}", translate("log.audit_channel_closed"));
if let Ok(mut fallback) = self.fallback_enabled.lock() {
*fallback = true;
}
self.store_in_sync_buffer(json);
}
}
}
Err(_) => {
log::warn!("{}", translate("log.audit_sender_lock_failed"));
if let Ok(mut fallback) = self.fallback_enabled.lock() {
*fallback = true;
}
self.store_in_sync_buffer(json);
}
}
}
fn store_in_sync_buffer(&self, json: String) {
match self.sync_buffer.lock() {
Ok(mut buf) => {
if buf.len() < 1000 {
buf.push(json);
}
}
Err(poisoned) => {
log::warn!("{}", translate("log.audit_sync_buffer_poisoned"));
let mut buf = poisoned.into_inner();
if buf.len() < 1000 {
buf.push(json);
}
}
}
}
pub fn new() -> Self {
let (sender, receiver): (
std::sync::mpsc::Sender<String>,
std::sync::mpsc::Receiver<String>,
) = channel();
let handle = thread::spawn(move || {
for log_entry in receiver {
log::info!(
"{}",
translate_with_args("audit.audit_entry", &[("entry", log_entry.as_str())])
);
}
log::warn!("{}", translate("log.audit_background_terminated"));
});
Self {
sender: Arc::new(Mutex::new(sender)),
sync_buffer: Arc::new(Mutex::new(Vec::with_capacity(100))),
_handle: Some(handle),
fallback_enabled: Arc::new(Mutex::new(false)),
}
}
pub fn init() {
log::info!("{}", translate("log.audit_initialized"));
}
pub fn log_with_tenant(
operation: &str,
algo: Option<Algorithm>,
key_id: Option<&str>,
tenant_id: Option<&str>,
result: Result<()>,
access_type: &str,
) {
debug_assert!(!operation.is_empty(), "Operation should not be empty");
debug_assert!(
operation.len() <= 100,
"Operation should not exceed 100 characters"
);
debug_assert!(!access_type.is_empty(), "Access type should not be empty");
debug_assert!(
access_type.len() <= 50,
"Access type should not exceed 50 characters"
);
if let Some(key_id) = key_id {
debug_assert!(
!key_id.is_empty(),
"Key ID should not be empty when provided"
);
debug_assert!(
key_id.len() <= 256,
"Key ID should not exceed 256 characters"
);
}
if let Some(tenant_id) = tenant_id {
debug_assert!(
!tenant_id.is_empty(),
"Tenant ID should not be empty when provided"
);
debug_assert!(
tenant_id.len() <= 128,
"Tenant ID should not exceed 128 characters"
);
}
let entry = AuditLog {
timestamp: Utc::now(),
operation: operation.to_string(),
algorithm: algo,
key_id: key_id.map(|s| s.to_string()),
tenant_id: tenant_id.map(|s| s.to_string()),
status: if result.is_ok() { "SUCCESS" } else { "FAILURE" }.to_string(),
details: result
.err()
.map(|e| sanitize_error_for_log(&e))
.unwrap_or_default(),
access_type: access_type.to_string(),
};
if let Ok(json) = serde_json::to_string(&entry) {
LOGGER.store_in_sync_buffer(json.clone());
LOGGER.send_with_fallback(json);
}
}
pub fn log(operation: &str, algo: Option<Algorithm>, key_id: Option<&str>, result: Result<()>) {
let entry = AuditLog {
timestamp: Utc::now(),
operation: operation.to_string(),
algorithm: algo,
key_id: key_id.map(|s| s.to_string()),
tenant_id: None,
status: if result.is_ok() { "SUCCESS" } else { "FAILURE" }.to_string(),
details: result
.err()
.map(|e| sanitize_error_for_log(&e))
.unwrap_or_default(),
access_type: "system".to_string(),
};
if let Ok(json) = serde_json::to_string(&entry) {
LOGGER.store_in_sync_buffer(json.clone());
LOGGER.send_with_fallback(json.clone());
log::info!(
"{}",
translate_with_args("audit.entry", &[("entry", &json)])
);
}
}
#[allow(dead_code)]
pub fn log_authorized_access(
operation: &str,
algo: Option<Algorithm>,
key_id: Option<&str>,
tenant_id: Option<&str>,
details: &str,
access_type: &str,
) {
let entry = AuditLog {
timestamp: Utc::now(),
operation: operation.to_string(),
algorithm: algo,
key_id: key_id.map(|s| s.to_string()),
tenant_id: tenant_id.map(|s| s.to_string()),
status: "SUCCESS".to_string(),
details: details.to_string(),
access_type: access_type.to_string(),
};
if let Ok(json) = serde_json::to_string(&entry) {
LOGGER.store_in_sync_buffer(json.clone());
LOGGER.send_with_fallback(json);
}
}
pub fn log_unauthorized_access(
operation: &str,
algo: Option<Algorithm>,
key_id: Option<&str>,
tenant_id: Option<&str>,
details: &str,
) {
let entry = AuditLog {
timestamp: Utc::now(),
operation: operation.to_string(),
algorithm: algo,
key_id: key_id.map(|s| s.to_string()),
tenant_id: tenant_id.map(|s| s.to_string()),
status: "UNAUTHORIZED".to_string(),
details: format!("SECURITY ALERT: {}", details),
access_type: "unauthorized".to_string(),
};
if let Ok(json) = serde_json::to_string(&entry) {
SECURITY_ALERTS_TOTAL.inc();
LOGGER.store_in_sync_buffer(json.clone());
LOGGER.send_with_fallback(json.clone());
log::warn!(
"{}",
translate_with_args("audit.security_alert", &[("alert", &json)])
);
}
}
#[allow(dead_code)]
pub fn log_key_operation(
operation: &str,
algo: Algorithm,
key_id: &str,
tenant_id: Option<&str>,
success: bool,
details: &str,
) {
let entry = AuditLog {
timestamp: Utc::now(),
operation: operation.to_string(),
algorithm: Some(algo),
key_id: Some(key_id.to_string()),
tenant_id: tenant_id.map(|s| s.to_string()),
status: if success { "SUCCESS" } else { "FAILURE" }.to_string(),
details: details.to_string(),
access_type: "key_operation".to_string(),
};
if let Ok(json) = serde_json::to_string(&entry) {
LOGGER.store_in_sync_buffer(json.clone());
LOGGER.send_with_fallback(json.clone());
log::info!(
"{}",
translate_with_args("audit.entry", &[("entry", &json)])
);
}
}
#[allow(dead_code)]
pub fn get_logs() -> Vec<String> {
match LOGGER.sync_buffer.lock() {
Ok(buffer) => {
let logs = buffer.clone();
for (i, log) in logs.iter().enumerate() {
if log.contains("KEY_GENERATE") {
log::debug!(
"{}",
translate_with_args(
"audit.key_generate_found",
&[("index", &i.to_string())]
)
);
}
}
logs
}
Err(poisoned) => {
log::warn!("{}", translate("log.audit_sync_buffer_poisoned"));
let buffer = poisoned.into_inner();
buffer.clone()
}
}
}
#[allow(dead_code)]
pub fn clear_logs() {
match LOGGER.sync_buffer.lock() {
Ok(mut buffer) => buffer.clear(),
Err(poisoned) => {
log::warn!("{}", translate("log.audit_sync_buffer_poisoned"));
let mut buffer = poisoned.into_inner();
buffer.clear();
}
}
}
#[allow(dead_code)]
pub fn gather_metrics() -> Result<String> {
use prometheus::Encoder;
let encoder = prometheus::TextEncoder::new();
let metric_families = REGISTRY.gather();
let mut buffer = Vec::new();
encoder
.encode(&metric_families, &mut buffer)
.map_err(|e| CryptoError::InternalError(format!("Failed to encode metrics: {}", e)))?;
String::from_utf8(buffer)
.map_err(|e| CryptoError::InternalError(format!("Invalid UTF-8 in metrics: {}", e)))
}
#[allow(dead_code)]
pub fn start_exporter(port: u16) {
use std::io::{Read, Write};
use std::net::SocketAddr;
use std::net::TcpListener;
use std::thread;
register_metrics();
let addr = SocketAddr::from(([127, 0, 0, 1], port));
thread::spawn(move || {
let addr_str = addr.to_string();
let listener = match TcpListener::bind(addr) {
Ok(l) => l,
Err(e) => {
log::error!(
"{}",
translate_with_args(
"audit.prometheus_bind_failed",
&[("addr", &addr_str), ("error", &e.to_string())]
)
);
return;
}
};
log::info!(
"{}",
translate_with_args("audit.prometheus_listening", &[("addr", &addr_str)])
);
for stream in listener.incoming() {
match stream {
Ok(mut stream) => {
let mut buffer = [0; 1024];
match stream.read(&mut buffer) {
Ok(n) if n > 0 => {
let metrics = match Self::gather_metrics() {
Ok(m) => m,
Err(e) => {
log::error!(
"{}",
translate_with_args(
"audit.prometheus_gather_failed",
&[("error", &e.to_string())]
)
);
let error_msg = translate_with_args(
"audit.prometheus_gather_failed",
&[("error", &e.to_string())],
);
let response = format!(
"HTTP/1.1 500 Internal Server Error\r\nContent-Type: text/plain\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
error_msg.len(),
error_msg
);
if let Err(write_err) =
stream.write_all(response.as_bytes())
{
log::error!(
"{}",
translate_with_args(
"audit.prometheus_write_response_failed",
&[("error", &write_err.to_string())]
)
);
}
let _ = stream.flush();
continue;
}
};
let response = format!(
"HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4\r\nContent-Length: {}\r\nConnection: close\r\n\r\n{}",
metrics.len(),
metrics
);
if let Err(e) = stream.write_all(response.as_bytes()) {
log::error!(
"{}",
translate_with_args(
"audit.prometheus_write_response_failed",
&[("error", &e.to_string())]
)
);
}
let _ = stream.flush();
}
_ => {}
}
}
Err(e) => {
log::error!(
"{}",
translate_with_args(
"audit.prometheus_accept_failed",
&[("error", &e.to_string())]
)
);
}
}
}
});
}
}
#[allow(dead_code)]
pub fn record_operation(
operation: &str,
algo: Option<Algorithm>,
latency_us: u64,
data_size: usize,
cache_hit: bool,
) {
PERFORMANCE_MONITOR.record_operation(operation, algo, latency_us, data_size, cache_hit);
}
#[allow(dead_code)]
pub fn get_performance_stats(operation: &str, algo: Option<Algorithm>) -> Option<PerformanceStats> {
PERFORMANCE_MONITOR.get_stats(operation, algo)
}
#[allow(dead_code)]
pub fn get_all_performance_stats() -> HashMap<String, PerformanceStats> {
PERFORMANCE_MONITOR.get_all_stats()
}
#[allow(dead_code)]
pub fn reset_performance_stats(operation: &str, algo: Option<Algorithm>) {
PERFORMANCE_MONITOR.reset_stats(operation, algo);
}
#[allow(dead_code)]
pub fn reset_all_performance_stats() {
PERFORMANCE_MONITOR.reset_all_stats();
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
use std::thread;
#[test]
fn test_performance_monitor_basic() {
let monitor = PerformanceMonitor::new();
monitor.record_operation("encrypt", Some(Algorithm::AES256GCM), 1000, 1024, true);
monitor.record_operation("encrypt", Some(Algorithm::AES256GCM), 1200, 1024, false);
monitor.record_operation("decrypt", Some(Algorithm::AES256GCM), 800, 1024, true);
let stats = monitor
.get_stats("encrypt", Some(Algorithm::AES256GCM))
.unwrap();
assert_eq!(stats.total_operations, 2);
assert!(stats.avg_latency_us > 0.0);
assert!(stats.avg_throughput_ops_per_sec > 0.0);
}
#[test]
fn test_performance_monitor_multiple_operations() {
let monitor = PerformanceMonitor::new();
let handles: Vec<_> = (0..100)
.map(|i| {
let monitor = monitor.clone();
thread::spawn(move || {
monitor.record_operation(
"test_op",
Some(Algorithm::AES256GCM),
1000 + (i * 10) as u64,
1024,
i % 2 == 0,
);
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
let stats = monitor
.get_stats("test_op", Some(Algorithm::AES256GCM))
.unwrap();
assert_eq!(stats.total_operations, 100);
assert!(stats.avg_latency_us > 0.0);
}
#[test]
fn test_performance_stats_reset() {
let monitor = PerformanceMonitor::new();
monitor.record_operation("encrypt", Some(Algorithm::AES256GCM), 1000, 1024, true);
let stats = monitor.get_stats("encrypt", Some(Algorithm::AES256GCM));
assert!(stats.is_some());
monitor.reset_stats("encrypt", Some(Algorithm::AES256GCM));
let stats = monitor.get_stats("encrypt", Some(Algorithm::AES256GCM));
assert!(stats.is_none());
}
#[test]
fn test_audit_logger_basic() {
let test_key = format!(
"test_key_basic_{}",
Utc::now().timestamp_nanos_opt().unwrap_or(0)
);
let initial_logs: HashSet<String> = AuditLogger::get_logs().into_iter().collect();
println!("Initial log count: {}", initial_logs.len());
AuditLogger::log(
"KEY_GENERATE",
Some(Algorithm::AES256GCM),
Some(&test_key),
Ok(()),
);
AuditLogger::log(
"ENCRYPT",
Some(Algorithm::AES256GCM),
Some(&test_key),
Err(CryptoError::InternalError("test error".into())),
);
let all_logs: HashSet<String> = AuditLogger::get_logs().into_iter().collect();
println!("Total log count after operations: {}", all_logs.len());
let new_logs: Vec<String> = all_logs.difference(&initial_logs).cloned().collect();
println!("New logs added by this test: {}", new_logs.len());
let mut keygen_logs = Vec::with_capacity(8);
let mut encrypt_logs = Vec::with_capacity(16);
let mut debug_logs = Vec::with_capacity(32);
for log_str in &new_logs {
if let Ok(audit_log) = serde_json::from_str::<AuditLog>(log_str) {
if audit_log.key_id.as_ref() == Some(&test_key) {
debug_logs.push(format!(
"Found log: operation={}, key_id={:?}, status={}",
audit_log.operation, audit_log.key_id, audit_log.status
));
if audit_log.operation == "KEY_GENERATE" {
keygen_logs.push(log_str.clone());
} else if audit_log.operation == "ENCRYPT" {
encrypt_logs.push(log_str.clone());
}
}
}
}
println!("Debug logs for key {}:", test_key);
for debug_log in debug_logs {
println!(" {}", debug_log);
}
println!("Total new logs in buffer: {}", new_logs.len());
println!("KEY_GENERATE logs found: {}", keygen_logs.len());
println!("ENCRYPT logs found: {}", encrypt_logs.len());
assert!(
!keygen_logs.is_empty(),
"Should have at least 1 KEY_GENERATE log for key {}. Found {} total new logs, {} matching keygen filter",
test_key,
new_logs.len(),
keygen_logs.len()
);
assert!(
!encrypt_logs.is_empty(),
"Should have at least 1 ENCRYPT log for {}",
test_key
);
let audit_log: AuditLog = serde_json::from_str(&keygen_logs[0]).unwrap();
assert_eq!(audit_log.operation, "KEY_GENERATE");
assert_eq!(audit_log.status, "SUCCESS");
}
#[test]
fn test_audit_logger_concurrent() {
AuditLogger::clear_logs();
let test_prefix = format!(
"concurrent_key_{}",
Utc::now().timestamp_nanos_opt().unwrap_or(0)
);
let test_prefix_clone = test_prefix.clone();
let handles: Vec<_> = (0..100)
.map(|i| {
let prefix = test_prefix_clone.clone();
thread::spawn(move || {
AuditLogger::log(
"test_op",
Some(Algorithm::AES256GCM),
Some(&format!("{}_{}", prefix, i)),
if i % 2 == 0 {
Ok(())
} else {
Err(CryptoError::InternalError("test error".into()))
},
);
})
})
.collect();
for handle in handles {
handle.join().unwrap();
}
thread::sleep(std::time::Duration::from_millis(500));
let logs = AuditLogger::get_logs();
let test_logs: Vec<_> = logs
.iter()
.filter(|log| log.contains(&test_prefix))
.collect();
assert!(
test_logs.len() >= 90,
"Expected at least 90 logs for this test, found {}. Total logs in buffer: {}",
test_logs.len(),
logs.len()
);
let success_count = test_logs
.iter()
.filter(|log| log.contains("SUCCESS"))
.count();
let failure_count = test_logs
.iter()
.filter(|log| log.contains("FAILURE"))
.count();
assert!(
(40..=60).contains(&success_count),
"Expected success count between 40-60, got {}. Total test logs: {}",
success_count,
test_logs.len()
);
assert!(
(40..=60).contains(&failure_count),
"Expected failure count between 40-60, got {}. Total test logs: {}",
failure_count,
test_logs.len()
);
assert!(success_count > 0, "Expected at least one success log");
assert!(failure_count > 0, "Expected at least one failure log");
}
#[test]
fn test_audit_logger_unauthorized_access() {
AuditLogger::clear_logs();
AuditLogger::log_unauthorized_access(
"KEY_ACCESS",
Some(Algorithm::AES256GCM),
Some("test_key"),
Some("tenant_123"),
"Test unauthorized access",
);
let logs = AuditLogger::get_logs();
assert!(
!logs.is_empty(),
"Expected at least 1 log, got {}",
logs.len()
);
let unauthorized_log = logs
.iter()
.find(|log| log.contains("UNAUTHORIZED"))
.expect("Should find an unauthorized access log");
let audit_log: AuditLog = serde_json::from_str(unauthorized_log).unwrap();
assert_eq!(audit_log.status, "UNAUTHORIZED");
assert!(audit_log.details.contains("SECURITY ALERT"));
assert_eq!(audit_log.tenant_id, Some("tenant_123".to_string()));
}
#[test]
fn test_performance_monitor_cache_simulation() {
let monitor = PerformanceMonitor::new();
for i in 0..100 {
monitor.record_operation(
"encrypt",
Some(Algorithm::AES256GCM),
1000,
1024,
i % 3 == 0, );
}
let stats = monitor
.get_stats("encrypt", Some(Algorithm::AES256GCM))
.unwrap();
assert_eq!(stats.total_operations, 100);
assert!(stats.avg_cache_hit_rate > 0.3 && stats.avg_cache_hit_rate < 0.4);
}
#[test]
fn test_performance_trend_calculation() {
let monitor = PerformanceMonitor::new();
for i in 0..50 {
monitor.record_operation(
"encrypt",
Some(Algorithm::AES256GCM),
2000 - (i * 20), 1024,
true,
);
}
let stats = monitor
.get_stats("encrypt", Some(Algorithm::AES256GCM))
.unwrap();
assert_eq!(stats.performance_trend, "stable"); }
#[test]
fn test_recent_metrics_retrieval() {
let monitor = PerformanceMonitor::new();
monitor.record_operation("encrypt", Some(Algorithm::AES128GCM), 1000, 1024, true);
monitor.record_operation("encrypt", Some(Algorithm::AES256GCM), 1200, 1024, false);
monitor.record_operation("decrypt", Some(Algorithm::SM4GCM), 800, 1024, true);
let all_stats = monitor.get_all_stats();
assert_eq!(all_stats.len(), 3);
assert!(all_stats.contains_key(&format!("encrypt_{:?}", Some(Algorithm::AES128GCM))));
assert!(all_stats.contains_key(&format!("encrypt_{:?}", Some(Algorithm::AES256GCM))));
assert!(all_stats.contains_key(&format!("decrypt_{:?}", Some(Algorithm::SM4GCM))));
}
#[test]
fn test_global_performance_functions() {
reset_all_performance_stats();
record_operation("test_op", Some(Algorithm::AES256GCM), 1000, 1024, true);
let stats = get_performance_stats("test_op", Some(Algorithm::AES256GCM));
assert!(stats.is_some());
assert_eq!(stats.unwrap().total_operations, 1);
reset_performance_stats("test_op", Some(Algorithm::AES256GCM));
let stats = get_performance_stats("test_op", Some(Algorithm::AES256GCM));
assert!(stats.is_none());
}
}