use crate::enclave::attestation::{AttestationLevel, DeviceIntegrityReport};
use crate::enclave::replay_guard::ReplayGuard;
use ed25519_dalek::{Signer, SigningKey};
use rand_core::Rng;
struct MockAttestationGenerator {
level: AttestationLevel,
}
impl MockAttestationGenerator {
fn new(level: AttestationLevel) -> Self {
Self { level }
}
fn generate_valid_report(&self, nonce: &[u8], timestamp: u64) -> DeviceIntegrityReport {
let mut seed = [0u8; 32];
rand::rng().fill_bytes(&mut seed);
let signing_key = SigningKey::from_bytes(&seed);
let pubkey_hex = hex::encode(signing_key.verifying_key().to_bytes());
let mut extension_data = "PURPOSE_SIGN|ALGORITHM_ED25519|OS_VERSION_14".to_string();
match self.level {
AttestationLevel::StrongBox | AttestationLevel::CloudTEE => {
extension_data.push_str("|HARDWARE_BACKED|SECURE_BOOT_ENABLED");
}
AttestationLevel::TEE => {
extension_data.push_str("|TEE_ENABLED|HARDWARE_ROOT_OF_TRUST");
}
AttestationLevel::Software => {
extension_data.push_str("|SIMULATED");
}
}
let mut data_to_verify = Vec::new();
data_to_verify.extend_from_slice(nonce);
data_to_verify.extend_from_slice(extension_data.as_bytes());
data_to_verify.extend_from_slice(×tamp.to_le_bytes());
let signature = signing_key.sign(&data_to_verify).to_bytes().to_vec();
let root_ca = match self.level {
AttestationLevel::CloudTEE => "CONCLAVE_CLOUD_ROOT_CA_V1".to_string(),
AttestationLevel::StrongBox => "GOOGLE_STRONGBOX_ROOT_V1".to_string(),
AttestationLevel::TEE => "CONCLAVE_ROOT_CA_V1".to_string(),
AttestationLevel::Software => "CONCLAVE_SIM_ROOT_V1".to_string(),
};
DeviceIntegrityReport {
level: self.level.clone(),
challenge_nonce: nonce.to_vec(),
signature,
certificate_chain: vec![pubkey_hex, root_ca],
timestamp,
extension_data,
}
}
fn generate_expired_report(&self, nonce: &[u8], age_secs: u64) -> DeviceIntegrityReport {
let now = 1_000_000_u64; let expired_timestamp = now.saturating_sub(age_secs);
self.generate_valid_report(nonce, expired_timestamp)
}
fn generate_wrong_nonce_report(
&self,
wrong_nonce: &[u8],
timestamp: u64,
) -> DeviceIntegrityReport {
self.generate_valid_report(wrong_nonce, timestamp)
}
fn generate_invalid_signature_report(
&self,
nonce: &[u8],
timestamp: u64,
) -> DeviceIntegrityReport {
let mut report = self.generate_valid_report(nonce, timestamp);
if !report.signature.is_empty() {
report.signature[0] ^= 0xFF;
}
report
}
fn generate_untrusted_root_report(
&self,
nonce: &[u8],
timestamp: u64,
) -> DeviceIntegrityReport {
let mut report = self.generate_valid_report(nonce, timestamp);
report.certificate_chain[1] = "UNTRUSTED_ROOT_CA".to_string();
report
}
fn generate_missing_hardware_hardening_report(
&self,
nonce: &[u8],
timestamp: u64,
) -> DeviceIntegrityReport {
let mut report = self.generate_valid_report(nonce, timestamp);
report.extension_data = "PURPOSE_SIGN|ALGORITHM_ED25519|SIMULATED".to_string();
report
}
}
#[cfg(test)]
mod trust_tier_tests {
use super::*;
#[test]
fn test_cloud_tee_attestation_valid() {
let generator = MockAttestationGenerator::new(AttestationLevel::CloudTEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now.saturating_sub(60));
assert!(
report.verify_at_time(&nonce, now),
"CloudTEE attestation should verify with valid report"
);
}
#[test]
fn test_strongbox_attestation_valid() {
let generator = MockAttestationGenerator::new(AttestationLevel::StrongBox);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now.saturating_sub(60));
assert!(
report.verify_at_time(&nonce, now),
"StrongBox attestation should verify with valid report"
);
}
#[test]
fn test_tee_attestation_valid() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now.saturating_sub(60));
assert!(
report.verify_at_time(&nonce, now),
"TEE attestation should verify with valid report"
);
}
#[test]
fn test_software_attestation_blocked_for_production() {
let generator = MockAttestationGenerator::new(AttestationLevel::Software);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now.saturating_sub(60));
assert!(
!report.verify_at_time(&nonce, now),
"Software attestation MUST be blocked for production paths"
);
}
}
#[cfg(test)]
mod freshness_tests {
use super::*;
use crate::enclave::attestation::MAX_ATTESTATION_AGE_SECS;
#[test]
fn test_rejects_stale_attestation() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_expired_report(&nonce, MAX_ATTESTATION_AGE_SECS + 1);
assert!(
!report.verify_at_time(&nonce, now),
"Stale attestation should be rejected"
);
}
#[test]
fn test_accepts_fresh_attestation() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now.saturating_sub(60));
assert!(
report.verify_at_time(&nonce, now),
"Fresh attestation should be accepted"
);
}
#[test]
fn test_rejects_future_timestamp() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now + 100);
assert!(
!report.verify_at_time(&nonce, now),
"Future timestamp attestation should be rejected"
);
}
#[test]
fn test_rejects_wrong_nonce() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let now = 1_000_000_u64;
let report = generator.generate_wrong_nonce_report(&[9, 8, 7, 6], now.saturating_sub(60));
assert!(
!report.verify_at_time(&[1, 2, 3, 4], now),
"Attestation with wrong nonce should be rejected"
);
}
#[test]
fn test_replay_guard_blocks_duplicate_attestation() {
let guard = ReplayGuard::new(300, 128);
let attestation_key = "attestation-12345";
let now = 100_u64;
assert!(
guard.check_and_record(attestation_key, now),
"First attestation should be accepted"
);
assert!(
!guard.check_and_record(attestation_key, now + 10),
"Duplicate attestation within window MUST be rejected"
);
}
#[test]
fn test_replay_guard_allows_after_ttl() {
let guard = ReplayGuard::new(10, 128); let attestation_key = "attestation-12345";
assert!(guard.check_and_record(attestation_key, 100));
assert!(
!guard.check_and_record(attestation_key, 105),
"Duplicate within TTL should be rejected"
);
assert!(
guard.check_and_record(attestation_key, 115),
"Same key after TTL expiry should be accepted"
);
}
}
#[cfg(test)]
mod crypto_verification_tests {
use super::*;
#[test]
fn test_rejects_invalid_signature() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_invalid_signature_report(&nonce, now.saturating_sub(60));
assert!(
!report.verify_at_time(&nonce, now),
"Attestation with invalid signature should be rejected"
);
}
#[test]
fn test_rejects_untrusted_root_ca() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_untrusted_root_report(&nonce, now.saturating_sub(60));
assert!(
!report.verify_at_time(&nonce, now),
"Attestation with untrusted root CA should be rejected"
);
}
#[test]
fn test_strongbox_requires_hardware_hardening() {
let generator = MockAttestationGenerator::new(AttestationLevel::StrongBox);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report =
generator.generate_missing_hardware_hardening_report(&nonce, now.saturating_sub(60));
assert!(
!report.verify_at_time(&nonce, now),
"StrongBox requires HARDWARE_BACKED and SECURE_BOOT_ENABLED"
);
}
#[test]
fn test_cloud_tee_requires_hardware_hardening() {
let generator = MockAttestationGenerator::new(AttestationLevel::CloudTEE);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report =
generator.generate_missing_hardware_hardening_report(&nonce, now.saturating_sub(60));
assert!(
!report.verify_at_time(&nonce, now),
"CloudTEE requires HARDWARE_BACKED and SECURE_BOOT_ENABLED"
);
}
}
#[cfg(test)]
mod fingerprint_tests {
use super::*;
#[test]
fn test_fingerprint_deterministic() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let nonce = [1, 2, 3, 4];
let timestamp = 1_000_000_u64;
let report1 = generator.generate_valid_report(&nonce, timestamp);
let report2 = generator.generate_valid_report(&nonce, timestamp);
let fp1 = report1.get_device_fingerprint();
let _fp2 = report2.get_device_fingerprint();
assert_eq!(
fp1.len(),
64,
"Fingerprint should be 64 hex characters (SHA-256)"
);
}
#[test]
fn test_different_certs_produce_different_fingerprints() {
let gen1 = MockAttestationGenerator::new(AttestationLevel::TEE);
let gen2 = MockAttestationGenerator::new(AttestationLevel::CloudTEE);
let report1 = gen1.generate_valid_report(&[1, 2, 3, 4], 1_000_000);
let report2 = gen2.generate_valid_report(&[1, 2, 3, 4], 1_000_000);
let fp1 = report1.get_device_fingerprint();
let fp2 = report2.get_device_fingerprint();
assert_ne!(
fp1, fp2,
"Different attestation levels should produce different fingerprints"
);
}
}
#[cfg(test)]
mod trust_enforcement_tests {
use super::*;
fn is_production_trust_level(level: &AttestationLevel) -> bool {
matches!(
level,
AttestationLevel::CloudTEE | AttestationLevel::StrongBox
)
}
fn is_development_trust_level(level: &AttestationLevel) -> bool {
matches!(level, AttestationLevel::TEE | AttestationLevel::Software)
}
#[test]
fn test_cloud_tee_is_production_trust() {
assert!(
is_production_trust_level(&AttestationLevel::CloudTEE),
"CloudTEE should be a production trust level"
);
}
#[test]
fn test_strongbox_is_production_trust() {
assert!(
is_production_trust_level(&AttestationLevel::StrongBox),
"StrongBox should be a production trust level"
);
}
#[test]
fn test_tee_is_development_trust() {
assert!(
is_development_trust_level(&AttestationLevel::TEE),
"TEE should be a development trust level"
);
}
#[test]
fn test_software_is_development_only() {
assert!(
is_development_trust_level(&AttestationLevel::Software),
"Software should be development-only"
);
assert!(
!is_production_trust_level(&AttestationLevel::Software),
"Software MUST NOT be a production trust level"
);
}
#[test]
fn test_production_signing_requires_hardware_attestation() {
let generator = MockAttestationGenerator::new(AttestationLevel::Software);
let nonce = [1, 2, 3, 4];
let now = 1_000_000_u64;
let report = generator.generate_valid_report(&nonce, now.saturating_sub(60));
let is_prod_trust = is_production_trust_level(&report.level);
let attestation_valid = report.verify_at_time(&nonce, now);
assert!(
!attestation_valid || !is_prod_trust,
"Production signing MUST require hardware attestation"
);
}
}
#[cfg(test)]
mod edge_case_tests {
use super::*;
#[test]
fn test_empty_signature_rejected() {
let report = DeviceIntegrityReport {
level: AttestationLevel::TEE,
challenge_nonce: vec![1, 2, 3, 4],
signature: vec![], certificate_chain: vec!["key".to_string(), "CONCLAVE_ROOT_CA_V1".to_string()],
timestamp: 1_000_000,
extension_data: "PURPOSE_SIGN|ALGORITHM_ED25519".to_string(),
};
assert!(
!report.verify(&[1, 2, 3, 4]),
"Empty signature should be rejected"
);
}
#[test]
fn test_empty_certificate_chain_rejected() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let mut report = generator.generate_valid_report(&[1, 2, 3, 4], 1_000_000);
report.certificate_chain.clear();
assert!(
!report.verify(&[1, 2, 3, 4]),
"Empty certificate chain should be rejected"
);
}
#[test]
fn test_single_certificate_rejected() {
let generator = MockAttestationGenerator::new(AttestationLevel::TEE);
let mut report = generator.generate_valid_report(&[1, 2, 3, 4], 1_000_000);
report.certificate_chain.pop();
assert!(
!report.verify(&[1, 2, 3, 4]),
"Single certificate should be rejected (need chain)"
);
}
#[test]
fn test_replay_guard_concurrent_access() {
use std::sync::{Arc, Barrier};
use std::thread;
let guard = Arc::new(ReplayGuard::new(300, 1000));
let barrier = Arc::new(Barrier::new(10));
let handles: Vec<_> = (0..10)
.map(|i| {
let guard = guard.clone();
let barrier = barrier.clone();
thread::spawn(move || {
barrier.wait();
guard.check_and_record(&format!("key-{}", i), 100)
})
})
.collect();
let results: Vec<bool> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert!(
results.iter().all(|r| *r),
"All unique attestation keys should be accepted"
);
}
}