1use a3s_box_core::error::{BoxError, Result};
9
10use super::attestation::{parse_platform_info, AttestationReport, PlatformInfo, SNP_REPORT_SIZE};
11use super::policy::{AttestationPolicy, PolicyResult, PolicyViolation};
12use super::simulate::is_simulated_report;
13
14#[derive(Debug, Clone)]
16pub struct VerificationResult {
17 pub verified: bool,
19 pub platform: PlatformInfo,
21 pub policy_result: PolicyResult,
23 pub signature_valid: bool,
25 pub cert_chain_valid: bool,
27 pub nonce_valid: bool,
29 pub report_age_valid: bool,
31 pub failures: Vec<String>,
33}
34
35pub fn verify_attestation(
55 report: &AttestationReport,
56 expected_nonce: &[u8],
57 policy: &AttestationPolicy,
58 allow_simulated: bool,
59) -> Result<VerificationResult> {
60 verify_attestation_with_time(report, expected_nonce, policy, allow_simulated, None)
61}
62
63pub fn verify_attestation_with_time(
78 report: &AttestationReport,
79 expected_nonce: &[u8],
80 policy: &AttestationPolicy,
81 allow_simulated: bool,
82 nonce_issued_at: Option<u64>,
83) -> Result<VerificationResult> {
84 let mut failures = Vec::new();
85
86 let platform = parse_platform_info(&report.report).ok_or_else(|| {
88 BoxError::AttestationError(format!(
89 "Invalid SNP report: expected {} bytes, got {}",
90 SNP_REPORT_SIZE,
91 report.report.len()
92 ))
93 })?;
94
95 let simulated = is_simulated_report(&report.report);
97 if simulated && !allow_simulated {
98 return Err(BoxError::AttestationError(
99 "Simulated report rejected: allow_simulated is false".to_string(),
100 ));
101 }
102 if simulated {
103 tracing::warn!("Accepting simulated TEE report (not hardware-attested)");
104 }
105
106 let nonce_valid = verify_nonce(&report.report, expected_nonce);
108 if !nonce_valid {
109 failures.push("Nonce mismatch: report_data does not contain expected nonce".to_string());
110 }
111
112 let signature_valid = if simulated {
114 true
115 } else {
116 let valid = verify_report_signature(&report.report, &report.cert_chain.vcek);
117 if !valid {
118 failures.push("Signature verification failed".to_string());
119 }
120 valid
121 };
122
123 let cert_chain_valid = if simulated {
125 true
126 } else {
127 let valid = verify_cert_chain(
128 &report.cert_chain.vcek,
129 &report.cert_chain.ask,
130 &report.cert_chain.ark,
131 );
132 if !valid {
133 failures.push("Certificate chain verification failed".to_string());
134 }
135 valid
136 };
137
138 let policy_result = check_policy(&platform, policy);
140 if !policy_result.passed {
141 for v in &policy_result.violations {
142 failures.push(v.to_string());
143 }
144 }
145
146 let report_age_valid = check_report_age(policy, nonce_issued_at, &mut failures);
148
149 let verified = nonce_valid
150 && signature_valid
151 && cert_chain_valid
152 && policy_result.passed
153 && report_age_valid;
154
155 Ok(VerificationResult {
156 verified,
157 platform,
158 policy_result,
159 signature_valid,
160 cert_chain_valid,
161 nonce_valid,
162 report_age_valid,
163 failures,
164 })
165}
166
167fn verify_nonce(report: &[u8], expected_nonce: &[u8]) -> bool {
172 if report.len() < 0x50 + 64 {
173 return false;
174 }
175
176 let report_data = &report[0x50..0x50 + 64];
177
178 let compare_len = expected_nonce.len().min(64);
181 report_data[..compare_len] == expected_nonce[..compare_len]
182}
183
184fn verify_report_signature(report: &[u8], vcek_der: &[u8]) -> bool {
191 if report.len() < SNP_REPORT_SIZE || vcek_der.is_empty() {
192 tracing::warn!(
193 report_len = report.len(),
194 vcek_len = vcek_der.len(),
195 "Cannot verify signature: invalid input sizes"
196 );
197 return false;
198 }
199
200 let signed_data = &report[..0x2A0];
202
203 let r_bytes = &report[0x2A0..0x2A0 + 72];
208 let s_bytes = &report[0x2A0 + 72..0x2A0 + 144];
209
210 let r_trimmed = trim_leading_zeros(r_bytes, 48);
212 let s_trimmed = trim_leading_zeros(s_bytes, 48);
213
214 match verify_p384_signature(signed_data, r_trimmed, s_trimmed, vcek_der) {
215 Ok(valid) => valid,
216 Err(e) => {
217 tracing::warn!("Signature verification error: {}", e);
218 false
219 }
220 }
221}
222
223fn trim_leading_zeros(bytes: &[u8], min_len: usize) -> &[u8] {
225 let start = bytes
226 .iter()
227 .position(|&b| b != 0)
228 .unwrap_or(bytes.len().saturating_sub(min_len));
229 let start = start.min(bytes.len().saturating_sub(min_len));
230 &bytes[start..]
231}
232
233fn verify_p384_signature(
235 message: &[u8],
236 r: &[u8],
237 s: &[u8],
238 cert_der: &[u8],
239) -> std::result::Result<bool, String> {
240 use der::Decode;
241 use p384::ecdsa::{signature::Verifier, Signature, VerifyingKey};
242 use x509_cert::Certificate;
243
244 let cert = Certificate::from_der(cert_der)
246 .map_err(|e| format!("Failed to parse VCEK certificate: {}", e))?;
247
248 let spki = cert.tbs_certificate.subject_public_key_info;
250 let pub_key_bytes = spki
251 .subject_public_key
252 .as_bytes()
253 .ok_or("Failed to extract public key bytes from VCEK")?;
254
255 let verifying_key = VerifyingKey::from_sec1_bytes(pub_key_bytes)
257 .map_err(|e| format!("Failed to create P-384 verifying key: {}", e))?;
258
259 let mut sig_bytes = [0u8; 96];
262 let r_offset = 48usize.saturating_sub(r.len());
264 let s_offset = 48usize.saturating_sub(s.len());
265 sig_bytes[r_offset..48].copy_from_slice(&r[r.len().saturating_sub(48)..]);
266 sig_bytes[48 + s_offset..96].copy_from_slice(&s[s.len().saturating_sub(48)..]);
267
268 let signature = Signature::from_slice(&sig_bytes)
269 .map_err(|e| format!("Failed to parse ECDSA signature: {}", e))?;
270
271 match verifying_key.verify(message, &signature) {
274 Ok(()) => Ok(true),
275 Err(_) => Ok(false),
276 }
277}
278
279fn verify_cert_chain(vcek_der: &[u8], ask_der: &[u8], ark_der: &[u8]) -> bool {
290 use der::Decode;
291 use x509_cert::Certificate;
292
293 if vcek_der.is_empty() || ask_der.is_empty() || ark_der.is_empty() {
299 tracing::warn!("Certificate chain incomplete — cannot verify, rejecting");
300 return false;
301 }
302
303 let vcek = match Certificate::from_der(vcek_der) {
305 Ok(c) => c,
306 Err(e) => {
307 tracing::warn!("Failed to parse VCEK certificate: {}", e);
308 return false;
309 }
310 };
311
312 let ask = match Certificate::from_der(ask_der) {
313 Ok(c) => c,
314 Err(e) => {
315 tracing::warn!("Failed to parse ASK certificate: {}", e);
316 return false;
317 }
318 };
319
320 let ark = match Certificate::from_der(ark_der) {
321 Ok(c) => c,
322 Err(e) => {
323 tracing::warn!("Failed to parse ARK certificate: {}", e);
324 return false;
325 }
326 };
327
328 if vcek.tbs_certificate.issuer != ask.tbs_certificate.subject {
331 tracing::warn!("VCEK issuer does not match ASK subject");
332 return false;
333 }
334
335 if ask.tbs_certificate.issuer != ark.tbs_certificate.subject {
337 tracing::warn!("ASK issuer does not match ARK subject");
338 return false;
339 }
340
341 if ark.tbs_certificate.issuer != ark.tbs_certificate.subject {
343 tracing::warn!("ARK is not self-signed");
344 return false;
345 }
346
347 if !verify_cert_signature(&ark, &ark) {
352 tracing::warn!("ARK self-signature verification failed");
353 return false;
354 }
355
356 if !verify_cert_signature(&ask, &ark) {
358 tracing::warn!("ASK signature verification failed (not signed by ARK)");
359 return false;
360 }
361
362 if !verify_cert_signature(&vcek, &ask) {
364 tracing::warn!("VCEK signature verification failed (not signed by ASK)");
365 return false;
366 }
367
368 if !super::ark_roots::is_trusted_ark(ark_der) {
374 tracing::warn!(
375 "ARK is not a pinned genuine AMD root key — rejecting (possible self-minted chain)"
376 );
377 return false;
378 }
379
380 true
381}
382
383fn verify_cert_signature(cert: &x509_cert::Certificate, issuer: &x509_cert::Certificate) -> bool {
391 use der::Encode;
392
393 let tbs_der = match cert.tbs_certificate.to_der() {
395 Ok(d) => d,
396 Err(e) => {
397 tracing::warn!("Failed to encode tbsCertificate to DER: {}", e);
398 return false;
399 }
400 };
401
402 let sig_bytes = match cert.signature.as_bytes() {
404 Some(b) => b,
405 None => {
406 tracing::warn!("Failed to extract signature bytes from certificate");
407 return false;
408 }
409 };
410
411 let issuer_spki = &issuer.tbs_certificate.subject_public_key_info;
414 let issuer_pub_key_bytes = match issuer_spki.subject_public_key.as_bytes() {
415 Some(b) => b,
416 None => {
417 tracing::warn!("Failed to extract issuer public key bytes");
418 return false;
419 }
420 };
421
422 match issuer_spki.algorithm.oid.to_string().as_str() {
423 "1.2.840.113549.1.1.1" | "1.2.840.113549.1.1.10" => {
425 verify_rsa_pss_sha384(&tbs_der, sig_bytes, issuer_pub_key_bytes)
426 }
427 "1.2.840.10045.2.1" => verify_ecdsa_p384_cert(&tbs_der, sig_bytes, issuer_pub_key_bytes),
429 other => {
430 tracing::warn!("Unsupported issuer key algorithm OID: {other}");
431 false
432 }
433 }
434}
435
436fn verify_rsa_pss_sha384(message: &[u8], signature: &[u8], rsa_pub_der: &[u8]) -> bool {
439 use ring::signature;
440 let key = signature::UnparsedPublicKey::new(&signature::RSA_PSS_2048_8192_SHA384, rsa_pub_der);
441 key.verify(message, signature).is_ok()
442}
443
444fn verify_ecdsa_p384_cert(message: &[u8], sig_der: &[u8], ec_pub_sec1: &[u8]) -> bool {
447 use p384::ecdsa::{signature::Verifier, DerSignature, VerifyingKey};
448
449 let signature = match DerSignature::from_bytes(sig_der) {
450 Ok(s) => s,
451 Err(e) => {
452 tracing::warn!("Failed to parse certificate ECDSA signature: {}", e);
453 return false;
454 }
455 };
456 let verifying_key = match VerifyingKey::from_sec1_bytes(ec_pub_sec1) {
457 Ok(k) => k,
458 Err(e) => {
459 tracing::warn!("Failed to create P-384 verifying key from issuer: {}", e);
460 return false;
461 }
462 };
463 verifying_key.verify(message, &signature).is_ok()
464}
465
466fn check_policy(platform: &PlatformInfo, policy: &AttestationPolicy) -> PolicyResult {
468 let mut violations = Vec::new();
469
470 if let Some(ref expected) = policy.expected_measurement {
472 if platform.measurement != *expected {
473 violations.push(PolicyViolation {
474 check: "measurement".to_string(),
475 reason: format!(
476 "Expected {}, got {}",
477 &expected[..expected.len().min(16)],
478 &platform.measurement[..platform.measurement.len().min(16)],
479 ),
480 });
481 }
482 }
483
484 if policy.require_no_debug {
486 let debug_enabled = (platform.policy >> 19) & 1 == 1;
487 if debug_enabled {
488 violations.push(PolicyViolation {
489 check: "debug".to_string(),
490 reason: "Debug mode is enabled (policy bit 19 set)".to_string(),
491 });
492 }
493 }
494
495 if policy.require_no_smt {
497 let smt_allowed = (platform.policy >> 16) & 1 == 1;
498 if smt_allowed {
499 violations.push(PolicyViolation {
500 check: "smt".to_string(),
501 reason: "SMT is enabled (policy bit 16 set)".to_string(),
502 });
503 }
504 }
505
506 if let Some(ref min_tcb) = policy.min_tcb {
508 let tcb = &platform.tcb_version;
509
510 if let Some(min_bl) = min_tcb.boot_loader {
511 if tcb.boot_loader < min_bl {
512 violations.push(PolicyViolation {
513 check: "tcb.boot_loader".to_string(),
514 reason: format!("Boot loader SVN {} < minimum {}", tcb.boot_loader, min_bl),
515 });
516 }
517 }
518
519 if let Some(min_tee) = min_tcb.tee {
520 if tcb.tee < min_tee {
521 violations.push(PolicyViolation {
522 check: "tcb.tee".to_string(),
523 reason: format!("TEE SVN {} < minimum {}", tcb.tee, min_tee),
524 });
525 }
526 }
527
528 if let Some(min_snp) = min_tcb.snp {
529 if tcb.snp < min_snp {
530 violations.push(PolicyViolation {
531 check: "tcb.snp".to_string(),
532 reason: format!("SNP SVN {} < minimum {}", tcb.snp, min_snp),
533 });
534 }
535 }
536
537 if let Some(min_uc) = min_tcb.microcode {
538 if tcb.microcode < min_uc {
539 violations.push(PolicyViolation {
540 check: "tcb.microcode".to_string(),
541 reason: format!("Microcode SVN {} < minimum {}", tcb.microcode, min_uc),
542 });
543 }
544 }
545 }
546
547 if let Some(mask) = policy.allowed_policy_mask {
549 if platform.policy & mask != mask {
550 violations.push(PolicyViolation {
551 check: "policy_mask".to_string(),
552 reason: format!(
553 "Guest policy {:#x} does not satisfy mask {:#x}",
554 platform.policy, mask,
555 ),
556 });
557 }
558 }
559
560 PolicyResult::from_violations(violations)
561}
562
563fn check_report_age(
572 policy: &AttestationPolicy,
573 nonce_issued_at: Option<u64>,
574 failures: &mut Vec<String>,
575) -> bool {
576 let max_age = match policy.max_report_age_secs {
577 Some(max) => max,
578 None => return true, };
580
581 let issued_at = match nonce_issued_at {
582 Some(t) => t,
583 None => {
584 tracing::warn!(
588 "max_report_age_secs={} set but nonce_issued_at not provided, skipping age check",
589 max_age
590 );
591 return true;
592 }
593 };
594
595 let now = std::time::SystemTime::now()
596 .duration_since(std::time::UNIX_EPOCH)
597 .map(|d| d.as_secs())
598 .unwrap_or(0);
599
600 if now < issued_at {
601 failures.push(format!(
603 "Report age check failed: nonce_issued_at ({}) is in the future (now={})",
604 issued_at, now
605 ));
606 return false;
607 }
608
609 let age = now - issued_at;
610 if age > max_age {
611 failures.push(format!(
612 "Report too old: age {}s exceeds maximum {}s (replay protection)",
613 age, max_age
614 ));
615 return false;
616 }
617
618 true
619}
620
621#[cfg(test)]
622mod tests {
623 use super::*;
624 use crate::tee::attestation::{CertificateChain, TcbVersion, SNP_REPORT_SIZE};
625 use crate::tee::policy::MinTcbPolicy;
626
627 fn make_test_report(nonce: &[u8]) -> Vec<u8> {
629 let mut report = vec![0u8; SNP_REPORT_SIZE];
630 report[0x00] = 2;
632 report[0x04] = 1;
634 let len = nonce.len().min(64);
636 report[0x50..0x50 + len].copy_from_slice(&nonce[..len]);
637 report[0x90] = 0xAA;
639 report[0x91] = 0xBB;
640 report[0x38] = 3; report[0x3E] = 8; report[0x3F] = 115; report
645 }
646
647 #[test]
648 fn test_verify_nonce_match() {
649 let nonce = vec![1, 2, 3, 4, 5, 6, 7, 8];
650 let report = make_test_report(&nonce);
651 assert!(verify_nonce(&report, &nonce));
652 }
653
654 #[test]
655 fn test_verify_nonce_mismatch() {
656 let nonce = vec![1, 2, 3, 4];
657 let report = make_test_report(&nonce);
658 let wrong_nonce = vec![9, 9, 9, 9];
659 assert!(!verify_nonce(&report, &wrong_nonce));
660 }
661
662 #[test]
663 fn test_verify_nonce_report_too_short() {
664 let report = vec![0u8; 10];
665 assert!(!verify_nonce(&report, &[1, 2, 3]));
666 }
667
668 #[test]
669 fn test_trim_leading_zeros() {
670 assert_eq!(trim_leading_zeros(&[0, 0, 0, 1, 2, 3], 3), &[1, 2, 3]);
671 assert_eq!(trim_leading_zeros(&[0, 0, 0, 0], 2), &[0, 0]);
672 assert_eq!(trim_leading_zeros(&[1, 2, 3], 3), &[1, 2, 3]);
673 assert_eq!(trim_leading_zeros(&[0, 1], 1), &[1]);
674 }
675
676 #[test]
677 fn test_verify_report_signature_empty_vcek() {
678 let report = vec![0u8; SNP_REPORT_SIZE];
679 assert!(!verify_report_signature(&report, &[]));
680 }
681
682 #[test]
683 fn test_verify_report_signature_short_report() {
684 assert!(!verify_report_signature(&[0u8; 100], &[1, 2, 3]));
685 }
686
687 #[test]
688 fn test_verify_cert_chain_empty_fails_closed() {
689 assert!(!verify_cert_chain(&[], &[], &[]));
692 }
693
694 #[test]
695 fn test_verify_cert_chain_partially_empty() {
696 assert!(!verify_cert_chain(&[1], &[], &[]));
698 assert!(!verify_cert_chain(&[], &[1], &[]));
699 assert!(!verify_cert_chain(&[], &[], &[1]));
700 }
701
702 fn make_test_cert_chain() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
709 use rcgen::{
710 BasicConstraints, CertificateParams, DistinguishedName, DnType, IsCa, KeyPair,
711 KeyUsagePurpose, PKCS_ECDSA_P384_SHA384,
712 };
713
714 let ark_key = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
716 let mut ark_params = CertificateParams::default();
717 let mut ark_dn = DistinguishedName::new();
718 ark_dn.push(DnType::CommonName, "AMD SEV ARK");
719 ark_dn.push(DnType::OrganizationName, "AMD");
720 ark_params.distinguished_name = ark_dn.clone();
721 ark_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
722 ark_params.key_usages = vec![KeyUsagePurpose::KeyCertSign];
723 let ark_cert = ark_params.self_signed(&ark_key).unwrap();
724
725 let ask_key = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
727 let mut ask_params = CertificateParams::default();
728 let mut ask_dn = DistinguishedName::new();
729 ask_dn.push(DnType::CommonName, "AMD SEV ASK");
730 ask_dn.push(DnType::OrganizationName, "AMD");
731 ask_params.distinguished_name = ask_dn;
732 ask_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
733 ask_params.key_usages = vec![KeyUsagePurpose::KeyCertSign];
734 let ask_cert = ask_params.signed_by(&ask_key, &ark_cert, &ark_key).unwrap();
735
736 let vcek_key = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
738 let mut vcek_params = CertificateParams::default();
739 let mut vcek_dn = DistinguishedName::new();
740 vcek_dn.push(DnType::CommonName, "AMD SEV VCEK");
741 vcek_dn.push(DnType::OrganizationName, "AMD");
742 vcek_params.distinguished_name = vcek_dn;
743 vcek_params.is_ca = IsCa::NoCa;
744 vcek_params.key_usages = vec![KeyUsagePurpose::DigitalSignature];
745 let vcek_cert = vcek_params
746 .signed_by(&vcek_key, &ask_cert, &ask_key)
747 .unwrap();
748
749 (
750 vcek_cert.der().to_vec(),
751 ask_cert.der().to_vec(),
752 ark_cert.der().to_vec(),
753 )
754 }
755
756 #[test]
757 fn test_verify_cert_chain_rejects_untrusted_synthetic_root() {
758 let (vcek, ask, ark) = make_test_cert_chain();
765 assert!(
766 !crate::tee::ark_roots::is_trusted_ark(&ark),
767 "a self-minted ARK must not be trusted"
768 );
769 assert!(
770 !verify_cert_chain(&vcek, &ask, &ark),
771 "an internally-consistent but self-minted chain must be rejected"
772 );
773 }
774
775 #[test]
776 fn test_genuine_amd_ark_self_signature_verifies_via_rsa_pss() {
777 use crate::tee::ark_roots::AMD_ARK_ROOTS;
778 use der::Decode;
779 for ark_der in AMD_ARK_ROOTS {
783 let ark = x509_cert::Certificate::from_der(ark_der).unwrap();
784 assert!(
785 verify_cert_signature(&ark, &ark),
786 "a genuine AMD ARK self-signature must verify via RSASSA-PSS/SHA-384"
787 );
788 }
789 }
790
791 #[test]
792 fn test_verify_cert_chain_wrong_ark_rejects() {
793 let (vcek, ask, _ark) = make_test_cert_chain();
794 let (_, _, wrong_ark) = make_test_cert_chain();
796 assert!(!verify_cert_chain(&vcek, &ask, &wrong_ark));
798 }
799
800 #[test]
801 fn test_verify_cert_chain_wrong_ask_rejects() {
802 let (vcek, _ask, ark) = make_test_cert_chain();
803 let (_, wrong_ask, _) = make_test_cert_chain();
805 assert!(!verify_cert_chain(&vcek, &wrong_ask, &ark));
807 }
808
809 #[test]
810 fn test_verify_cert_chain_swapped_ask_ark_rejects() {
811 let (vcek, ask, ark) = make_test_cert_chain();
812 assert!(!verify_cert_chain(&vcek, &ark, &ask));
815 }
816
817 #[test]
818 fn test_verify_cert_signature_self_signed() {
819 use der::Decode;
820 use rcgen::{
821 CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_ECDSA_P384_SHA384,
822 };
823
824 let key = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
825 let mut params = CertificateParams::default();
826 let mut dn = DistinguishedName::new();
827 dn.push(DnType::CommonName, "Test Self-Signed");
828 params.distinguished_name = dn;
829 let cert = params.self_signed(&key).unwrap();
830
831 let parsed = x509_cert::Certificate::from_der(cert.der()).unwrap();
832 assert!(verify_cert_signature(&parsed, &parsed));
833 }
834
835 #[test]
836 fn test_verify_cert_signature_wrong_issuer_rejects() {
837 use der::Decode;
838 use rcgen::{
839 CertificateParams, DistinguishedName, DnType, KeyPair, PKCS_ECDSA_P384_SHA384,
840 };
841
842 let key1 = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
844 let mut params1 = CertificateParams::default();
845 let mut dn1 = DistinguishedName::new();
846 dn1.push(DnType::CommonName, "Cert A");
847 params1.distinguished_name = dn1;
848 let cert1 = params1.self_signed(&key1).unwrap();
849
850 let key2 = KeyPair::generate_for(&PKCS_ECDSA_P384_SHA384).unwrap();
851 let mut params2 = CertificateParams::default();
852 let mut dn2 = DistinguishedName::new();
853 dn2.push(DnType::CommonName, "Cert B");
854 params2.distinguished_name = dn2;
855 let cert2 = params2.self_signed(&key2).unwrap();
856
857 let parsed1 = x509_cert::Certificate::from_der(cert1.der()).unwrap();
858 let parsed2 = x509_cert::Certificate::from_der(cert2.der()).unwrap();
859
860 assert!(!verify_cert_signature(&parsed1, &parsed2));
862 }
863
864 #[test]
865 fn test_check_policy_pass_default() {
866 let platform = PlatformInfo {
867 version: 2,
868 guest_svn: 1,
869 policy: 0, measurement: "aabb".repeat(24),
871 tcb_version: TcbVersion {
872 boot_loader: 3,
873 tee: 0,
874 snp: 8,
875 microcode: 115,
876 },
877 chip_id: "00".repeat(64),
878 };
879 let policy = AttestationPolicy::default();
880 let result = check_policy(&platform, &policy);
881 assert!(result.passed);
882 assert!(result.violations.is_empty());
883 }
884
885 #[test]
886 fn test_check_policy_debug_violation() {
887 let platform = PlatformInfo {
888 policy: 1 << 19, ..Default::default()
890 };
891 let policy = AttestationPolicy {
892 require_no_debug: true,
893 ..Default::default()
894 };
895 let result = check_policy(&platform, &policy);
896 assert!(!result.passed);
897 assert!(result.violations.iter().any(|v| v.check == "debug"));
898 }
899
900 #[test]
901 fn test_check_policy_smt_violation() {
902 let platform = PlatformInfo {
903 policy: 1 << 16, ..Default::default()
905 };
906 let policy = AttestationPolicy {
907 require_no_debug: false,
908 require_no_smt: true,
909 ..Default::default()
910 };
911 let result = check_policy(&platform, &policy);
912 assert!(!result.passed);
913 assert!(result.violations.iter().any(|v| v.check == "smt"));
914 }
915
916 #[test]
917 fn test_check_policy_measurement_mismatch() {
918 let platform = PlatformInfo {
919 measurement: "aa".repeat(48),
920 ..Default::default()
921 };
922 let policy = AttestationPolicy {
923 expected_measurement: Some("bb".repeat(48)),
924 require_no_debug: false,
925 ..Default::default()
926 };
927 let result = check_policy(&platform, &policy);
928 assert!(!result.passed);
929 assert!(result.violations.iter().any(|v| v.check == "measurement"));
930 }
931
932 #[test]
933 fn test_check_policy_measurement_match() {
934 let m = "aa".repeat(48);
935 let platform = PlatformInfo {
936 measurement: m.clone(),
937 ..Default::default()
938 };
939 let policy = AttestationPolicy {
940 expected_measurement: Some(m),
941 require_no_debug: false,
942 ..Default::default()
943 };
944 let result = check_policy(&platform, &policy);
945 assert!(result.passed);
946 }
947
948 #[test]
949 fn test_check_policy_tcb_violation() {
950 let platform = PlatformInfo {
951 tcb_version: TcbVersion {
952 boot_loader: 2,
953 tee: 0,
954 snp: 5,
955 microcode: 100,
956 },
957 ..Default::default()
958 };
959 let policy = AttestationPolicy {
960 require_no_debug: false,
961 min_tcb: Some(MinTcbPolicy {
962 snp: Some(8), microcode: Some(93), ..Default::default()
965 }),
966 ..Default::default()
967 };
968 let result = check_policy(&platform, &policy);
969 assert!(!result.passed);
970 assert!(result.violations.iter().any(|v| v.check == "tcb.snp"));
971 assert!(!result.violations.iter().any(|v| v.check == "tcb.microcode"));
972 }
973
974 #[test]
975 fn test_check_policy_mask_violation() {
976 let platform = PlatformInfo {
977 policy: 0x30, ..Default::default()
979 };
980 let policy = AttestationPolicy {
981 require_no_debug: false,
982 allowed_policy_mask: Some(0x70), ..Default::default()
984 };
985 let result = check_policy(&platform, &policy);
986 assert!(!result.passed);
987 assert!(result.violations.iter().any(|v| v.check == "policy_mask"));
988 }
989
990 #[test]
991 fn test_verify_attestation_nonce_mismatch() {
992 let nonce = vec![1, 2, 3, 4];
993 let report_bytes = make_test_report(&nonce);
994 let report = AttestationReport {
995 report: report_bytes,
996 cert_chain: CertificateChain::default(),
997 platform: PlatformInfo::default(),
998 };
999 let wrong_nonce = vec![9, 9, 9, 9];
1000 let policy = AttestationPolicy {
1001 require_no_debug: false,
1002 ..Default::default()
1003 };
1004 let result = verify_attestation(&report, &wrong_nonce, &policy, false).unwrap();
1005 assert!(!result.verified);
1006 assert!(!result.nonce_valid);
1007 }
1008
1009 #[test]
1010 fn test_verify_attestation_invalid_report_size() {
1011 let report = AttestationReport {
1012 report: vec![0u8; 100], cert_chain: CertificateChain::default(),
1014 platform: PlatformInfo::default(),
1015 };
1016 let result = verify_attestation(&report, &[1, 2, 3], &AttestationPolicy::default(), false);
1017 assert!(result.is_err());
1018 }
1019
1020 #[test]
1021 fn test_verify_simulated_report_accepted() {
1022 let nonce = vec![1, 2, 3, 4];
1023 let mut report_data = [0u8; 64];
1024 report_data[..4].copy_from_slice(&nonce);
1025 let report_bytes = crate::tee::simulate::build_simulated_report(&report_data);
1026 let report = AttestationReport {
1027 report: report_bytes,
1028 cert_chain: CertificateChain::default(),
1029 platform: PlatformInfo::default(),
1030 };
1031 let policy = AttestationPolicy {
1032 require_no_debug: false,
1033 ..Default::default()
1034 };
1035 let result = verify_attestation(&report, &nonce, &policy, true).unwrap();
1036 assert!(result.verified);
1037 assert!(result.signature_valid);
1038 assert!(result.cert_chain_valid);
1039 assert!(result.nonce_valid);
1040 }
1041
1042 #[test]
1043 fn test_verify_simulated_report_rejected_when_not_allowed() {
1044 let nonce = vec![1, 2, 3, 4];
1045 let mut report_data = [0u8; 64];
1046 report_data[..4].copy_from_slice(&nonce);
1047 let report_bytes = crate::tee::simulate::build_simulated_report(&report_data);
1048 let report = AttestationReport {
1049 report: report_bytes,
1050 cert_chain: CertificateChain::default(),
1051 platform: PlatformInfo::default(),
1052 };
1053 let policy = AttestationPolicy::default();
1054 let result = verify_attestation(&report, &nonce, &policy, false);
1055 assert!(result.is_err());
1056 }
1057
1058 #[test]
1059 fn test_verify_simulated_report_nonce_still_checked() {
1060 let nonce = vec![1, 2, 3, 4];
1061 let mut report_data = [0u8; 64];
1062 report_data[..4].copy_from_slice(&nonce);
1063 let report_bytes = crate::tee::simulate::build_simulated_report(&report_data);
1064 let report = AttestationReport {
1065 report: report_bytes,
1066 cert_chain: CertificateChain::default(),
1067 platform: PlatformInfo::default(),
1068 };
1069 let wrong_nonce = vec![9, 9, 9, 9];
1070 let policy = AttestationPolicy {
1071 require_no_debug: false,
1072 ..Default::default()
1073 };
1074 let result = verify_attestation(&report, &wrong_nonce, &policy, true).unwrap();
1075 assert!(!result.verified);
1076 assert!(!result.nonce_valid);
1077 }
1078
1079 fn now_secs() -> u64 {
1084 std::time::SystemTime::now()
1085 .duration_since(std::time::UNIX_EPOCH)
1086 .unwrap()
1087 .as_secs()
1088 }
1089
1090 #[test]
1091 fn test_check_report_age_no_policy() {
1092 let policy = AttestationPolicy {
1094 require_no_debug: false,
1095 ..Default::default()
1096 };
1097 let mut failures = Vec::new();
1098 assert!(check_report_age(
1099 &policy,
1100 Some(now_secs() - 9999),
1101 &mut failures
1102 ));
1103 assert!(failures.is_empty());
1104 }
1105
1106 #[test]
1107 fn test_check_report_age_no_timestamp() {
1108 let policy = AttestationPolicy {
1110 require_no_debug: false,
1111 max_report_age_secs: Some(60),
1112 ..Default::default()
1113 };
1114 let mut failures = Vec::new();
1115 assert!(check_report_age(&policy, None, &mut failures));
1116 assert!(failures.is_empty());
1117 }
1118
1119 #[test]
1120 fn test_check_report_age_fresh_report() {
1121 let policy = AttestationPolicy {
1122 require_no_debug: false,
1123 max_report_age_secs: Some(60),
1124 ..Default::default()
1125 };
1126 let mut failures = Vec::new();
1127 assert!(check_report_age(
1129 &policy,
1130 Some(now_secs() - 5),
1131 &mut failures
1132 ));
1133 assert!(failures.is_empty());
1134 }
1135
1136 #[test]
1137 fn test_check_report_age_stale_report() {
1138 let policy = AttestationPolicy {
1139 require_no_debug: false,
1140 max_report_age_secs: Some(60),
1141 ..Default::default()
1142 };
1143 let mut failures = Vec::new();
1144 assert!(!check_report_age(
1146 &policy,
1147 Some(now_secs() - 120),
1148 &mut failures
1149 ));
1150 assert!(failures.len() == 1);
1151 assert!(failures[0].contains("too old"));
1152 }
1153
1154 #[test]
1155 fn test_check_report_age_future_timestamp() {
1156 let policy = AttestationPolicy {
1157 require_no_debug: false,
1158 max_report_age_secs: Some(60),
1159 ..Default::default()
1160 };
1161 let mut failures = Vec::new();
1162 assert!(!check_report_age(
1164 &policy,
1165 Some(now_secs() + 3600),
1166 &mut failures
1167 ));
1168 assert!(failures[0].contains("future"));
1169 }
1170
1171 #[test]
1172 fn test_check_report_age_exact_boundary() {
1173 let policy = AttestationPolicy {
1174 require_no_debug: false,
1175 max_report_age_secs: Some(60),
1176 ..Default::default()
1177 };
1178 let mut failures = Vec::new();
1179 assert!(check_report_age(
1181 &policy,
1182 Some(now_secs() - 60),
1183 &mut failures
1184 ));
1185 assert!(failures.is_empty());
1186 }
1187
1188 #[test]
1189 fn test_verify_attestation_with_time_fresh() {
1190 let nonce = vec![1, 2, 3, 4];
1191 let mut report_data = [0u8; 64];
1192 report_data[..4].copy_from_slice(&nonce);
1193 let report_bytes = crate::tee::simulate::build_simulated_report(&report_data);
1194 let report = AttestationReport {
1195 report: report_bytes,
1196 cert_chain: CertificateChain::default(),
1197 platform: PlatformInfo::default(),
1198 };
1199 let policy = AttestationPolicy {
1200 require_no_debug: false,
1201 max_report_age_secs: Some(60),
1202 ..Default::default()
1203 };
1204 let result =
1205 verify_attestation_with_time(&report, &nonce, &policy, true, Some(now_secs() - 5))
1206 .unwrap();
1207 assert!(result.verified);
1208 assert!(result.report_age_valid);
1209 }
1210
1211 #[test]
1212 fn test_verify_attestation_with_time_stale() {
1213 let nonce = vec![1, 2, 3, 4];
1214 let mut report_data = [0u8; 64];
1215 report_data[..4].copy_from_slice(&nonce);
1216 let report_bytes = crate::tee::simulate::build_simulated_report(&report_data);
1217 let report = AttestationReport {
1218 report: report_bytes,
1219 cert_chain: CertificateChain::default(),
1220 platform: PlatformInfo::default(),
1221 };
1222 let policy = AttestationPolicy {
1223 require_no_debug: false,
1224 max_report_age_secs: Some(30),
1225 ..Default::default()
1226 };
1227 let result =
1228 verify_attestation_with_time(&report, &nonce, &policy, true, Some(now_secs() - 120))
1229 .unwrap();
1230 assert!(!result.verified);
1231 assert!(!result.report_age_valid);
1232 }
1233}