#[cfg(feature = "encrypt")]
use crate::error::CryptoError;
#[cfg(feature = "encrypt")]
use crate::error::Result;
#[cfg(feature = "encrypt")]
use crate::types::Algorithm;
use chrono::{DateTime, Utc};
#[cfg(feature = "encrypt")]
use std::collections::HashMap;
#[cfg(feature = "encrypt")]
use std::sync::Arc;
#[cfg(feature = "encrypt")]
use std::sync::Mutex;
#[cfg(feature = "encrypt")]
use crate::key::Key;
fn chi_squared_critical_value_99(df: f64) -> f64 {
const Z_99: f64 = 2.3263478740408408;
if df <= 0.0 {
return f64::INFINITY;
}
if df < 1.0 {
return f64::INFINITY;
}
let term = 1.0 - 2.0 / (9.0 * df) + Z_99 * (2.0 / (9.0 * df)).sqrt();
df * term.powi(3)
}
#[derive(Debug, Clone)]
pub struct SelfTestResult {
pub test_name: String,
pub passed: bool,
pub error_message: Option<String>,
pub timestamp: std::time::SystemTime,
}
#[cfg(feature = "encrypt")]
#[derive(Clone)]
pub struct FipsSelfTestEngine {
test_results: Arc<Mutex<HashMap<String, SelfTestResult>>>,
alert_threshold: Arc<AlertThreshold>,
alert_handler: Option<Arc<dyn AlertHandler + Send + Sync>>,
}
#[cfg(feature = "encrypt")]
impl FipsSelfTestEngine {
pub fn new() -> Self {
Self {
test_results: Arc::new(Mutex::new(HashMap::new())),
alert_threshold: Arc::new(AlertThreshold::default()),
alert_handler: None,
}
}
}
#[cfg(not(feature = "encrypt"))]
#[derive(Clone)]
pub struct FipsSelfTestEngine;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct AlertThreshold {
pub min_entropy_bits: f64, pub max_failures_per_hour: u32, pub max_consecutive_failures: u32, }
impl Default for AlertThreshold {
fn default() -> Self {
Self {
min_entropy_bits: 7.5, max_failures_per_hour: 5, max_consecutive_failures: 3, }
}
}
pub trait AlertHandler {
fn handle_alert(&self, alert: &Alert);
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Alert {
pub severity: AlertSeverity,
pub category: AlertCategory,
pub message: String,
pub timestamp: DateTime<Utc>,
pub test_name: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AlertSeverity {
Warning,
Critical,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AlertCategory {
EntropyDegradation,
TestFailure,
SystemMalfunction,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct NistTestResult {
pub passed: bool,
pub tests_passed: usize,
pub total_tests: usize,
pub entropy_bits: f64,
pub error_message: Option<String>,
}
impl Default for FipsSelfTestEngine {
fn default() -> Self {
#[cfg(feature = "encrypt")]
{
Self::new()
}
#[cfg(not(feature = "encrypt"))]
{
Self
}
}
}
#[cfg(feature = "encrypt")]
impl FipsSelfTestEngine {
#[allow(dead_code)]
pub fn set_alert_handler(&mut self, handler: Arc<dyn AlertHandler + Send + Sync>) {
self.alert_handler = Some(handler);
}
#[allow(dead_code)]
pub fn set_alert_threshold(&mut self, threshold: AlertThreshold) {
self.alert_threshold = Arc::new(threshold);
}
pub fn run_power_on_self_tests(&self) -> Result<()> {
let results = vec![
self.aes_kat_test()?,
self.sha_kat_test()?,
self.ecdsa_signature_test()?,
self.rsa_signature_test()?,
self.rng_health_test()?,
self.hmac_test()?,
self.kdf_test()?,
self.sm4_kat_test()?,
self.hardware_acceleration_test()?,
];
let mut test_results = self.test_results.lock().unwrap();
for result in &results {
test_results.insert(result.test_name.clone(), result.clone());
}
let failed_tests: Vec<_> = results.iter().filter(|r| !r.passed).collect();
if !failed_tests.is_empty() {
let error_messages: Vec<String> = failed_tests
.iter()
.map(|r| {
format!(
"{}: {}",
r.test_name,
r.error_message.as_deref().unwrap_or("Unknown error")
)
})
.collect();
return Err(CryptoError::FipsError(format!(
"FIPS POST failed: {}",
error_messages.join(", ")
)));
}
Ok(())
}
pub fn run_conditional_self_test(&self, algorithm: Algorithm) -> Result<()> {
match algorithm {
Algorithm::ECDSAP256 | Algorithm::ECDSAP384 => self.ecdsa_pairwise_consistency_test(),
Algorithm::RSA2048 | Algorithm::RSA3072 | Algorithm::RSA4096 => {
self.rsa_pairwise_consistency_test()
}
Algorithm::Ed25519 => self.ed25519_pairwise_consistency_test(),
Algorithm::AES128GCM | Algorithm::AES192GCM | Algorithm::AES256GCM => {
self.aes_kat_test()
}
_ => Ok(SelfTestResult {
test_name: format!("conditional_{:?}", algorithm),
passed: true,
error_message: None,
timestamp: std::time::SystemTime::now(),
}),
}
.map(|_| ())
}
pub fn run_periodic_tests(&self) -> Result<()> {
let results = vec![
self.aes_kat_test()?,
self.sha_kat_test()?,
self.rng_health_test()?,
];
let mut test_results = self.test_results.lock().unwrap();
for result in &results {
test_results.insert(result.test_name.clone(), result.clone());
}
if results.iter().any(|r| !r.passed) {
return Err(CryptoError::FipsError(
"FIPS periodic self test failed".to_string(),
));
}
Ok(())
}
fn aes_kat_test(&self) -> Result<SelfTestResult> {
let test_name = "aes_256_gcm_kat".to_string();
let timestamp = std::time::SystemTime::now();
let key_hex = "0000000000000000000000000000000000000000000000000000000000000000";
let iv_hex = "000000000000000000000000";
let plaintext_hex = "";
let aad_hex = "";
let expected_ciphertext_hex = "";
let expected_tag_hex = "530f8afbc74536b9a963b4f1c4cb738b";
let key_bytes = hex::decode(key_hex).unwrap();
let iv_bytes = hex::decode(iv_hex).unwrap();
let plaintext_bytes = hex::decode(plaintext_hex).unwrap();
let aad_bytes = hex::decode(aad_hex).unwrap();
use crate::cipher::aes::Aes256GcmProvider;
use crate::cipher::provider::SymmetricCipher;
let provider = Aes256GcmProvider::new()?;
let key = Key::new_active(Algorithm::AES256GCM, key_bytes)?;
let mut full_ciphertext = Vec::with_capacity(iv_bytes.len() + plaintext_bytes.len() + 16);
full_ciphertext.extend_from_slice(&iv_bytes);
full_ciphertext.extend_from_slice(&hex::decode(expected_ciphertext_hex).unwrap());
full_ciphertext.extend_from_slice(&hex::decode(expected_tag_hex).unwrap());
let decrypted = provider.decrypt(&key, &full_ciphertext, Some(&aad_bytes));
let passed = match decrypted {
Ok(dec) => dec == plaintext_bytes,
Err(_) => false,
};
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some("AES-GCM KAT validation failed: Decryption mismatch".to_string())
},
timestamp,
})
}
fn sha_kat_test(&self) -> Result<SelfTestResult> {
let test_name = "sha_256_kat".to_string();
let timestamp = std::time::SystemTime::now();
let input = b"abc";
let expected_output_hex =
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
use ring::digest::{Context, SHA256};
let mut context = Context::new(&SHA256);
context.update(input);
let digest = context.finish();
let actual_output_hex = hex::encode(digest.as_ref());
let passed = actual_output_hex == expected_output_hex;
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some(format!(
"SHA-256 KAT failed: expected {}, got {}",
expected_output_hex, actual_output_hex
))
},
timestamp,
})
}
fn sm4_kat_test(&self) -> Result<SelfTestResult> {
let test_name = String::from("sm4_ctr_kat");
let timestamp = std::time::SystemTime::now();
let key = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10,
];
let plaintext = [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10,
];
let expected_ciphertext = [
0x68, 0x1e, 0xdf, 0x34, 0xd2, 0x06, 0x96, 0x5e, 0x86, 0xb3, 0xe9, 0x4f, 0x53, 0x6e,
0x42, 0x46,
];
use libsm::sm4::cipher::Sm4Cipher;
let sm4 = Sm4Cipher::new(&key).unwrap();
let ciphertext = sm4.encrypt(&plaintext).unwrap();
let passed = ciphertext.to_vec() == expected_ciphertext.to_vec();
Ok(SelfTestResult {
test_name,
passed,
error_message: None,
timestamp,
})
}
#[cfg(feature = "encrypt")]
fn hardware_acceleration_test(&self) -> Result<SelfTestResult> {
let test_name = "hardware_acceleration_test".to_string();
let timestamp = std::time::SystemTime::now();
use crate::hardware::{CpuFeatures, AES_NI_SUPPORTED, AVX2_SUPPORTED, SHA_NI_SUPPORTED};
crate::hardware::init_cpu_features();
let detected_features = CpuFeatures::detect();
let stored_aes_ni = AES_NI_SUPPORTED.load(std::sync::atomic::Ordering::Relaxed);
let stored_avx2 = AVX2_SUPPORTED.load(std::sync::atomic::Ordering::Relaxed);
let stored_sha_ni = SHA_NI_SUPPORTED.load(std::sync::atomic::Ordering::Relaxed);
let consistency_check = detected_features.aes_ni == stored_aes_ni
&& detected_features.avx2 == stored_avx2
&& detected_features.sha_ni == stored_sha_ni;
let accelerated_hash_test = if detected_features.sha_ni || detected_features.avx2 {
let test_data = b"FIPS hardware acceleration test data";
let hash_result =
crate::hardware::accelerated_hash(test_data, crate::types::Algorithm::SHA256);
hash_result.is_ok()
} else {
true
};
let accelerated_aes_test = if detected_features.aes_ni {
let key = [0u8; 32];
let nonce = [0u8; 12];
let plaintext = b"test";
if let Ok(ciphertext) =
crate::hardware::accelerated_aes_encrypt(&key, plaintext, &nonce)
{
match crate::hardware::accelerated_aes_decrypt(&key, &ciphertext, &nonce) {
Ok(decrypted) => decrypted[..plaintext.len()] == *plaintext,
Err(_) => false,
}
} else {
false
}
} else {
true
};
let mut error_messages: Vec<String> = Vec::new();
if !consistency_check {
error_messages.push("CPU feature detection inconsistency".to_string());
}
if !accelerated_hash_test {
error_messages.push("Accelerated hash test failed".to_string());
}
if !accelerated_aes_test {
error_messages.push("Accelerated AES test failed".to_string());
}
#[cfg(feature = "gpu")]
{
let gpu_init_result = crate::hardware::init_gpu();
let gpu_enabled = crate::hardware::is_gpu_enabled();
if gpu_enabled && gpu_init_result.is_ok() {
use crate::hardware::gpu::kernels::{GpuKernel, KernelManager};
use crate::types::Algorithm;
let mut kernel_manager = KernelManager::new();
let mut aes_kernel = crate::hardware::gpu::kernels::AesKernel::default();
aes_kernel.initialize()?;
let aes_kernel = Arc::new(std::sync::RwLock::new(aes_kernel));
let mut hash_kernel = crate::hardware::gpu::kernels::HashKernel::default();
hash_kernel.initialize()?;
let hash_kernel = Arc::new(std::sync::RwLock::new(hash_kernel));
kernel_manager.register_kernel(aes_kernel);
kernel_manager.register_kernel(hash_kernel);
let aes_test_result: Result<bool> = {
let key = [0u8; 32];
let nonce = [0u8; 12];
let plaintext = b"test data for GPU AES encryption test";
kernel_manager
.get_kernel(Algorithm::AES256GCM)
.ok_or_else(|| {
CryptoError::HardwareAccelerationUnavailable("Kernel not found".into())
})
.and_then(|kernel| {
kernel
.write()
.unwrap()
.execute_aes_gcm_encrypt(&key, &nonce, plaintext, None)
})
.and_then(|ciphertext| {
kernel_manager
.get_kernel(Algorithm::AES256GCM)
.ok_or_else(|| {
CryptoError::HardwareAccelerationUnavailable(
"Kernel not found".into(),
)
})
.and_then(|kernel| {
kernel.write().unwrap().execute_aes_gcm_decrypt(
&key,
&nonce,
&ciphertext,
None,
)
})
.map(|decrypted| decrypted.as_slice() == plaintext)
})
};
match aes_test_result {
Ok(true) => {}
Ok(false) => {
error_messages.push(String::from("AES-GCM decrypt mismatch"));
}
Err(e) => {
error_messages.push(format!("AES-GCM failed: {}", e));
}
}
let hash_test_result: Result<bool> = {
let test_data = b"FIPS GPU hash test data for acceleration verification";
kernel_manager
.get_kernel(Algorithm::SHA256)
.ok_or_else(|| {
CryptoError::HardwareAccelerationUnavailable("Kernel not found".into())
})
.and_then(|kernel| {
kernel
.write()
.unwrap()
.execute_hash(test_data, Algorithm::SHA256)
})
.map(|hash| hash.len() == 32)
};
match hash_test_result {
Ok(true) => {}
Ok(false) => {
error_messages.push(String::from("GPU SHA256 hash length mismatch"));
}
Err(e) => {
error_messages.push(format!("GPU SHA256 hash failed: {}", e));
}
}
let batch_test_result: Result<bool> = {
let batch_data: Vec<Vec<u8>> = (0..32)
.map(|i| format!("batch test data item {}", i).into_bytes())
.collect();
kernel_manager
.get_kernel(Algorithm::SHA256)
.ok_or_else(|| {
CryptoError::HardwareAccelerationUnavailable("Kernel not found".into())
})
.and_then(|kernel| {
kernel
.write()
.unwrap()
.execute_hash_batch(&batch_data, Algorithm::SHA256)
})
.map(|hashes| {
let all_correct_length = hashes.iter().all(|h| h.len() == 32);
let count_correct = hashes.len() == 32;
if !count_correct {
error_messages.push(format!(
"Batch hash count mismatch: expected 32, got {}",
hashes.len()
));
}
all_correct_length && count_correct
})
};
match batch_test_result {
Ok(true) => {}
Ok(false) => {
error_messages.push(String::from("GPU batch hash failed"));
}
Err(e) => {
error_messages.push(format!("GPU batch hash failed: {}", e));
}
}
let _ = kernel_manager.shutdown_all();
}
let _ = crate::hardware::shutdown_gpu();
}
#[cfg(not(feature = "gpu"))]
{
let _ = crate::hardware::init_gpu();
}
let passed = consistency_check
&& accelerated_hash_test
&& accelerated_aes_test
&& error_messages.is_empty();
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some(error_messages.join("; "))
},
timestamp,
})
}
#[cfg(not(feature = "encrypt"))]
fn hardware_acceleration_test(&self) -> Result<SelfTestResult> {
Ok(SelfTestResult {
test_name: "hardware_acceleration_test".to_string(),
passed: true,
error_message: None,
timestamp: std::time::SystemTime::now(),
})
}
fn ecdsa_signature_test(&self) -> Result<SelfTestResult> {
let test_name = "ecdsa_p256_signature_test".to_string();
let timestamp = std::time::SystemTime::now();
use crate::cipher::provider::REGISTRY;
let algo = Algorithm::ECDSAP256;
let signer = REGISTRY.get_signer(algo)?;
let key_hex = "308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104205c0b313ded1bd01223a22c84ba0e5007277eb979de0b747f3cf1612255b74156a144034200049a0f0dc6d486d4db63a8c829f206168661d6a5b7da9b9cdcab62901bee0ba048f4d5e5caccc931fa063d0176c570c144b3f57a57347b99f608a0218be57c4753";
let key_bytes = hex::decode(key_hex).unwrap();
let key = Key::new_active(algo, key_bytes)?;
let message = b"test message for ECDSA";
let signature = signer.sign(&key, message)?;
let passed = signer.verify(&key, message, &signature)?;
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some("ECDSA signature test failed".to_string())
},
timestamp,
})
}
fn rsa_signature_test(&self) -> Result<SelfTestResult> {
let test_name = "rsa_2048_signature_test".to_string();
let timestamp = std::time::SystemTime::now();
use crate::cipher::provider::REGISTRY;
let algo = Algorithm::RSA2048;
let signer = REGISTRY.get_signer(algo)?;
let der_bytes = crate::key::openssl_rsa::generate_openssl_rsa_private_key(2048)
.map_err(|e| CryptoError::KeyError(format!("生成 RSA 密钥失败: {}", e)))?;
let pkcs8_bytes = crate::key::openssl_rsa::convert_rsa_der_to_pkcs8(&der_bytes)
.map_err(|e| CryptoError::KeyError(format!("转换为 PKCS#8 失败: {}", e)))?;
let private_key = Key::new_active(algo, pkcs8_bytes.clone())?;
let public_key = Key::new_active(algo, pkcs8_bytes)?;
let message = b"test message for RSA";
let signature = signer.sign(&private_key, message);
let passed = match signature {
Ok(sig) => signer.verify(&public_key, message, &sig).unwrap_or(false),
Err(_) => false,
};
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some("RSA 签名自检失败".to_string())
},
timestamp,
})
}
pub fn rng_health_test(&self) -> Result<SelfTestResult> {
let test_name = "rng_health_test".to_string();
let timestamp = std::time::SystemTime::now();
let mut random_bytes = vec![0u8; 100000];
if crate::random::SecureRandom::new()
.and_then(|rng| rng.fill(&mut random_bytes))
.is_err()
{
return Ok(SelfTestResult {
test_name,
passed: false,
error_message: Some("Failed to generate random bytes".to_string()),
timestamp,
});
}
let nist_result = self.nist_randomness_tests(&random_bytes);
let all_zeros = random_bytes.iter().all(|&b| b == 0);
let all_ones = random_bytes.iter().all(|&b| b == 0xFF);
let basic_passed = !all_zeros && !all_ones && random_bytes.len() == 100000;
let entropy_passed = nist_result.entropy_bits >= self.alert_threshold.min_entropy_bits;
let passed = basic_passed && nist_result.passed && entropy_passed;
if nist_result.entropy_bits < self.alert_threshold.min_entropy_bits {
self.trigger_alert(
AlertSeverity::Warning,
AlertCategory::EntropyDegradation,
format!("Low entropy detected: {:.2} bits", nist_result.entropy_bits),
Some(test_name.clone()),
);
}
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some(format!(
"RNG 健康测试失败: {}",
nist_result.error_message.unwrap_or_default()
))
},
timestamp,
})
}
pub fn nist_randomness_tests(&self, data: &[u8]) -> NistTestResult {
#[cfg(feature = "encrypt")]
{
let mut tests_passed = 0;
let mut total_tests = 0;
let mut error_messages = Vec::with_capacity(8);
total_tests += 1;
if self.frequency_test(data) {
tests_passed += 1;
} else {
error_messages.push("频率测试失败");
}
total_tests += 1;
if self.block_frequency_test(data, 128) {
tests_passed += 1;
} else {
error_messages.push("块内频率测试失败");
}
total_tests += 1;
if self.runs_test(data) {
tests_passed += 1;
} else {
error_messages.push("游程测试失败");
}
total_tests += 1;
if self.longest_run_test(data) {
tests_passed += 1;
} else {
error_messages.push("最长游程测试失败");
}
total_tests += 1;
if self.binary_matrix_rank_test(data) {
tests_passed += 1;
} else {
error_messages.push("二进制矩阵秩测试失败");
}
total_tests += 1;
if self.dft_test(data) {
tests_passed += 1;
} else {
error_messages.push("离散傅里叶变换测试失败");
}
total_tests += 1;
if self.non_overlapping_template_test(data, &[0, 1, 0, 0, 1]) {
tests_passed += 1;
} else {
error_messages.push("非重叠模板匹配测试失败");
}
total_tests += 1;
if self.overlapping_template_test(data, &[1, 1, 1, 1, 1]) {
tests_passed += 1;
} else {
error_messages.push("重叠模板匹配测试失败");
}
total_tests += 1;
if self.universal_statistical_test(data, 7) {
tests_passed += 1;
} else {
error_messages.push("通用统计测试失败");
}
total_tests += 1;
if self.linear_complexity_test(data, 500) {
tests_passed += 1;
} else {
error_messages.push("线性复杂度测试失败");
}
total_tests += 1;
if self.serial_test(data, 16) {
tests_passed += 1;
} else {
error_messages.push("序列测试失败");
}
total_tests += 1;
if self.approximate_entropy_test(data, 10) {
tests_passed += 1;
} else {
error_messages.push("近似熵测试失败");
}
total_tests += 1;
if self.cumulative_sums_test(data) {
tests_passed += 1;
} else {
error_messages.push("累加和测试失败");
}
total_tests += 1;
if self.random_excursion_test(data) {
tests_passed += 1;
} else {
error_messages.push("随机游走测试失败");
}
let entropy_bits = self.estimate_entropy(data);
NistTestResult {
passed: tests_passed >= total_tests * 2 / 3, tests_passed,
total_tests,
entropy_bits,
error_message: if error_messages.is_empty() {
None
} else {
Some(error_messages.join(", "))
},
}
}
#[cfg(not(feature = "encrypt"))]
{
let _ = data;
NistTestResult {
passed: true,
tests_passed: 14,
total_tests: 14,
entropy_bits: 8.0,
error_message: None,
}
}
}
fn trigger_alert(
&self,
severity: AlertSeverity,
category: AlertCategory,
message: String,
test_name: Option<String>,
) {
crate::audit::AuditLogger::log(
"RNG_SECURITY_ALERT",
None,
None,
Err(crate::CryptoError::FipsError(format!(
"[{:?}] Category: {:?}, Message: {}",
severity, category, message
))),
);
if let Some(handler) = &self.alert_handler {
let alert = Alert {
severity,
category,
message,
timestamp: Utc::now(),
test_name,
};
handler.handle_alert(&alert);
}
}
fn hmac_test(&self) -> Result<SelfTestResult> {
let test_name = "hmac_sha256_test".to_string();
let timestamp = std::time::SystemTime::now();
let key = hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
.unwrap();
let message = b"Sample message for keylen=blocklen";
let expected_mac_hex = "648c89dc60d3d2ee50b5a2d116fdb7583eb98dc1aa90aab3dff3ecfd02ac90be";
use ring::hmac;
let s_key = hmac::Key::new(hmac::HMAC_SHA256, &key);
let tag = hmac::sign(&s_key, message);
let actual_mac_hex = hex::encode(tag.as_ref());
let passed = actual_mac_hex == expected_mac_hex;
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some(format!(
"HMAC-SHA256 KAT failed: expected {}, got {}",
expected_mac_hex, actual_mac_hex
))
},
timestamp,
})
}
fn kdf_test(&self) -> Result<SelfTestResult> {
let test_name = "hkdf_test".to_string();
let timestamp = std::time::SystemTime::now();
let ikm = hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap();
let salt = hex::decode("000102030405060708090a0b0c").unwrap();
let info = hex::decode("f0f1f2f3f4f5f6f7f8f9").unwrap();
let expected_okm_hex = "3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf";
use ring::hkdf;
let salt = hkdf::Salt::new(hkdf::HKDF_SHA256, &salt);
let prk = salt.extract(&ikm);
let info_slice = [info.as_slice()];
let okm_iter = prk
.expand(&info_slice, hkdf::HKDF_SHA256)
.map_err(|_| CryptoError::InternalError("HKDF expansion failed".into()))?;
let mut actual_okm = vec![0u8; 32];
okm_iter
.fill(&mut actual_okm)
.map_err(|_| CryptoError::InternalError("HKDF fill failed".into()))?;
let actual_okm_hex = hex::encode(actual_okm);
let passed = actual_okm_hex == expected_okm_hex;
Ok(SelfTestResult {
test_name,
passed,
error_message: if passed {
None
} else {
Some(format!(
"HKDF KAT failed: expected {}, got {}",
expected_okm_hex, actual_okm_hex
))
},
timestamp,
})
}
fn ecdsa_pairwise_consistency_test(&self) -> Result<SelfTestResult> {
let test_name = "ecdsa_pairwise_consistency".to_string();
let timestamp = std::time::SystemTime::now();
#[cfg(feature = "encrypt")]
use crate::cipher::provider::REGISTRY;
#[cfg(feature = "encrypt")]
use crate::key::Key;
let mut all_passed = true;
let mut error_messages = Vec::with_capacity(8);
let test_cases = vec![
(Algorithm::ECDSAP256, "P-256 测试向量1"),
(Algorithm::ECDSAP384, "P-384 测试向量1"),
];
for (algo, test_vector_name) in test_cases {
let signer = REGISTRY.get_signer(algo)?;
let test_vectors: Vec<(&[u8], &str)> = vec![
(b"ECDSA pairwise consistency test message 1", "测试消息1"),
(b"ECDSA pairwise consistency test message 2", "测试消息2"),
(b"A longer test message to verify signature consistency across different message sizes", "长消息测试"),
(&[0u8; 32][..], "零消息测试"),
(&[0xFFu8; 64][..], "全1消息测试"),
];
let key_bytes = match algo {
Algorithm::ECDSAP256 => {
hex::decode("308187020100301306072a8648ce3d020106082a8648ce3d030107046d306b02010104205c0b313ded1bd01223a22c84ba0e5007277eb979de0b747f3cf1612255b74156a144034200049a0f0dc6d486d4db63a8c829f206168661d6a5b7da9b9cdcab62901bee0ba048f4d5e5caccc931fa063d0176c570c144b3f57a57347b99f608a0218be57c4753").unwrap()
},
Algorithm::ECDSAP384 => {
use ring::signature::EcdsaKeyPair;
let rng = ring::rand::SystemRandom::new();
let pkcs8_bytes = EcdsaKeyPair::generate_pkcs8(&ring::signature::ECDSA_P384_SHA384_FIXED_SIGNING, &rng)
.map_err(|e| CryptoError::KeyError(format!("Failed to generate ECDSA P-384 key: {}", e)))?;
pkcs8_bytes.as_ref().to_vec()
},
_ => return Err(CryptoError::UnsupportedAlgorithm(format!("Unsupported ECDSA algorithm: {:?}", algo))),
};
let key = Key::new_active(algo, key_bytes)?;
for (message, msg_desc) in &test_vectors {
match signer.sign(&key, message) {
Ok(signature) => {
match signer.verify(&key, message, &signature) {
Ok(verified) => {
if !verified {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: 签名验证失败",
test_vector_name, msg_desc, algo
));
}
}
Err(e) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: 签名验证错误: {}",
test_vector_name, msg_desc, algo, e
));
}
}
let wrong_message = b"This is a different message";
match signer.verify(&key, wrong_message, &signature) {
Ok(verified) => {
if verified {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: 错误消息验证应该失败但通过了",
test_vector_name, msg_desc, algo
));
}
}
Err(_) => {
}
}
}
Err(e) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: 签名失败: {}",
test_vector_name, msg_desc, algo, e
));
}
}
}
}
Ok(SelfTestResult {
test_name,
passed: all_passed,
error_message: if all_passed {
None
} else {
Some(error_messages.join("; "))
},
timestamp,
})
}
fn rsa_pairwise_consistency_test(&self) -> Result<SelfTestResult> {
let test_name = "rsa_pairwise_consistency".to_string();
let timestamp = std::time::SystemTime::now();
use crate::cipher::provider::REGISTRY;
use crate::key::Key;
let mut all_passed = true;
let mut error_messages = Vec::with_capacity(8);
let test_cases = vec![
(Algorithm::RSA2048, "RSA-2048 测试向量1"),
(Algorithm::RSA3072, "RSA-3072 测试向量1"),
(Algorithm::RSA4096, "RSA-4096 测试向量1"),
];
for (algo, test_vector_name) in test_cases {
let signer = REGISTRY.get_signer(algo)?;
let key_bytes = match algo {
Algorithm::RSA2048 | Algorithm::RSA3072 | Algorithm::RSA4096 => {
let bits = match algo {
Algorithm::RSA2048 => 2048,
Algorithm::RSA3072 => 3072,
Algorithm::RSA4096 => 4096,
_ => unreachable!(),
};
let der_bytes = crate::key::openssl_rsa::generate_openssl_rsa_private_key(bits)
.map_err(|e| {
CryptoError::KeyError(format!("Failed to generate RSA key: {}", e))
})?;
let pkcs8_bytes = crate::key::openssl_rsa::convert_rsa_der_to_pkcs8(&der_bytes)
.map_err(|e| {
CryptoError::KeyError(format!("Failed to convert to PKCS#8: {}", e))
})?;
pkcs8_bytes
}
_ => {
return Err(CryptoError::UnsupportedAlgorithm(format!(
"Unsupported RSA algorithm: {:?}",
algo
)))
}
};
let key = Key::new_active(algo, key_bytes)?;
let test_vectors = [
(
b"RSA pairwise consistency test message 1" as &[u8],
"test message 1",
),
(b"RSA pairwise consistency test message 2", "test message 2"),
(
b"A longer test message to verify signature consistency",
"long message test",
),
(&[0u8; 32], "zero message test"),
(&[0xFFu8; 64], "all ones message test"),
(b"", "empty message test"),
(b"Short", "short message test"),
];
for (message, description) in test_vectors.iter() {
let signature = signer.sign(&key, message)?;
let verify_result = signer.verify(&key, message, &signature);
match verify_result {
Ok(true) => {
let wrong_message =
b"This is a different message that should fail verification";
let wrong_verify = signer.verify(&key, wrong_message, &signature);
match wrong_verify {
Ok(false) => {
}
Ok(true) => {
all_passed = false;
error_messages.push(format!("{} - {} - {}: wrong message verification should fail but passed", test_vector_name, description, algo));
}
Err(e) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: wrong message verification error: {}",
test_vector_name, description, algo, e
));
}
}
}
Ok(false) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: signature verification failed",
test_vector_name, description, algo
));
}
Err(e) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - {}: verification error: {}",
test_vector_name, description, algo, e
));
}
}
}
}
let error_message = if all_passed {
None
} else {
Some(error_messages.join("; "))
};
let result = SelfTestResult {
test_name,
passed: all_passed,
error_message,
timestamp,
};
if let Ok(mut results) = self.test_results.lock() {
results.insert(result.test_name.clone(), result.clone());
}
Ok(result)
}
fn ed25519_pairwise_consistency_test(&self) -> Result<SelfTestResult> {
let test_name = "ed25519_pairwise_consistency".to_string();
let timestamp = std::time::SystemTime::now();
use crate::cipher::provider::REGISTRY;
use crate::key::Key;
let signer = REGISTRY.get_signer(Algorithm::Ed25519)?;
use ring::rand::SystemRandom;
use ring::signature::Ed25519KeyPair;
let rng = SystemRandom::new();
let pkcs8_bytes = Ed25519KeyPair::generate_pkcs8(&rng)
.map_err(|e| CryptoError::KeyError(format!("生成Ed25519密钥失败: {}", e)))?;
let key_bytes = pkcs8_bytes.as_ref().to_vec();
eprintln!(
"[DEBUG] Generated Ed25519 key bytes length: {}",
key_bytes.len()
);
let key = Key::new_active(Algorithm::Ed25519, key_bytes)?;
let test_vectors = [
(
b"Ed25519 pairwise consistency test message 1" as &[u8],
"Test message 1",
),
(
b"Ed25519 pairwise consistency test message 2",
"Test message 2",
),
(
b"A longer message to verify signature consistency",
"Long message test",
),
(&[0u8; 32], "Zero message test"),
(&[0xFFu8; 64], "All 1s message test"),
(b"", "Empty message test"),
(b"short", "Short message test"),
];
let mut all_passed = true;
let mut error_messages = Vec::with_capacity(8);
let test_vector_name = "Ed25519 测试向量";
for (message, description) in test_vectors.iter() {
eprintln!("[DEBUG] Processing test vector: {:?}", description);
eprintln!("[DEBUG] Message length: {}", message.len());
let signature = signer.sign(&key, message)?;
eprintln!("[DEBUG] Signature generated, length: {}", signature.len());
let verify_result = signer.verify(&key, message, &signature);
eprintln!("[DEBUG] Verification result: {:?}", verify_result);
match verify_result {
Ok(true) => {
let wrong_message = b"Another message that should fail verification";
let wrong_verify = signer.verify(&key, wrong_message, &signature);
match wrong_verify {
Ok(false) => {}
Ok(true) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - Ed25519: 错误消息验证应该失败但通过了",
test_vector_name, description
));
}
Err(_) => {}
}
}
Ok(false) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - Ed25519: 签名验证失败",
test_vector_name, description
));
}
Err(e) => {
all_passed = false;
error_messages.push(format!(
"{} - {} - Ed25519: 验证错误: {}",
test_vector_name, description, e
));
}
}
}
let pkcs8_bytes2 = Ed25519KeyPair::generate_pkcs8(&rng)
.map_err(|e| CryptoError::KeyError(format!("生成第二个Ed25519密钥失败: {}", e)))?;
let key_bytes2 = pkcs8_bytes2.as_ref().to_vec();
let key2 = Key::new_active(Algorithm::Ed25519, key_bytes2)?;
let message = b"Key rotation test message";
let signature1 = signer.sign(&key, message)?;
eprintln!(
"[DEBUG] Key rotation test - signature1 length: {}",
signature1.len()
);
let signature2 = signer.sign(&key2, message)?;
eprintln!(
"[DEBUG] Key rotation test - signature2 length: {}",
signature2.len()
);
if signature1 == signature2 {
all_passed = false;
error_messages.push("密钥轮换测试失败: 不同密钥生成了相同的签名".to_string());
}
eprintln!("[DEBUG] Testing cross-verification...");
eprintln!("[DEBUG] verify key1's signature with key2's public key...");
let verify_result1 = signer.verify(&key2, message, &signature1);
eprintln!("[DEBUG] verify1_with_2 result: {:?}", verify_result1);
let verify1_with_2 = match verify_result1 {
Ok(b) => b,
Err(e) => {
eprintln!("[DEBUG] verify1_with_2 error (expected): {}", e);
false }
};
eprintln!("[DEBUG] verify key2's signature with key1's public key...");
let verify_result2 = signer.verify(&key, message, &signature2);
eprintln!("[DEBUG] verify2_with_1 result: {:?}", verify_result2);
let verify2_with_1 = match verify_result2 {
Ok(b) => b,
Err(e) => {
eprintln!("[DEBUG] verify2_with_1 error (expected): {}", e);
false }
};
eprintln!(
"[DEBUG] verify1_with_2: {}, verify2_with_1: {}",
verify1_with_2, verify2_with_1
);
if verify1_with_2 || verify2_with_1 {
all_passed = false;
error_messages.push("密钥轮换测试失败: 密钥交叉验证通过了".to_string());
}
eprintln!("[DEBUG] All passed: {}", all_passed);
if !all_passed {
eprintln!("[DEBUG] Error messages: {:?}", error_messages);
}
let error_message = if all_passed {
None
} else {
Some(error_messages.join(", "))
};
let result = SelfTestResult {
test_name,
passed: all_passed,
error_message,
timestamp,
};
if let Ok(mut results) = self.test_results.lock() {
results.insert(result.test_name.clone(), result.clone());
}
Ok(result)
}
pub fn get_test_results(&self) -> HashMap<String, SelfTestResult> {
self.test_results.lock().unwrap().clone()
}
pub fn get_test_result(&self, test_name: &str) -> Option<SelfTestResult> {
self.test_results.lock().ok()?.get(test_name).cloned()
}
pub fn all_required_tests_passed(&self) -> bool {
let test_results = self.get_test_results();
let required_tests = vec![
"aes_256_gcm_kat",
"sha_256_kat",
"ecdsa_p256_signature_test",
"rsa_2048_signature_test",
"rng_health_test",
"hmac_sha256_test",
"hkdf_test",
"sm4_ctr_kat",
];
for test_name in required_tests {
match test_results.get(test_name) {
Some(result) => {
if !result.passed {
return false;
}
}
None => {
return false;
}
}
}
true
}
#[allow(dead_code)]
pub fn run_periodic_self_test(&self) -> Result<()> {
let rng_result = self.rng_health_test()?;
let aes_result = self.aes_kat_test()?;
let mut test_results = self.test_results.lock().unwrap();
test_results.insert(rng_result.test_name.clone(), rng_result);
test_results.insert(aes_result.test_name.clone(), aes_result);
Ok(())
}
fn frequency_test(&self, data: &[u8]) -> bool {
let ones = data.iter().map(|&b| b.count_ones() as u64).sum::<u64>();
let zeros = data.len() as u64 * 8 - ones;
let n = data.len() as u64 * 8;
if n == 0 {
return true;
}
let s = (ones as i64 - zeros as i64).abs();
let statistic = s as f64 / (n as f64).sqrt();
statistic < 3.291
}
fn block_frequency_test(&self, data: &[u8], block_size: usize) -> bool {
let num_blocks = data.len() * 8 / block_size;
if num_blocks == 0 {
return true;
}
let mut proportions = Vec::new();
for i in 0..num_blocks {
let start_bit = i * block_size;
let end_bit = start_bit + block_size;
let mut ones = 0;
for bit_idx in start_bit..end_bit {
let byte_idx = bit_idx / 8;
let bit_pos = bit_idx % 8;
if byte_idx < data.len() && (data[byte_idx] & (1 << (7 - bit_pos))) != 0 {
ones += 1;
}
}
proportions.push(ones as f64 / block_size as f64);
}
let chi_squared =
4.0 * block_size as f64 * proportions.iter().map(|&p| (p - 0.5).powi(2)).sum::<f64>();
let df = num_blocks as f64;
let threshold =
df * (1.0 - 2.0 / (9.0 * df) + 1.6448536269514722 * (2.0 / (9.0 * df)).sqrt()).powi(3);
chi_squared <= threshold
}
fn runs_test(&self, data: &[u8]) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let ones = bits.iter().filter(|&&b| b == 1).count() as f64;
let n = bits.len() as f64;
let pi = ones / n;
if n < 100.0 {
return true;
}
let mut runs = 1;
for i in 1..bits.len() {
if bits[i] != bits[i - 1] {
runs += 1;
}
}
let expected_runs = 2.0 * n * pi * (1.0 - pi);
if expected_runs <= 0.0 {
return true;
}
let variance = 2.0 * n * pi * (1.0 - pi) * (2.0 * n * pi * (1.0 - pi) - 1.0);
if variance <= 0.0 {
return true;
}
let z = (runs as f64 - expected_runs) / variance.sqrt();
z.abs() < 2.576 }
fn longest_run_test(&self, data: &[u8]) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let mut current_run = 0;
let mut max_run = 0;
for &bit in &bits {
if bit == 1 {
current_run += 1;
max_run = max_run.max(current_run);
} else {
current_run = 0;
}
}
(7..=26).contains(&max_run)
}
fn binary_matrix_rank_test(&self, data: &[u8]) -> bool {
let matrix_size = 32;
let num_matrices = data.len() * 8 / (matrix_size * matrix_size);
if num_matrices == 0 {
return true;
}
let mut full_rank_matrices = 0;
let mut rank_minus_one_matrices = 0;
for i in 0..num_matrices {
let start_bit = i * matrix_size * matrix_size;
let mut matrix = Vec::with_capacity(matrix_size);
for row in 0..matrix_size {
let mut row_val: u32 = 0;
for col in 0..matrix_size {
let bit_idx = start_bit + row * matrix_size + col;
let byte_idx = bit_idx / 8;
let bit_pos = bit_idx % 8;
if byte_idx < data.len() && (data[byte_idx] & (1 << (7 - bit_pos))) != 0 {
row_val |= 1 << (matrix_size - 1 - col);
}
}
matrix.push(row_val);
}
let mut rank = 0;
let mut row = 0;
let mut col = 0;
while row < matrix_size && col < matrix_size {
let mut pivot_row = row;
while pivot_row < matrix_size
&& (matrix[pivot_row] & (1 << (matrix_size - 1 - col))) == 0
{
pivot_row += 1;
}
if pivot_row < matrix_size {
matrix.swap(row, pivot_row);
let pivot_val = matrix[row];
for item in matrix.iter_mut().take(matrix_size).skip(row + 1) {
if (*item & (1 << (matrix_size - 1 - col))) != 0 {
*item ^= pivot_val;
}
}
rank += 1;
row += 1;
}
col += 1;
}
if rank == matrix_size {
full_rank_matrices += 1;
} else if rank == matrix_size - 1 {
rank_minus_one_matrices += 1;
}
}
let p_full = 0.2888;
let p_minus_one = 0.5776;
let p_remainder = 0.1336;
let chi_squared = (full_rank_matrices as f64 - p_full * num_matrices as f64).powi(2)
/ (p_full * num_matrices as f64)
+ (rank_minus_one_matrices as f64 - p_minus_one * num_matrices as f64).powi(2)
/ (p_minus_one * num_matrices as f64)
+ ((num_matrices - full_rank_matrices - rank_minus_one_matrices) as f64
- p_remainder * num_matrices as f64)
.powi(2)
/ (p_remainder * num_matrices as f64);
chi_squared < 9.21
}
fn dft_test(&self, data: &[u8]) -> bool {
use rustfft::{num_complex::Complex, FftPlanner};
let n = data.len() * 8;
let mut x: Vec<Complex<f64>> = Vec::with_capacity(n);
for &byte in data {
for i in 0..8 {
let bit = (byte >> (7 - i)) & 1;
x.push(Complex::new(if bit == 1 { 1.0 } else { -1.0 }, 0.0));
}
}
let mut planner = FftPlanner::new();
let fft = planner.plan_fft_forward(n);
fft.process(&mut x);
let mut peak_count = 0;
let threshold = 95.0;
for item in x.iter().take(n / 2).skip(1) {
let magnitude = item.norm();
if magnitude > threshold {
peak_count += 1;
}
}
let expected_peaks = (n as f64 * 0.05) * 0.95;
let tolerance = (n as f64 * 0.05) * 0.05 * 3.0;
(peak_count as f64 - expected_peaks).abs() <= tolerance
}
fn non_overlapping_template_test(&self, data: &[u8], template: &[u8]) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let m = template.len();
let n = bits.len();
if n < m {
return true;
}
let mut count = 0;
let mut i = 0;
while i <= n - m {
let mut matched = true;
for j in 0..m {
if bits[i + j] != template[j] {
matched = false;
break;
}
}
if matched {
count += 1;
i += m;
} else {
i += 1;
}
}
let num_blocks = (n - m + 1) as f64;
let expected = num_blocks / (2.0f64.powi(m as i32));
if expected < 5.0 {
return true;
}
let variance = expected * (1.0 - expected / num_blocks);
let std_dev = variance.sqrt();
if std_dev == 0.0 {
return true;
}
let z = (count as f64 - expected) / std_dev;
z.abs() < 3.291 }
fn overlapping_template_test(&self, data: &[u8], template: &[u8]) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let m = template.len();
let n = bits.len();
if n < m {
return true;
}
let mut count = 0;
for i in 0..=n - m {
let mut matched = true;
for j in 0..m {
if bits[i + j] != template[j] {
matched = false;
break;
}
}
if matched {
count += 1;
}
}
let expected = (n - m + 1) as f64 / (2.0f64.powi(m as i32));
let prob = 1.0 / 2.0f64.powi(m as i32);
let variance = (n as f64) * prob * (1.0 - prob) + 2.0 * (n as f64) * (prob.powi(2));
let z = (count as f64 - expected) / variance.sqrt();
z.abs() < 4.0
}
fn universal_statistical_test(&self, data: &[u8], l: usize) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let q = 10 * (1 << l);
let k = (bits.len() / l).saturating_sub(q);
if k < 1000 {
return true;
}
let mut table = vec![0; 1 << l];
for i in 0..q {
let mut val = 0;
for j in 0..l {
val = (val << 1) | bits[i * l + j] as usize;
}
table[val] = i + 1;
}
let mut sum = 0.0;
for i in q..(q + k) {
let mut val = 0;
for j in 0..l {
val = (val << 1) | bits[i * l + j] as usize;
}
let dist = i + 1 - table[val];
sum += (dist as f64).log2();
table[val] = i + 1;
}
let fn_val = sum / k as f64;
let expected = 6.1962507;
let variance = 3.125;
let c = 0.7 - 0.8 / l as f64
+ (4.0 + 32.0 / l as f64) * (k as f64).powf(-3.0 / l as f64) / 15.0;
let sigma = c * (variance / k as f64).sqrt();
let statistic = (fn_val - expected).abs() / sigma;
statistic < 3.0 }
fn linear_complexity_test(&self, data: &[u8], block_size: usize) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let n = bits.len();
let num_blocks = n / block_size;
if num_blocks < 10 {
return true; }
let mut buckets = [0; 7];
let pi = [
0.01047, 0.03125, 0.12500, 0.50000, 0.25000, 0.06250, 0.020833,
];
let mu = block_size as f64 / 2.0
+ (9.0
+ if block_size.is_multiple_of(2) {
1.0
} else {
-1.0
})
/ 36.0
- (block_size as f64 / 3.0 + 2.0 / 9.0) / 2.0f64.powi(block_size as i32);
for i in 0..num_blocks {
let block = &bits[i * block_size..(i + 1) * block_size];
let mut l = 0;
let mut m = -1i32;
let mut b = vec![0u8; block_size];
let mut c = vec![0u8; block_size];
let mut p = vec![0u8; block_size];
b[0] = 1;
c[0] = 1;
for j in 0..block_size {
let mut d = block[j];
for k in 1..=l {
d ^= c[k] & block[j - k];
}
if d == 1 {
p.copy_from_slice(&c);
let shift = (j as i32 - m) as usize;
if shift < block_size {
for k in 0..block_size - shift {
c[k + shift] ^= b[k];
}
}
if l as i32 <= j as i32 / 2 {
l = j + 1 - l;
m = j as i32;
b.copy_from_slice(&p);
}
}
}
let t = if block_size.is_multiple_of(2) {
1.0
} else {
-1.0
} * (l as f64 - mu)
+ 2.0 / 9.0;
if t <= -2.5 {
buckets[0] += 1;
} else if t <= -1.5 {
buckets[1] += 1;
} else if t <= -0.5 {
buckets[2] += 1;
} else if t <= 0.5 {
buckets[3] += 1;
} else if t <= 1.5 {
buckets[4] += 1;
} else if t <= 2.5 {
buckets[5] += 1;
} else {
buckets[6] += 1;
}
}
let mut chi_squared = 0.0;
for i in 0..7 {
let expected = num_blocks as f64 * pi[i];
chi_squared += (buckets[i] as f64 - expected).powi(2) / expected;
}
chi_squared < 16.812
}
fn serial_test(&self, data: &[u8], m: usize) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let n = bits.len();
if n < m * 4 {
return true;
}
let get_psi_sq = |m_len: usize| -> f64 {
if m_len == 0 {
return 0.0;
}
let mut counts = std::collections::HashMap::new();
let mut extended_bits = bits.clone();
for item in bits.iter().take(m_len - 1) {
extended_bits.push(*item);
}
for i in 0..n {
let mut pattern = 0usize;
for j in 0..m_len {
pattern = (pattern << 1) | extended_bits[i + j] as usize;
}
*counts.entry(pattern).or_insert(0) += 1;
}
let _expected = n as f64 / (1 << m_len) as f64;
let mut sum_sq = 0.0;
for i in 0..(1 << m_len) {
let count = *counts.get(&i).unwrap_or(&0);
sum_sq += (count as f64).powi(2);
}
(1 << m_len) as f64 / n as f64 * sum_sq - n as f64
};
let psi_sq_m = get_psi_sq(m);
let psi_sq_m_minus_1 = get_psi_sq(m - 1);
let psi_sq_m_minus_2 = get_psi_sq(m - 2);
let delta1 = psi_sq_m - psi_sq_m_minus_1;
let delta2 = psi_sq_m - 2.0 * psi_sq_m_minus_1 + psi_sq_m_minus_2;
let df1 = (1 << (m - 2)) as f64;
let df2 = (1 << (m - 3)) as f64;
let z_95 = 1.6448536269514722;
let threshold1 =
df1 * (1.0 - 2.0 / (9.0 * df1) + z_95 * (2.0 / (9.0 * df1)).sqrt()).powi(3);
let threshold2 =
df2 * (1.0 - 2.0 / (9.0 * df2) + z_95 * (2.0 / (9.0 * df2)).sqrt()).powi(3);
delta1 < threshold1 && delta2 < threshold2
}
fn approximate_entropy_test(&self, data: &[u8], m: usize) -> bool {
let bits: Vec<u8> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let n = bits.len();
if n < m * 10 {
return true;
}
let get_phi = |m_len: usize| -> f64 {
let mut counts = std::collections::HashMap::new();
let mut extended_bits = bits.clone();
for item in bits.iter().take(m_len - 1) {
extended_bits.push(*item);
}
for i in 0..n {
let mut pattern = 0usize;
for j in 0..m_len {
pattern = (pattern << 1) | extended_bits[i + j] as usize;
}
*counts.entry(pattern).or_insert(0) += 1;
}
let mut sum = 0.0;
for count in counts.values() {
let p = *count as f64 / n as f64;
sum += p * p.ln(); }
sum
};
let phi_m = get_phi(m);
let phi_m_plus_1 = get_phi(m + 1);
let apen = phi_m - phi_m_plus_1;
let chi_sq = 2.0 * n as f64 * (2.0f64.ln() - apen);
let df = (1 << m) as f64;
let threshold = chi_squared_critical_value_99(df);
chi_sq < threshold
}
fn cumulative_sums_test(&self, data: &[u8]) -> bool {
let bits: Vec<i32> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| if (b >> (7 - i)) & 1 != 0 { 1 } else { -1 }))
.collect();
let n = bits.len();
if n == 0 {
return true;
}
let mut s = 0;
let mut max_s = 0;
for &bit in &bits {
s += bit;
max_s = max_s.max(s.abs());
}
let mut s_rev = 0;
let mut max_s_rev = 0;
for &bit in bits.iter().rev() {
s_rev += bit;
max_s_rev = max_s_rev.max(s_rev.abs());
}
let limit = 4.0 * (n as f64).sqrt();
max_s as f64 <= limit && max_s_rev as f64 <= limit
}
fn random_excursion_test(&self, data: &[u8]) -> bool {
let bits: Vec<i32> = data
.iter()
.flat_map(|&b| (0..8).map(move |i| if (b >> (7 - i)) & 1 != 0 { 1 } else { -1 }))
.collect();
let n = bits.len();
if n == 0 {
return true;
}
let mut s = vec![0; n + 2];
s[0] = 0;
for i in 0..n {
s[i + 1] = s[i] + bits[i];
}
s[n + 1] = 0;
let mut _j = 0;
let mut zero_indices = Vec::with_capacity(n / 2);
for (i, &val) in s.iter().enumerate().take(n + 1).skip(1) {
if val == 0 {
zero_indices.push(i);
}
}
let j_count = zero_indices.len();
if j_count < 5 {
return true; }
let states = [-4, -3, -2, -1, 1, 2, 3, 4];
let mut passed = true;
for &x in &states {
let mut counts = std::collections::HashMap::new();
let mut last_idx = 0;
for &curr_idx in &zero_indices {
let count_in_cycle = s[last_idx + 1..=curr_idx]
.iter()
.filter(|&&val| val == x)
.count();
*counts.entry(count_in_cycle).or_insert(0) += 1;
last_idx = curr_idx;
}
let total_visits: usize = counts.iter().map(|(&k, &v)| k * v).sum();
let diff = (total_visits as f64 - j_count as f64).abs();
if diff > 5.0 * (j_count as f64).sqrt() {
passed = false;
}
}
passed
}
fn estimate_entropy(&self, data: &[u8]) -> f64 {
let mut byte_counts = [0usize; 256];
for &byte in data {
byte_counts[byte as usize] += 1;
}
let total = data.len() as f64;
let mut entropy = 0.0;
for &count in &byte_counts {
if count > 0 {
let p = count as f64 / total;
entropy -= p * p.log2();
}
}
entropy
}
#[allow(dead_code)]
fn estimate_linear_complexity(&self, sequence: &[u8]) -> usize {
let n = sequence.len();
if n == 0 {
return 0;
}
let bits: Vec<u8> = sequence
.iter()
.flat_map(|&b| (0..8).map(move |i| (b >> (7 - i)) & 1))
.collect();
let len = bits.len();
let mut b = vec![0u8; len]; let mut c = vec![0u8; len]; let mut t = vec![0u8; len];
b[0] = 1;
c[0] = 1;
let mut l = 0;
let mut m = -1i32;
for n_step in 0..len {
let mut d = bits[n_step];
for i in 1..=l {
if c[i] == 1 {
d ^= bits[n_step - i];
}
}
if d == 1 {
t.copy_from_slice(&c);
let shift = (n_step as i32 - m) as usize;
for i in 0..len - shift {
if b[i] == 1 {
c[i + shift] ^= 1;
}
}
if 2 * l <= n_step {
l = n_step + 1 - l;
m = n_step as i32;
b.copy_from_slice(&t);
}
}
}
l
}
#[allow(dead_code)]
fn compute_phi(&self, bits: &[u8], m: usize) -> f64 {
if bits.len() < m {
return 0.0;
}
let mut patterns = std::collections::HashMap::new();
for i in 0..bits.len().saturating_sub(m) {
let pattern: String = bits[i..i + m].iter().map(|&b| b.to_string()).collect();
*patterns.entry(pattern).or_insert(0) += 1;
}
let total = (bits.len() - m) as f64;
patterns
.values()
.map(|&count| {
let p = count as f64 / total;
p * p.log2()
})
.sum::<f64>()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ecdsa_self_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.ecdsa_signature_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_rsa_self_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.rsa_signature_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_aes_kat_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.aes_kat_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_sha_kat_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.sha_kat_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_sm4_kat_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.sm4_kat_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_hmac_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.hmac_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_kdf_test() {
let engine = FipsSelfTestEngine::new();
let result = engine.kdf_test().unwrap();
assert!(result.passed);
}
#[test]
fn test_rng_health_test() {
let engine = FipsSelfTestEngine::new();
let mut passed_count = 0;
let total_runs = 3;
let mut all_errors: Vec<String> = Vec::new();
for i in 0..total_runs {
match engine.rng_health_test() {
Ok(result) => {
if result.passed {
passed_count += 1;
} else if let Some(msg) = &result.error_message {
all_errors.push(format!("Run {}: {}", i + 1, msg));
}
}
Err(e) => {
all_errors.push(format!("Run {}: {:?}", i + 1, e));
}
}
}
let pass_rate = passed_count as f64 / total_runs as f64;
if pass_rate >= 0.66 {
return;
}
let failure_message = if all_errors.is_empty() {
"All test runs failed without error messages".to_string()
} else {
all_errors.join("; ")
};
panic!(
"RNG health test failed: only {}/{} runs passed ({:.1}%). \
This may indicate a real issue with the RNG or statistical fluctuation. \
Errors: {}",
passed_count,
total_runs,
pass_rate * 100.0,
failure_message
);
}
#[test]
fn test_run_power_on_self_tests() {
let engine = FipsSelfTestEngine::new();
let result = engine.run_power_on_self_tests();
if let Err(e) = &result {
println!("上电自检失败,错误: {:?}", e);
}
assert!(result.is_ok(), "上电自检失败: {:?}", result.err());
}
#[test]
fn test_pairwise_consistency_tests() {
let engine = FipsSelfTestEngine::new();
let ecdsa_result = engine.ecdsa_pairwise_consistency_test().unwrap();
if !ecdsa_result.passed {
panic!("ECDSA 成对一致性测试失败: {:?}", ecdsa_result.error_message);
}
assert!(ecdsa_result.passed);
let ed25519_result = engine.ed25519_pairwise_consistency_test().unwrap();
if !ed25519_result.passed {
panic!(
"Ed25519 成对一致性测试失败: {:?}",
ed25519_result.error_message
);
}
assert!(ed25519_result.passed);
let _rsa_result = engine.rsa_pairwise_consistency_test();
}
#[test]
fn test_nist_randomness_tests() {
let engine = FipsSelfTestEngine::new();
let mut data = vec![0u8; 1000];
for (i, item) in data.iter_mut().enumerate() {
*item = (i % 256) as u8;
}
let result1 = engine.nist_randomness_tests(&data);
assert!(result1.total_tests > 0);
let data_zeros = vec![0u8; 1000];
let result2 = engine.nist_randomness_tests(&data_zeros);
assert!(result2.entropy_bits < 1.0);
let mut data_rand = vec![0u8; 1000];
for (i, item) in data_rand.iter_mut().enumerate() {
*item = (i * 31 + 17) as u8;
}
let result3 = engine.nist_randomness_tests(&data_rand);
assert!(result3.total_tests > 0);
}
#[test]
fn test_all_required_tests_passed() {
let engine = FipsSelfTestEngine::new();
assert!(!engine.all_required_tests_passed());
engine.run_power_on_self_tests().unwrap();
assert!(engine.all_required_tests_passed());
}
#[test]
fn test_periodic_self_test() {
let engine = FipsSelfTestEngine::new();
assert!(engine.run_periodic_self_test().is_ok());
let results = engine.get_test_results();
assert!(results.contains_key("rng_health_test"));
assert!(results.contains_key("aes_256_gcm_kat"));
}
#[test]
fn test_get_results() {
let engine = FipsSelfTestEngine::new();
engine.run_power_on_self_tests().unwrap();
let results = engine.get_test_results();
assert!(!results.is_empty());
let aes_result = engine.get_test_result("aes_256_gcm_kat");
assert!(aes_result.is_some());
assert!(aes_result.unwrap().passed);
let non_existent = engine.get_test_result("non_existent_test");
assert!(non_existent.is_none());
}
#[test]
fn test_alert_threshold_configuration() {
let mut engine = FipsSelfTestEngine::new();
let threshold = AlertThreshold {
min_entropy_bits: 6.0,
max_failures_per_hour: 10,
max_consecutive_failures: 5,
};
engine.set_alert_threshold(threshold.clone());
assert_eq!(engine.alert_threshold.min_entropy_bits, 6.0);
assert_eq!(engine.alert_threshold.max_failures_per_hour, 10);
}
#[test]
fn test_run_conditional_self_test() {
let engine = FipsSelfTestEngine::new();
assert!(engine
.run_conditional_self_test(Algorithm::AES256GCM)
.is_ok());
assert!(engine
.run_conditional_self_test(Algorithm::ECDSAP256)
.is_ok());
assert!(engine.run_conditional_self_test(Algorithm::Ed25519).is_ok());
let _ = engine.run_conditional_self_test(Algorithm::RSA2048);
assert!(engine.run_conditional_self_test(Algorithm::SM4GCM).is_ok());
}
#[test]
fn test_alert_handling() {
struct MockHandler {
called: std::sync::atomic::AtomicBool,
}
impl AlertHandler for MockHandler {
fn handle_alert(&self, _alert: &Alert) {
self.called.store(true, std::sync::atomic::Ordering::SeqCst);
}
}
let handler = Arc::new(MockHandler {
called: std::sync::atomic::AtomicBool::new(false),
});
let mut engine = FipsSelfTestEngine::new();
engine.set_alert_handler(handler.clone());
engine.trigger_alert(
AlertSeverity::Warning,
AlertCategory::TestFailure,
"测试告警".to_string(),
None,
);
assert!(handler.called.load(std::sync::atomic::Ordering::SeqCst));
}
#[test]
fn test_dft_implementation_details() {
let engine = FipsSelfTestEngine::new();
let mut random_data = vec![0u8; 10000]; let mut state: u32 = 0xDEADBEEF;
for x in random_data.iter_mut() {
state = state.wrapping_mul(1664525).wrapping_add(1013904223);
*x = (state >> 24) as u8;
}
if random_data.len() >= 1000 {
let _ = engine.dft_test(&random_data);
}
let periodic_data = vec![0xAAu8; 2500];
let passed_periodic = engine.dft_test(&periodic_data);
assert!(!passed_periodic, "周期性数据应该失败 DFT 测试");
let zero_data = vec![0u8; 2500];
let passed_zeros = engine.dft_test(&zero_data);
assert!(!passed_zeros, "全零数据应该失败 DFT 测试");
}
}