#![deny(unsafe_code)]
#![deny(missing_docs)]
#![deny(clippy::unwrap_used)]
#![deny(clippy::panic)]
use crate::prelude::error::{LatticeArcError, Result};
use subtle::ConstantTimeEq;
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SelfTestResult {
Pass,
Fail(String),
}
impl SelfTestResult {
#[must_use]
pub fn is_pass(&self) -> bool {
matches!(self, SelfTestResult::Pass)
}
#[must_use]
pub fn is_fail(&self) -> bool {
matches!(self, SelfTestResult::Fail(_))
}
pub fn to_result(&self) -> Result<()> {
match self {
SelfTestResult::Pass => Ok(()),
SelfTestResult::Fail(msg) => Err(LatticeArcError::ValidationError {
message: format!("FIPS 140-3 self-test failed: {}", msg),
}),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IndividualTestResult {
pub algorithm: String,
pub result: SelfTestResult,
pub duration_us: Option<u64>,
}
#[derive(Debug, Clone)]
pub struct SelfTestReport {
pub overall_result: SelfTestResult,
pub tests: Vec<IndividualTestResult>,
pub total_duration_us: u64,
}
#[must_use]
pub fn run_power_up_tests() -> SelfTestResult {
if let Err(e) = integrity_test() {
return SelfTestResult::Fail(format!("Module Integrity Test failed: {}", e));
}
if let Err(e) = kat_sha256() {
return SelfTestResult::Fail(format!("SHA-256 KAT failed: {}", e));
}
if let Err(e) = kat_hkdf_sha256() {
return SelfTestResult::Fail(format!("HKDF-SHA256 KAT failed: {}", e));
}
if let Err(e) = kat_aes_256_gcm() {
return SelfTestResult::Fail(format!("AES-256-GCM KAT failed: {}", e));
}
if let Err(e) = kat_sha3_256() {
return SelfTestResult::Fail(format!("SHA3-256 KAT failed: {}", e));
}
if let Err(e) = kat_hmac_sha256() {
return SelfTestResult::Fail(format!("HMAC-SHA256 KAT failed: {}", e));
}
if let Err(e) = kat_ml_kem_768() {
return SelfTestResult::Fail(format!("ML-KEM-768 KAT failed: {}", e));
}
if let Err(e) = kat_ml_dsa() {
return SelfTestResult::Fail(format!("ML-DSA KAT failed: {}", e));
}
if let Err(e) = kat_slh_dsa() {
return SelfTestResult::Fail(format!("SLH-DSA KAT failed: {}", e));
}
if let Err(e) = kat_fn_dsa() {
return SelfTestResult::Fail(format!("FN-DSA KAT failed: {}", e));
}
SelfTestResult::Pass
}
#[must_use]
pub fn run_power_up_tests_with_report() -> SelfTestReport {
use std::time::Instant;
fn duration_to_us(duration: std::time::Duration) -> u64 {
u64::try_from(duration.as_micros()).unwrap_or(u64::MAX)
}
let start = Instant::now();
let mut tests = Vec::new();
let mut overall_pass = true;
let sha_start = Instant::now();
let sha_result = match kat_sha256() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "SHA-256".to_string(),
result: sha_result,
duration_us: Some(duration_to_us(sha_start.elapsed())),
});
let hkdf_start = Instant::now();
let hkdf_result = match kat_hkdf_sha256() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "HKDF-SHA256".to_string(),
result: hkdf_result,
duration_us: Some(duration_to_us(hkdf_start.elapsed())),
});
let aes_start = Instant::now();
let aes_result = match kat_aes_256_gcm() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "AES-256-GCM".to_string(),
result: aes_result,
duration_us: Some(duration_to_us(aes_start.elapsed())),
});
let sha3_start = Instant::now();
let sha3_result = match kat_sha3_256() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "SHA3-256".to_string(),
result: sha3_result,
duration_us: Some(duration_to_us(sha3_start.elapsed())),
});
let hmac_start = Instant::now();
let hmac_result = match kat_hmac_sha256() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "HMAC-SHA256".to_string(),
result: hmac_result,
duration_us: Some(duration_to_us(hmac_start.elapsed())),
});
let kem_start = Instant::now();
let kem_result = match kat_ml_kem_768() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "ML-KEM-768".to_string(),
result: kem_result,
duration_us: Some(duration_to_us(kem_start.elapsed())),
});
let mldsa_start = Instant::now();
let mldsa_result = match kat_ml_dsa() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "ML-DSA-44".to_string(),
result: mldsa_result,
duration_us: Some(duration_to_us(mldsa_start.elapsed())),
});
let slhdsa_start = Instant::now();
let slhdsa_result = match kat_slh_dsa() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "SLH-DSA-SHAKE-128s".to_string(),
result: slhdsa_result,
duration_us: Some(duration_to_us(slhdsa_start.elapsed())),
});
let fndsa_start = Instant::now();
let fndsa_result = match kat_fn_dsa() {
Ok(()) => SelfTestResult::Pass,
Err(e) => {
overall_pass = false;
SelfTestResult::Fail(e.to_string())
}
};
tests.push(IndividualTestResult {
algorithm: "FN-DSA-512".to_string(),
result: fndsa_result,
duration_us: Some(duration_to_us(fndsa_start.elapsed())),
});
let overall_result = if overall_pass {
SelfTestResult::Pass
} else {
let failed: Vec<_> =
tests.iter().filter(|t| t.result.is_fail()).map(|t| t.algorithm.clone()).collect();
SelfTestResult::Fail(format!("Failed tests: {}", failed.join(", ")))
};
SelfTestReport { overall_result, tests, total_duration_us: duration_to_us(start.elapsed()) }
}
pub fn kat_sha256() -> Result<()> {
use crate::primitives::hash::sha256;
const INPUT: &[u8] = b"abc";
const EXPECTED: [u8; 32] = [
0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22,
0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00,
0x15, 0xad,
];
let result = sha256(INPUT).map_err(|e| LatticeArcError::ValidationError {
message: format!("SHA-256 KAT: hash computation failed: {}", e),
})?;
if bool::from(result.ct_eq(&EXPECTED)) {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "SHA-256 KAT: computed hash does not match expected value".to_string(),
})
}
}
pub fn kat_hkdf_sha256() -> Result<()> {
use crate::primitives::kdf::hkdf;
const IKM: [u8; 22] = [
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
];
const SALT: [u8; 13] =
[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c];
const INFO: [u8; 10] = [0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9];
const EXPECTED_OKM: [u8; 42] = [
0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f,
0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4,
0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65,
];
let result = hkdf(&IKM, Some(&SALT), Some(&INFO), 42)?;
if bool::from(result.key().ct_eq(&EXPECTED_OKM)) {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "HKDF-SHA256 KAT: derived key does not match expected value".to_string(),
})
}
}
pub fn kat_sha3_256() -> Result<()> {
use crate::primitives::hash::sha3::sha3_256;
const INPUT: &[u8] = b"abc";
const EXPECTED: [u8; 32] = [
0x3a, 0x98, 0x5d, 0xa7, 0x4f, 0xe2, 0x25, 0xb2, 0x04, 0x5c, 0x17, 0x2d, 0x6b, 0xd3, 0x90,
0xbd, 0x85, 0x5f, 0x08, 0x6e, 0x3e, 0x9d, 0x52, 0x5b, 0x46, 0xbf, 0xe2, 0x45, 0x11, 0x43,
0x15, 0x32,
];
let result = sha3_256(INPUT);
if bool::from(result.ct_eq(&EXPECTED)) {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "SHA3-256 KAT: computed hash does not match expected value".to_string(),
})
}
}
pub fn kat_hmac_sha256() -> Result<()> {
use crate::primitives::mac::hmac::hmac_sha256;
const KEY: &[u8] = b"Jefe";
const DATA: &[u8] = b"what do ya want for nothing?";
const EXPECTED: [u8; 32] = [
0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e, 0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75,
0xc7, 0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83, 0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec,
0x38, 0x43,
];
let result = hmac_sha256(KEY, DATA).map_err(|e| LatticeArcError::ValidationError {
message: format!("HMAC-SHA256 KAT: computation failed: {}", e),
})?;
if bool::from(result.ct_eq(&EXPECTED)) {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "HMAC-SHA256 KAT: computed HMAC does not match expected value".to_string(),
})
}
}
pub fn kat_aes_256_gcm() -> Result<()> {
use crate::primitives::aead::{AeadCipher, aes_gcm::AesGcm256};
const KEY: [u8; 32] = [
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
0x1e, 0x1f,
];
const NONCE: [u8; 12] =
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
const PLAINTEXT: &[u8] = b"FIPS 140-3 KAT";
const AAD: &[u8] = b"additional data";
const EXPECTED_CT: [u8; 14] =
[0x48, 0xf5, 0xe5, 0x8d, 0x95, 0x1d, 0xb7, 0x8d, 0x25, 0x9b, 0x89, 0x7e, 0x59, 0x78];
const EXPECTED_TAG: [u8; 16] = [
0x15, 0xad, 0x41, 0x23, 0xf6, 0x60, 0x99, 0xe1, 0xad, 0xa5, 0x5b, 0xd6, 0x7d, 0xa6, 0xc5,
0xeb,
];
let cipher = AesGcm256::new(&KEY).map_err(|e| LatticeArcError::ValidationError {
message: format!("AES-256-GCM KAT: cipher initialization failed: {}", e),
})?;
let (ciphertext, tag) = cipher.encrypt(&NONCE, PLAINTEXT, Some(AAD)).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("AES-256-GCM KAT: encryption failed: {}", e),
}
})?;
if !bool::from(ciphertext.ct_eq(&EXPECTED_CT)) {
return Err(LatticeArcError::ValidationError {
message: "AES-256-GCM KAT: ciphertext does not match expected value".to_string(),
});
}
if !bool::from(tag.ct_eq(&EXPECTED_TAG)) {
return Err(LatticeArcError::ValidationError {
message: "AES-256-GCM KAT: tag does not match expected value".to_string(),
});
}
let decrypted = cipher.decrypt(&NONCE, &ciphertext, &tag, Some(AAD)).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("AES-256-GCM KAT: decryption failed: {}", e),
}
})?;
if bool::from(decrypted.ct_eq(PLAINTEXT)) {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "AES-256-GCM KAT: decrypted plaintext does not match original".to_string(),
})
}
}
pub fn kat_ml_kem_768() -> Result<()> {
use crate::primitives::kem::ml_kem::{MlKem, MlKemSecurityLevel};
let dk = MlKem::generate_decapsulation_keypair(MlKemSecurityLevel::MlKem768).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("ML-KEM-768 KAT: key generation failed: {}", e),
}
})?;
let pk = dk.public_key();
if pk.as_bytes().len() != MlKemSecurityLevel::MlKem768.public_key_size() {
return Err(LatticeArcError::ValidationError {
message: format!(
"ML-KEM-768 KAT: public key size mismatch: expected {}, got {}",
MlKemSecurityLevel::MlKem768.public_key_size(),
pk.as_bytes().len()
),
});
}
let (ss_encap, ciphertext) =
MlKem::encapsulate(pk).map_err(|e| LatticeArcError::ValidationError {
message: format!("ML-KEM-768 KAT: encapsulation failed: {}", e),
})?;
if ciphertext.as_bytes().len() != MlKemSecurityLevel::MlKem768.ciphertext_size() {
return Err(LatticeArcError::ValidationError {
message: format!(
"ML-KEM-768 KAT: ciphertext size mismatch: expected {}, got {}",
MlKemSecurityLevel::MlKem768.ciphertext_size(),
ciphertext.as_bytes().len()
),
});
}
let ss_decap = dk.decapsulate(&ciphertext).map_err(|e| LatticeArcError::ValidationError {
message: format!("ML-KEM-768 KAT: decapsulation failed: {}", e),
})?;
if !bool::from(ss_encap.as_bytes().ct_eq(ss_decap.as_bytes())) {
return Err(LatticeArcError::ValidationError {
message: "ML-KEM-768 KAT: encapsulated and decapsulated shared secrets do not match"
.to_string(),
});
}
let all_zeros = ss_encap.as_bytes().iter().all(|&b| b == 0);
if all_zeros {
return Err(LatticeArcError::ValidationError {
message: "ML-KEM-768 KAT: shared secret is all zeros".to_string(),
});
}
Ok(())
}
pub fn kat_ml_dsa() -> Result<()> {
use crate::primitives::sig::ml_dsa::{MlDsaParameterSet, generate_keypair, sign, verify};
const TEST_MESSAGE: &[u8] = b"FIPS 140-3 ML-DSA Known Answer Test";
const CONTEXT: &[u8] = b"";
let (public_key, secret_key) = generate_keypair(MlDsaParameterSet::MlDsa44).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("ML-DSA KAT: key generation failed: {}", e),
}
})?;
if public_key.len() != MlDsaParameterSet::MlDsa44.public_key_size() {
return Err(LatticeArcError::ValidationError {
message: format!(
"ML-DSA KAT: public key size mismatch: expected {}, got {}",
MlDsaParameterSet::MlDsa44.public_key_size(),
public_key.len()
),
});
}
let signature = sign(&secret_key, TEST_MESSAGE, CONTEXT).map_err(|e| {
LatticeArcError::ValidationError { message: format!("ML-DSA KAT: signing failed: {}", e) }
})?;
if signature.len() != MlDsaParameterSet::MlDsa44.signature_size() {
return Err(LatticeArcError::ValidationError {
message: format!(
"ML-DSA KAT: signature size mismatch: expected {}, got {}",
MlDsaParameterSet::MlDsa44.signature_size(),
signature.len()
),
});
}
let is_valid = verify(&public_key, TEST_MESSAGE, &signature, CONTEXT).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("ML-DSA KAT: verification failed: {}", e),
}
})?;
if !is_valid {
return Err(LatticeArcError::ValidationError {
message: "ML-DSA KAT: valid signature was rejected".to_string(),
});
}
const WRONG_MESSAGE: &[u8] = b"FIPS 140-3 ML-DSA Wrong Message";
let is_valid_wrong = verify(&public_key, WRONG_MESSAGE, &signature, CONTEXT).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("ML-DSA KAT: verification check failed: {}", e),
}
})?;
if is_valid_wrong {
return Err(LatticeArcError::ValidationError {
message: "ML-DSA KAT: invalid signature was accepted".to_string(),
});
}
Ok(())
}
pub fn kat_slh_dsa() -> Result<()> {
use crate::primitives::sig::slh_dsa::{SigningKey, SlhDsaSecurityLevel};
const TEST_MESSAGE: &[u8] = b"FIPS 140-3 SLH-DSA Known Answer Test";
let (signing_key, verifying_key) = SigningKey::generate(SlhDsaSecurityLevel::Shake128s)
.map_err(|e| LatticeArcError::ValidationError {
message: format!("SLH-DSA KAT: key generation failed: {}", e),
})?;
let expected_pk_size = SlhDsaSecurityLevel::Shake128s.public_key_size();
if verifying_key.as_bytes().len() != expected_pk_size {
return Err(LatticeArcError::ValidationError {
message: format!(
"SLH-DSA KAT: public key size mismatch: expected {}, got {}",
expected_pk_size,
verifying_key.as_bytes().len()
),
});
}
let expected_sk_size = SlhDsaSecurityLevel::Shake128s.secret_key_size();
if signing_key.as_bytes().len() != expected_sk_size {
return Err(LatticeArcError::ValidationError {
message: format!(
"SLH-DSA KAT: secret key size mismatch: expected {}, got {}",
expected_sk_size,
signing_key.as_bytes().len()
),
});
}
let signature = signing_key.sign(TEST_MESSAGE, None).map_err(|e| {
LatticeArcError::ValidationError { message: format!("SLH-DSA KAT: signing failed: {}", e) }
})?;
let expected_sig_size = SlhDsaSecurityLevel::Shake128s.signature_size();
if signature.len() != expected_sig_size {
return Err(LatticeArcError::ValidationError {
message: format!(
"SLH-DSA KAT: signature size mismatch: expected {}, got {}",
expected_sig_size,
signature.len()
),
});
}
let is_valid = verifying_key.verify(TEST_MESSAGE, &signature, None).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("SLH-DSA KAT: verification failed: {}", e),
}
})?;
if !is_valid {
return Err(LatticeArcError::ValidationError {
message: "SLH-DSA KAT: valid signature was rejected".to_string(),
});
}
const WRONG_MESSAGE: &[u8] = b"FIPS 140-3 SLH-DSA Wrong Message";
let is_valid_wrong = verifying_key.verify(WRONG_MESSAGE, &signature, None).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("SLH-DSA KAT: verification check failed: {}", e),
}
})?;
if is_valid_wrong {
return Err(LatticeArcError::ValidationError {
message: "SLH-DSA KAT: invalid signature was accepted".to_string(),
});
}
Ok(())
}
pub fn kat_fn_dsa() -> Result<()> {
use crate::primitives::sig::fndsa::{FnDsaSecurityLevel, KeyPair};
use rand::rngs::OsRng;
const TEST_MESSAGE: &[u8] = b"FIPS 140-3 FN-DSA Known Answer Test";
std::thread::Builder::new()
.stack_size(32 * 1024 * 1024) .spawn(|| -> Result<()> {
let mut rng = OsRng;
let mut keypair = KeyPair::generate_with_rng(&mut rng, FnDsaSecurityLevel::Level512)
.map_err(|e| LatticeArcError::ValidationError {
message: format!("FN-DSA KAT: key generation failed: {}", e),
})?;
let expected_pk_size = FnDsaSecurityLevel::Level512.verifying_key_size();
if keypair.verifying_key().to_bytes().len() != expected_pk_size {
return Err(LatticeArcError::ValidationError {
message: format!(
"FN-DSA KAT: verifying key size mismatch: expected {}, got {}",
expected_pk_size,
keypair.verifying_key().to_bytes().len()
),
});
}
let expected_sk_size = FnDsaSecurityLevel::Level512.signing_key_size();
if keypair.signing_key().to_bytes().len() != expected_sk_size {
return Err(LatticeArcError::ValidationError {
message: format!(
"FN-DSA KAT: signing key size mismatch: expected {}, got {}",
expected_sk_size,
keypair.signing_key().to_bytes().len()
),
});
}
let mut rng = OsRng;
let signature = keypair.sign_with_rng(&mut rng, TEST_MESSAGE).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("FN-DSA KAT: signing failed: {}", e),
}
})?;
let expected_sig_size = FnDsaSecurityLevel::Level512.signature_size();
if signature.len() != expected_sig_size {
return Err(LatticeArcError::ValidationError {
message: format!(
"FN-DSA KAT: signature size mismatch: expected {}, got {}",
expected_sig_size,
signature.len()
),
});
}
let is_valid = keypair.verify(TEST_MESSAGE, &signature).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("FN-DSA KAT: verification failed: {}", e),
}
})?;
if !is_valid {
return Err(LatticeArcError::ValidationError {
message: "FN-DSA KAT: valid signature was rejected".to_string(),
});
}
const WRONG_MESSAGE: &[u8] = b"FIPS 140-3 FN-DSA Wrong Message";
let is_valid_wrong = keypair.verify(WRONG_MESSAGE, &signature).map_err(|e| {
LatticeArcError::ValidationError {
message: format!("FN-DSA KAT: verification check failed: {}", e),
}
})?;
if is_valid_wrong {
return Err(LatticeArcError::ValidationError {
message: "FN-DSA KAT: invalid signature was accepted".to_string(),
});
}
Ok(())
})
.map_err(|e| LatticeArcError::ValidationError {
message: format!("FN-DSA KAT: failed to spawn thread: {}", e),
})?
.join()
.map_err(|_e| LatticeArcError::ValidationError {
message: "FN-DSA KAT: thread panicked".to_string(),
})?
}
pub fn integrity_test() -> Result<()> {
const INTEGRITY_KEY: &[u8] = crate::types::domains::MODULE_INTEGRITY_HMAC_KEY;
let module_path = std::env::current_exe().map_err(|e| LatticeArcError::ValidationError {
message: format!("Integrity test: cannot locate module binary: {}", e),
})?;
let module_bytes =
std::fs::read(&module_path).map_err(|e| LatticeArcError::ValidationError {
message: format!("Integrity test: cannot read module binary: {}", e),
})?;
let computed_hmac = crate::primitives::mac::hmac::hmac_sha256(INTEGRITY_KEY, &module_bytes)
.map_err(|e| LatticeArcError::ValidationError {
message: format!("Integrity test: HMAC computation failed: {}", e),
})?;
mod generated {
include!(concat!(env!("OUT_DIR"), "/integrity_hmac.rs"));
}
let expected_hmac = generated::EXPECTED_HMAC;
let Some(expected_hmac) = expected_hmac else {
#[cfg(debug_assertions)]
{
#[allow(clippy::print_stderr)] {
eprintln!("FIPS Integrity Test: Development mode (debug build)");
eprintln!(" Expected HMAC not configured. Computed HMAC:");
eprintln!(" {:02x?}", computed_hmac.as_slice());
eprintln!(" Configure PRODUCTION_HMAC.txt for production builds.");
}
return Ok(());
}
#[cfg(not(debug_assertions))]
{
return Err(LatticeArcError::ValidationError {
message: "FIPS Integrity Test FAILED: No expected HMAC configured. \
Production builds require PRODUCTION_HMAC.txt with the module HMAC."
.to_string(),
});
}
};
use subtle::ConstantTimeEq;
let hmac_match = computed_hmac.ct_eq(expected_hmac);
if hmac_match.into() {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "FIPS Integrity Test FAILED: Module binary has been modified or corrupted. \
This is a critical security violation."
.to_string(),
})
}
}
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static SELF_TEST_PASSED: AtomicBool = AtomicBool::new(false);
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum ModuleErrorCode {
NoError = 0,
SelfTestFailure = 1,
EntropyFailure = 2,
IntegrityFailure = 3,
CriticalCryptoError = 4,
KeyZeroizationFailure = 5,
AuthenticationFailure = 6,
HsmError = 7,
UnknownCriticalError = 255,
}
impl ModuleErrorCode {
#[must_use]
pub fn from_u32(value: u32) -> Self {
match value {
0 => Self::NoError,
1 => Self::SelfTestFailure,
2 => Self::EntropyFailure,
3 => Self::IntegrityFailure,
4 => Self::CriticalCryptoError,
5 => Self::KeyZeroizationFailure,
6 => Self::AuthenticationFailure,
7 => Self::HsmError,
_ => Self::UnknownCriticalError,
}
}
#[must_use]
pub fn is_error(&self) -> bool {
*self != Self::NoError
}
#[must_use]
pub fn description(&self) -> &'static str {
match self {
Self::NoError => "No error",
Self::SelfTestFailure => "FIPS 140-3 self-test failure",
Self::EntropyFailure => "Entropy source failure",
Self::IntegrityFailure => "Software/firmware integrity check failure",
Self::CriticalCryptoError => "Critical cryptographic operation error",
Self::KeyZeroizationFailure => "Sensitive key material zeroization failure",
Self::AuthenticationFailure => "Repeated authentication failures",
Self::HsmError => "Hardware security module error",
Self::UnknownCriticalError => "Unknown critical error",
}
}
}
#[derive(Debug, Clone)]
pub struct ModuleErrorState {
pub error_code: ModuleErrorCode,
pub timestamp: u64,
}
impl ModuleErrorState {
#[must_use]
pub fn is_error(&self) -> bool {
self.error_code.is_error()
}
}
static MODULE_ERROR_CODE: AtomicU32 = AtomicU32::new(0);
static MODULE_ERROR_TIMESTAMP: AtomicU64 = AtomicU64::new(0);
fn current_timestamp() -> u64 {
SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0)
}
pub fn set_module_error(code: ModuleErrorCode) {
let timestamp = current_timestamp();
MODULE_ERROR_CODE.store(code as u32, Ordering::SeqCst);
MODULE_ERROR_TIMESTAMP.store(timestamp, Ordering::SeqCst);
if code.is_error() {
SELF_TEST_PASSED.store(false, Ordering::SeqCst);
}
}
#[must_use]
pub fn get_module_error_state() -> ModuleErrorState {
ModuleErrorState {
error_code: ModuleErrorCode::from_u32(MODULE_ERROR_CODE.load(Ordering::SeqCst)),
timestamp: MODULE_ERROR_TIMESTAMP.load(Ordering::SeqCst),
}
}
#[must_use]
pub fn is_module_operational() -> bool {
let error_code = ModuleErrorCode::from_u32(MODULE_ERROR_CODE.load(Ordering::Acquire));
!error_code.is_error() && SELF_TEST_PASSED.load(Ordering::Acquire)
}
#[doc(hidden)]
pub fn clear_error_state() {
MODULE_ERROR_CODE.store(ModuleErrorCode::NoError as u32, Ordering::SeqCst);
MODULE_ERROR_TIMESTAMP.store(0, Ordering::SeqCst);
SELF_TEST_PASSED.store(false, Ordering::Release);
}
#[doc(hidden)]
pub fn restore_operational_state() {
MODULE_ERROR_CODE.store(ModuleErrorCode::NoError as u32, Ordering::SeqCst);
MODULE_ERROR_TIMESTAMP.store(0, Ordering::SeqCst);
SELF_TEST_PASSED.store(true, Ordering::Release);
}
#[must_use]
pub fn self_tests_passed() -> bool {
SELF_TEST_PASSED.load(Ordering::Acquire)
}
#[must_use]
pub fn initialize_and_test() -> SelfTestResult {
let result = run_power_up_tests();
if result.is_pass() {
SELF_TEST_PASSED.store(true, Ordering::Release);
} else {
set_module_error(ModuleErrorCode::SelfTestFailure);
std::process::abort();
}
result
}
pub fn verify_operational() -> Result<()> {
let error_state = get_module_error_state();
if error_state.is_error() {
return Err(LatticeArcError::ValidationError {
message: format!(
"FIPS module not operational: {} (error set at timestamp {})",
error_state.error_code.description(),
error_state.timestamp
),
});
}
if self_tests_passed() {
Ok(())
} else {
Err(LatticeArcError::ValidationError {
message: "FIPS module not operational: self-tests have not passed".to_string(),
})
}
}
#[cfg(test)]
#[allow(clippy::indexing_slicing)]
mod tests {
use super::*;
#[test]
fn test_sha256_kat_passes() {
assert!(kat_sha256().is_ok());
}
#[test]
fn test_hkdf_sha256_kat_passes() {
assert!(kat_hkdf_sha256().is_ok());
}
#[test]
fn test_aes_256_gcm_kat_passes() {
assert!(kat_aes_256_gcm().is_ok());
}
#[test]
fn test_ml_kem_768_kat_passes() {
assert!(kat_ml_kem_768().is_ok());
}
#[test]
fn test_power_up_tests_pass_succeeds() {
let result = run_power_up_tests();
assert!(result.is_pass(), "Power-up tests should pass: {:?}", result);
}
#[test]
fn test_power_up_tests_with_report_succeeds() {
let report = run_power_up_tests_with_report();
assert!(report.overall_result.is_pass(), "Overall result should pass");
assert!(!report.tests.is_empty(), "Should have individual test results");
for test in &report.tests {
assert!(test.result.is_pass(), "Test {} should pass", test.algorithm);
assert!(test.duration_us.is_some(), "Duration should be measured");
}
}
#[test]
fn test_self_test_result_methods_return_correct_values_succeeds() {
let pass = SelfTestResult::Pass;
let fail = SelfTestResult::Fail("test failure".to_string());
assert!(pass.is_pass());
assert!(!pass.is_fail());
assert!(pass.to_result().is_ok());
assert!(!fail.is_pass());
assert!(fail.is_fail());
assert!(fail.to_result().is_err());
}
#[test]
fn test_initialize_and_verify_sets_passed_flag_succeeds() {
SELF_TEST_PASSED.store(false, Ordering::Release);
assert!(verify_operational().is_err());
let result = initialize_and_test();
assert!(result.is_pass());
assert!(verify_operational().is_ok());
assert!(self_tests_passed());
}
#[test]
fn test_ml_dsa_kat_passes() {
let result = kat_ml_dsa();
assert!(result.is_ok(), "ML-DSA KAT should pass: {:?}", result);
}
#[test]
fn test_slh_dsa_kat_passes() {
let result = kat_slh_dsa();
assert!(result.is_ok(), "SLH-DSA KAT should pass: {:?}", result);
}
#[test]
fn test_fn_dsa_kat_passes() {
let result = kat_fn_dsa();
assert!(result.is_ok(), "FN-DSA KAT should pass: {:?}", result);
}
#[test]
fn test_integrity_test_passes() {
assert!(integrity_test().is_ok());
}
#[test]
fn test_module_error_code_from_u32_fails() {
assert_eq!(ModuleErrorCode::from_u32(0), ModuleErrorCode::NoError);
assert_eq!(ModuleErrorCode::from_u32(1), ModuleErrorCode::SelfTestFailure);
assert_eq!(ModuleErrorCode::from_u32(2), ModuleErrorCode::EntropyFailure);
assert_eq!(ModuleErrorCode::from_u32(3), ModuleErrorCode::IntegrityFailure);
assert_eq!(ModuleErrorCode::from_u32(4), ModuleErrorCode::CriticalCryptoError);
assert_eq!(ModuleErrorCode::from_u32(5), ModuleErrorCode::KeyZeroizationFailure);
assert_eq!(ModuleErrorCode::from_u32(6), ModuleErrorCode::AuthenticationFailure);
assert_eq!(ModuleErrorCode::from_u32(7), ModuleErrorCode::HsmError);
assert_eq!(ModuleErrorCode::from_u32(100), ModuleErrorCode::UnknownCriticalError);
assert_eq!(ModuleErrorCode::from_u32(255), ModuleErrorCode::UnknownCriticalError);
}
#[test]
fn test_module_error_code_is_error_fails() {
assert!(!ModuleErrorCode::NoError.is_error());
assert!(ModuleErrorCode::SelfTestFailure.is_error());
assert!(ModuleErrorCode::EntropyFailure.is_error());
assert!(ModuleErrorCode::IntegrityFailure.is_error());
assert!(ModuleErrorCode::CriticalCryptoError.is_error());
assert!(ModuleErrorCode::KeyZeroizationFailure.is_error());
assert!(ModuleErrorCode::AuthenticationFailure.is_error());
assert!(ModuleErrorCode::HsmError.is_error());
assert!(ModuleErrorCode::UnknownCriticalError.is_error());
}
#[test]
fn test_module_error_code_description_returns_correct_strings_fails() {
assert_eq!(ModuleErrorCode::NoError.description(), "No error");
assert_eq!(ModuleErrorCode::SelfTestFailure.description(), "FIPS 140-3 self-test failure");
assert_eq!(ModuleErrorCode::EntropyFailure.description(), "Entropy source failure");
}
#[test]
fn test_set_and_get_module_error_succeeds() {
clear_error_state();
let state = get_module_error_state();
assert!(!state.is_error());
assert_eq!(state.error_code, ModuleErrorCode::NoError);
set_module_error(ModuleErrorCode::SelfTestFailure);
let state = get_module_error_state();
assert!(state.is_error());
assert_eq!(state.error_code, ModuleErrorCode::SelfTestFailure);
assert!(state.timestamp > 0);
clear_error_state();
let state = get_module_error_state();
assert!(!state.is_error());
assert_eq!(state.error_code, ModuleErrorCode::NoError);
assert_eq!(state.timestamp, 0);
}
#[test]
fn test_is_module_operational_succeeds() {
clear_error_state();
SELF_TEST_PASSED.store(false, Ordering::SeqCst);
assert!(!is_module_operational());
SELF_TEST_PASSED.store(true, Ordering::SeqCst);
assert!(is_module_operational());
set_module_error(ModuleErrorCode::EntropyFailure);
assert!(!is_module_operational());
clear_error_state();
SELF_TEST_PASSED.store(true, Ordering::SeqCst);
assert!(is_module_operational());
}
#[test]
fn test_verify_operational_with_error_state_fails() {
clear_error_state();
let result = initialize_and_test();
assert!(result.is_pass());
assert!(verify_operational().is_ok());
set_module_error(ModuleErrorCode::CriticalCryptoError);
let result = verify_operational();
assert!(result.is_err());
if let Err(LatticeArcError::ValidationError { message }) = result {
assert!(message.contains("Critical cryptographic operation error"));
}
clear_error_state();
let result = initialize_and_test();
assert!(result.is_pass());
assert!(verify_operational().is_ok());
}
#[test]
fn test_set_error_clears_self_test_passed_fails() {
clear_error_state();
let result = initialize_and_test();
assert!(result.is_pass());
assert!(self_tests_passed());
set_module_error(ModuleErrorCode::IntegrityFailure);
assert!(!self_tests_passed());
clear_error_state();
}
#[test]
fn test_module_error_state_struct_is_correct() {
let state = ModuleErrorState { error_code: ModuleErrorCode::NoError, timestamp: 0 };
assert!(!state.is_error());
let state =
ModuleErrorState { error_code: ModuleErrorCode::HsmError, timestamp: 1234567890 };
assert!(state.is_error());
}
#[test]
fn test_module_error_code_all_descriptions_return_correct_strings_fails() {
assert_eq!(
ModuleErrorCode::IntegrityFailure.description(),
"Software/firmware integrity check failure"
);
assert_eq!(
ModuleErrorCode::CriticalCryptoError.description(),
"Critical cryptographic operation error"
);
assert_eq!(
ModuleErrorCode::KeyZeroizationFailure.description(),
"Sensitive key material zeroization failure"
);
assert_eq!(
ModuleErrorCode::AuthenticationFailure.description(),
"Repeated authentication failures"
);
assert_eq!(ModuleErrorCode::HsmError.description(), "Hardware security module error");
assert_eq!(ModuleErrorCode::UnknownCriticalError.description(), "Unknown critical error");
}
#[test]
fn test_set_module_error_no_error_does_not_clear_self_test_fails() {
clear_error_state();
let result = initialize_and_test();
assert!(result.is_pass());
assert!(self_tests_passed());
set_module_error(ModuleErrorCode::NoError);
assert!(self_tests_passed());
clear_error_state();
}
#[test]
fn test_self_test_result_debug_clone_work_correctly_succeeds() {
let pass = SelfTestResult::Pass;
let cloned = pass.clone();
assert_eq!(pass, cloned);
let debug = format!("{:?}", pass);
assert!(debug.contains("Pass"));
let fail = SelfTestResult::Fail("oops".to_string());
let fail_clone = fail.clone();
assert_eq!(fail, fail_clone);
let debug = format!("{:?}", fail);
assert!(debug.contains("oops"));
}
#[test]
fn test_individual_test_result_fields_succeeds() {
let result = IndividualTestResult {
algorithm: "SHA-256".to_string(),
result: SelfTestResult::Pass,
duration_us: Some(42),
};
assert_eq!(result.algorithm, "SHA-256");
assert!(result.result.is_pass());
assert_eq!(result.duration_us, Some(42));
let cloned = result.clone();
assert_eq!(cloned.algorithm, "SHA-256");
assert_eq!(cloned, result);
let debug = format!("{:?}", result);
assert!(debug.contains("SHA-256"));
}
#[test]
fn test_self_test_report_fields_succeeds() {
let report = run_power_up_tests_with_report();
assert_eq!(report.tests.len(), 9); assert!(report.total_duration_us > 0);
let cloned = report.clone();
assert_eq!(cloned.tests.len(), 9);
let debug = format!("{:?}", report);
assert!(debug.contains("SelfTestReport"));
}
#[test]
fn test_module_error_code_debug_produces_expected_output_fails() {
let code = ModuleErrorCode::SelfTestFailure;
let debug = format!("{:?}", code);
assert!(debug.contains("SelfTestFailure"));
let cloned = code;
assert_eq!(cloned, ModuleErrorCode::SelfTestFailure);
}
#[test]
fn test_module_error_state_debug_clone_work_correctly_fails() {
let state =
ModuleErrorState { error_code: ModuleErrorCode::EntropyFailure, timestamp: 1000 };
let cloned = state.clone();
assert_eq!(cloned.error_code, ModuleErrorCode::EntropyFailure);
assert_eq!(cloned.timestamp, 1000);
let debug = format!("{:?}", state);
assert!(debug.contains("EntropyFailure"));
}
#[test]
fn test_verify_operational_without_self_tests_fails() {
clear_error_state();
SELF_TEST_PASSED.store(false, Ordering::SeqCst);
let result = verify_operational();
assert!(result.is_err());
if let Err(LatticeArcError::ValidationError { message }) = result {
assert!(message.contains("self-tests have not passed"));
}
let _ = initialize_and_test();
}
#[test]
fn test_multiple_error_states_in_sequence_fails() {
clear_error_state();
set_module_error(ModuleErrorCode::EntropyFailure);
let state = get_module_error_state();
assert_eq!(state.error_code, ModuleErrorCode::EntropyFailure);
set_module_error(ModuleErrorCode::HsmError);
let state = get_module_error_state();
assert_eq!(state.error_code, ModuleErrorCode::HsmError);
set_module_error(ModuleErrorCode::KeyZeroizationFailure);
let state = get_module_error_state();
assert_eq!(state.error_code, ModuleErrorCode::KeyZeroizationFailure);
clear_error_state();
let _ = initialize_and_test();
}
#[test]
fn test_self_test_result_fail_to_result_contains_message_fails() {
let fail = SelfTestResult::Fail("module corrupted".to_string());
let result = fail.to_result();
assert!(result.is_err());
if let Err(LatticeArcError::ValidationError { message }) = result {
assert!(message.contains("module corrupted"));
assert!(message.contains("FIPS 140-3"));
}
}
#[test]
fn test_individual_test_result_with_no_duration_succeeds() {
let result = IndividualTestResult {
algorithm: "TEST".to_string(),
result: SelfTestResult::Fail("error".to_string()),
duration_us: None,
};
assert!(result.result.is_fail());
assert!(result.duration_us.is_none());
let debug = format!("{:?}", result);
assert!(debug.contains("None"));
}
#[test]
fn test_self_test_report_with_failures_has_correct_fields_fails() {
let report = SelfTestReport {
overall_result: SelfTestResult::Fail("SHA-256 failed".to_string()),
tests: vec![
IndividualTestResult {
algorithm: "SHA-256".to_string(),
result: SelfTestResult::Fail("KAT mismatch".to_string()),
duration_us: Some(100),
},
IndividualTestResult {
algorithm: "AES-GCM".to_string(),
result: SelfTestResult::Pass,
duration_us: Some(200),
},
],
total_duration_us: 300,
};
assert!(report.overall_result.is_fail());
assert_eq!(report.tests.len(), 2);
assert!(report.tests[0].result.is_fail());
assert!(report.tests[1].result.is_pass());
let debug = format!("{:?}", report);
assert!(debug.contains("SelfTestReport"));
}
#[test]
fn test_module_error_code_repr_values_fails() {
assert_eq!(ModuleErrorCode::NoError as u32, 0);
assert_eq!(ModuleErrorCode::SelfTestFailure as u32, 1);
assert_eq!(ModuleErrorCode::EntropyFailure as u32, 2);
assert_eq!(ModuleErrorCode::IntegrityFailure as u32, 3);
assert_eq!(ModuleErrorCode::CriticalCryptoError as u32, 4);
assert_eq!(ModuleErrorCode::KeyZeroizationFailure as u32, 5);
assert_eq!(ModuleErrorCode::AuthenticationFailure as u32, 6);
assert_eq!(ModuleErrorCode::HsmError as u32, 7);
assert_eq!(ModuleErrorCode::UnknownCriticalError as u32, 255);
}
#[test]
fn test_module_error_code_from_u32_boundary_fails() {
assert_eq!(ModuleErrorCode::from_u32(8), ModuleErrorCode::UnknownCriticalError);
assert_eq!(ModuleErrorCode::from_u32(128), ModuleErrorCode::UnknownCriticalError);
assert_eq!(ModuleErrorCode::from_u32(254), ModuleErrorCode::UnknownCriticalError);
assert_eq!(ModuleErrorCode::from_u32(u32::MAX), ModuleErrorCode::UnknownCriticalError);
}
#[test]
fn test_module_error_state_no_error_timestamp_zero_fails() {
clear_error_state();
let state = get_module_error_state();
assert!(!state.is_error());
assert_eq!(state.timestamp, 0);
}
#[test]
fn test_module_error_state_error_has_nonzero_timestamp_fails() {
clear_error_state();
set_module_error(ModuleErrorCode::SelfTestFailure);
let state = get_module_error_state();
assert!(state.is_error());
assert!(state.timestamp > 0);
clear_error_state();
let _ = initialize_and_test();
}
#[test]
fn test_verify_operational_error_message_contains_description_fails() {
clear_error_state();
set_module_error(ModuleErrorCode::EntropyFailure);
let result = verify_operational();
assert!(result.is_err());
if let Err(LatticeArcError::ValidationError { message }) = result {
assert!(message.contains("Entropy source failure"));
assert!(message.contains("error set at timestamp"));
}
clear_error_state();
let _ = initialize_and_test();
}
#[test]
fn test_all_error_codes_block_operations_fails() {
let error_codes = [
ModuleErrorCode::SelfTestFailure,
ModuleErrorCode::EntropyFailure,
ModuleErrorCode::IntegrityFailure,
ModuleErrorCode::CriticalCryptoError,
ModuleErrorCode::KeyZeroizationFailure,
ModuleErrorCode::AuthenticationFailure,
ModuleErrorCode::HsmError,
ModuleErrorCode::UnknownCriticalError,
];
for code in &error_codes {
clear_error_state();
SELF_TEST_PASSED.store(true, Ordering::SeqCst);
set_module_error(*code);
assert!(!is_module_operational(), "{:?} should block operations", code);
assert!(verify_operational().is_err(), "{:?} should fail verify", code);
}
clear_error_state();
let _ = initialize_and_test();
}
#[test]
fn test_initialize_and_test_sets_flag_succeeds() {
SELF_TEST_PASSED.store(false, Ordering::SeqCst);
clear_error_state();
assert!(!self_tests_passed());
let result = initialize_and_test();
assert!(result.is_pass());
assert!(self_tests_passed());
}
#[test]
fn test_current_timestamp_reasonable_succeeds() {
let ts = current_timestamp();
assert!(ts > 1_577_836_800, "Timestamp should be after 2020");
}
#[test]
fn test_kat_sha256_is_deterministic() {
for _ in 0..5 {
assert!(kat_sha256().is_ok());
}
}
#[test]
fn test_kat_hkdf_sha256_is_deterministic() {
for _ in 0..5 {
assert!(kat_hkdf_sha256().is_ok());
}
}
#[test]
fn test_kat_aes_256_gcm_is_deterministic() {
for _ in 0..5 {
assert!(kat_aes_256_gcm().is_ok());
}
}
#[test]
fn test_kat_ml_kem_768_always_succeeds() {
for _ in 0..3 {
assert!(kat_ml_kem_768().is_ok());
}
}
#[test]
fn test_run_power_up_tests_is_deterministic() {
for _ in 0..3 {
let result = run_power_up_tests();
assert!(result.is_pass());
}
}
#[test]
fn test_run_power_up_tests_with_report_all_pass_succeeds() {
let report = run_power_up_tests_with_report();
assert!(report.overall_result.is_pass());
for test in &report.tests {
assert!(
test.result.is_pass(),
"Test {} should pass but got: {:?}",
test.algorithm,
test.result
);
assert!(test.duration_us.is_some());
}
assert!(report.total_duration_us > 0);
}
#[test]
fn test_kat_sha3_256_passes() {
assert!(kat_sha3_256().is_ok());
}
#[test]
fn test_kat_hmac_sha256_passes() {
assert!(kat_hmac_sha256().is_ok());
}
#[test]
fn test_self_test_report_all_fields_populated_succeeds() {
let report = run_power_up_tests_with_report();
assert!(report.overall_result.is_pass());
assert!(report.tests.len() >= 9, "Should have at least 9 KAT results");
assert!(report.total_duration_us > 0);
for test in &report.tests {
assert!(!test.algorithm.is_empty(), "Algorithm name should not be empty");
assert!(
test.duration_us.is_some(),
"Duration should be measured for {}",
test.algorithm
);
}
}
#[test]
fn test_error_state_timestamp_ordering_fails() {
clear_error_state();
set_module_error(ModuleErrorCode::EntropyFailure);
let state1 = get_module_error_state();
let ts1 = state1.timestamp;
set_module_error(ModuleErrorCode::IntegrityFailure);
let state2 = get_module_error_state();
let ts2 = state2.timestamp;
assert!(ts2 >= ts1, "Second timestamp should be >= first");
assert_eq!(state2.error_code, ModuleErrorCode::IntegrityFailure);
clear_error_state();
}
#[test]
fn test_verify_operational_after_reset_succeeds() {
set_module_error(ModuleErrorCode::HsmError);
assert!(verify_operational().is_err());
clear_error_state();
let result = initialize_and_test();
assert!(result.is_pass());
assert!(verify_operational().is_ok());
assert!(is_module_operational());
}
}