1use serde::{Deserialize, Serialize};
23
24use crate::error::GatekeeperError;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
32pub enum TeePlatform {
33 Sgx,
35 TrustZone,
37 Sev,
39 Simulated,
41}
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
49pub enum TeeEnvironment {
50 Production,
52 Testing,
54}
55
56#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
62pub struct TeeAttestation {
63 pub platform: TeePlatform,
65 pub measurement_hash: [u8; 32],
67 pub timestamp: u64,
69 pub signature: Vec<u8>,
71}
72
73impl std::fmt::Debug for TeeAttestation {
74 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75 f.debug_struct("TeeAttestation")
76 .field("platform", &self.platform)
77 .field("measurement_hash", &"[REDACTED]")
78 .field("timestamp", &self.timestamp)
79 .field("signature", &"[REDACTED]")
80 .finish()
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
90pub struct TeePolicy {
91 pub accepted_platforms: Vec<TeePlatform>,
93 pub required_measurements: Vec<[u8; 32]>,
95 pub max_age_ms: u64,
97 pub current_time_ms: u64,
99 pub environment: TeeEnvironment,
101}
102
103impl Default for TeePolicy {
104 fn default() -> Self {
106 Self::production()
107 }
108}
109
110impl TeePolicy {
111 #[must_use]
113 pub fn production() -> Self {
114 Self {
115 accepted_platforms: vec![TeePlatform::Sgx, TeePlatform::TrustZone, TeePlatform::Sev],
116 required_measurements: vec![],
117 max_age_ms: 0,
118 current_time_ms: 0,
119 environment: TeeEnvironment::Production,
120 }
121 }
122
123 #[must_use]
125 pub fn testing() -> Self {
126 Self {
127 accepted_platforms: vec![
128 TeePlatform::Sgx,
129 TeePlatform::TrustZone,
130 TeePlatform::Sev,
131 TeePlatform::Simulated,
132 ],
133 required_measurements: vec![],
134 max_age_ms: 0,
135 current_time_ms: 0,
136 environment: TeeEnvironment::Testing,
137 }
138 }
139}
140
141fn is_platform_allowed(platform: &TeePlatform, policy: &TeePolicy) -> bool {
148 if *platform == TeePlatform::Simulated && policy.environment != TeeEnvironment::Testing {
149 return false;
150 }
151 policy.accepted_platforms.contains(platform)
152}
153
154#[must_use]
164pub fn generate_attestation(
165 platform: &TeePlatform,
166 measurement: &[u8],
167 timestamp: u64,
168) -> TeeAttestation {
169 let measurement_hash = *blake3::hash(measurement).as_bytes();
170
171 TeeAttestation {
172 platform: *platform,
173 measurement_hash,
174 timestamp,
175 signature: synthetic_attestation_signature(platform, &measurement_hash, timestamp),
176 }
177}
178
179fn tee_platform_tag(platform: &TeePlatform) -> &'static [u8] {
181 match platform {
182 TeePlatform::Sgx => b"tee.platform.sgx.v1",
183 TeePlatform::Sev => b"tee.platform.sev.v1",
184 TeePlatform::TrustZone => b"tee.platform.trustzone.v1",
185 TeePlatform::Simulated => b"tee.platform.simulated.v1",
186 }
187}
188
189fn synthetic_attestation_signature(
190 platform: &TeePlatform,
191 measurement_hash: &[u8; 32],
192 timestamp: u64,
193) -> Vec<u8> {
194 let mut sig_input = Vec::new();
195 sig_input.extend_from_slice(measurement_hash);
196 sig_input.extend_from_slice(×tamp.to_le_bytes());
197 sig_input.extend_from_slice(tee_platform_tag(platform));
198 blake3::hash(&sig_input).as_bytes().to_vec()
199}
200
201fn synthetic_signature_allowed(attestation: &TeeAttestation, policy: &TeePolicy) -> bool {
202 if attestation.platform != TeePlatform::Simulated {
203 return false;
204 }
205
206 policy.environment == TeeEnvironment::Testing
207}
208
209fn measurement_hash_eq_ct(left: &[u8; 32], right: &[u8; 32]) -> bool {
210 let mut diff = 0u8;
211 for (left_byte, right_byte) in left.iter().zip(right.iter()) {
212 diff |= left_byte ^ right_byte;
213 }
214 diff == 0
215}
216
217fn required_measurement_matches(
218 required_measurements: &[[u8; 32]],
219 measurement: &[u8; 32],
220) -> bool {
221 let mut matched = 0u8;
222 for required in required_measurements {
223 matched |= u8::from(measurement_hash_eq_ct(required, measurement));
224 }
225 matched != 0
226}
227
228pub trait TeeQuoteVerifier {
238 fn verify_quote(
244 &self,
245 attestation: &TeeAttestation,
246 policy: &TeePolicy,
247 ) -> Result<(), GatekeeperError>;
248}
249
250impl<F> TeeQuoteVerifier for F
251where
252 F: Fn(&TeeAttestation, &TeePolicy) -> Result<(), GatekeeperError>,
253{
254 fn verify_quote(
255 &self,
256 attestation: &TeeAttestation,
257 policy: &TeePolicy,
258 ) -> Result<(), GatekeeperError> {
259 self(attestation, policy)
260 }
261}
262
263enum AttestationSignatureKind {
264 SimulatedFixture,
265 HardwareQuote,
266}
267
268fn check_attestation_policy(
269 attestation: &TeeAttestation,
270 policy: &TeePolicy,
271) -> Result<AttestationSignatureKind, GatekeeperError> {
272 if !is_platform_allowed(&attestation.platform, policy) {
274 return Err(GatekeeperError::TeeError(format!(
275 "Platform {:?} is not accepted by policy",
276 attestation.platform
277 )));
278 }
279
280 if !policy.required_measurements.is_empty()
282 && !required_measurement_matches(
283 &policy.required_measurements,
284 &attestation.measurement_hash,
285 )
286 {
287 return Err(GatekeeperError::TeeError(
288 "Measurement hash does not match any required measurement".into(),
289 ));
290 }
291
292 if attestation.signature.is_empty() {
294 return Err(GatekeeperError::TeeError(
295 "Attestation signature is empty".into(),
296 ));
297 }
298
299 if policy.max_age_ms > 0 {
301 let age = policy
302 .current_time_ms
303 .checked_sub(attestation.timestamp)
304 .ok_or_else(|| {
305 GatekeeperError::TeeError(format!(
306 "Attestation timestamp {} is in the future relative to policy time {}",
307 attestation.timestamp, policy.current_time_ms
308 ))
309 })?;
310 if age > policy.max_age_ms {
311 return Err(GatekeeperError::TeeError(format!(
312 "Attestation is too old: {} ms (max: {} ms)",
313 age, policy.max_age_ms
314 )));
315 }
316 }
317
318 let synthetic_sig = synthetic_attestation_signature(
319 &attestation.platform,
320 &attestation.measurement_hash,
321 attestation.timestamp,
322 );
323
324 if attestation.signature == synthetic_sig {
325 if synthetic_signature_allowed(attestation, policy) {
326 return Ok(AttestationSignatureKind::SimulatedFixture);
327 }
328 return Err(GatekeeperError::TeeError(
329 "synthetic TEE attestation signatures are only accepted for simulated TEEs in explicitly allowed environments".into(),
330 ));
331 }
332
333 if attestation.platform == TeePlatform::Simulated {
334 return Err(GatekeeperError::TeeError(
335 "simulated TEE attestation signature verification failed".into(),
336 ));
337 }
338
339 Ok(AttestationSignatureKind::HardwareQuote)
340}
341
342pub fn verify_attestation(
344 attestation: &TeeAttestation,
345 policy: &TeePolicy,
346) -> Result<(), GatekeeperError> {
347 match check_attestation_policy(attestation, policy)? {
348 AttestationSignatureKind::SimulatedFixture => Ok(()),
349 AttestationSignatureKind::HardwareQuote => Err(GatekeeperError::TeeError(format!(
350 "Hardware TEE attestation for platform {:?} requires a platform quote verifier",
351 attestation.platform
352 ))),
353 }
354}
355
356pub fn verify_attestation_with_quote_verifier<V>(
362 attestation: &TeeAttestation,
363 policy: &TeePolicy,
364 verifier: &V,
365) -> Result<(), GatekeeperError>
366where
367 V: TeeQuoteVerifier + ?Sized,
368{
369 match check_attestation_policy(attestation, policy)? {
370 AttestationSignatureKind::SimulatedFixture => Ok(()),
371 AttestationSignatureKind::HardwareQuote => verifier.verify_quote(attestation, policy),
372 }
373}
374
375#[cfg(test)]
380mod tests {
381 use super::*;
382
383 const MEASUREMENT: &[u8] = b"enclave-binary-v1.0";
384 const TIMESTAMP: u64 = 1_700_000_000_000;
385
386 fn valid_attestation() -> TeeAttestation {
387 generate_attestation(&TeePlatform::Simulated, MEASUREMENT, TIMESTAMP)
388 }
389
390 fn permissive_policy() -> TeePolicy {
391 TeePolicy {
392 accepted_platforms: vec![
393 TeePlatform::Sgx,
394 TeePlatform::TrustZone,
395 TeePlatform::Sev,
396 TeePlatform::Simulated,
397 ],
398 required_measurements: vec![],
399 max_age_ms: 0,
400 current_time_ms: TIMESTAMP,
401 environment: TeeEnvironment::Testing,
402 }
403 }
404
405 fn accepting_sgx_quote_verifier(
406 verified_att: &TeeAttestation,
407 _policy: &TeePolicy,
408 ) -> Result<(), GatekeeperError> {
409 if verified_att.platform == TeePlatform::Sgx && verified_att.signature == vec![0xA5; 64] {
410 Ok(())
411 } else {
412 Err(GatekeeperError::TeeError(
413 "unexpected quote material".into(),
414 ))
415 }
416 }
417
418 fn revoked_quote_verifier(
419 _attestation: &TeeAttestation,
420 _policy: &TeePolicy,
421 ) -> Result<(), GatekeeperError> {
422 Err(GatekeeperError::TeeError("quote revoked".into()))
423 }
424
425 fn panic_quote_verifier(
426 _attestation: &TeeAttestation,
427 _policy: &TeePolicy,
428 ) -> Result<(), GatekeeperError> {
429 panic!("synthetic hardware attestations must not reach quote verifier")
430 }
431
432 #[test]
435 fn generate_produces_valid_attestation() {
436 let att = valid_attestation();
437 assert_eq!(att.platform, TeePlatform::Simulated);
438 assert_eq!(att.timestamp, TIMESTAMP);
439 assert!(!att.signature.is_empty());
440 assert_eq!(att.measurement_hash, *blake3::hash(MEASUREMENT).as_bytes());
441 }
442
443 #[test]
444 fn tee_attestation_debug_redacts_sensitive_material() {
445 let att = valid_attestation();
446 let rendered = format!("{att:?}");
447
448 assert!(rendered.contains("TeeAttestation"));
449 assert!(rendered.contains("measurement_hash: \"[REDACTED]\""));
450 assert!(rendered.contains("signature: \"[REDACTED]\""));
451 assert!(!rendered.contains(&format!("{:?}", att.measurement_hash)));
452 assert!(!rendered.contains(&format!("{:?}", att.signature)));
453 }
454
455 #[test]
456 fn generate_is_deterministic() {
457 let att1 = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
458 let att2 = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
459 assert_eq!(att1, att2);
460 }
461
462 #[test]
463 fn generate_different_platforms_produce_different_signatures() {
464 let att_sgx = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
465 let att_sev = generate_attestation(&TeePlatform::Sev, MEASUREMENT, TIMESTAMP);
466 assert_ne!(att_sgx.signature, att_sev.signature);
467 }
468
469 #[test]
470 fn generate_different_measurements_produce_different_hashes() {
471 let att1 = generate_attestation(&TeePlatform::Simulated, b"binary-v1", TIMESTAMP);
472 let att2 = generate_attestation(&TeePlatform::Simulated, b"binary-v2", TIMESTAMP);
473 assert_ne!(att1.measurement_hash, att2.measurement_hash);
474 }
475
476 #[test]
479 fn verify_passes_for_valid_attestation() {
480 let att = valid_attestation();
481 let policy = permissive_policy();
482 assert!(verify_attestation(&att, &policy).is_ok());
483 }
484
485 #[test]
486 fn verify_rejects_synthetic_sgx_attestation() {
487 let att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
488 let policy = permissive_policy();
489 let result = verify_attestation(&att, &policy);
490 assert!(result.is_err());
491 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
492 }
493
494 #[test]
495 fn verify_rejects_synthetic_trustzone_attestation() {
496 let att = generate_attestation(&TeePlatform::TrustZone, MEASUREMENT, TIMESTAMP);
497 let policy = permissive_policy();
498 let result = verify_attestation(&att, &policy);
499 assert!(result.is_err());
500 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
501 }
502
503 #[test]
504 fn verify_rejects_synthetic_sev_attestation() {
505 let att = generate_attestation(&TeePlatform::Sev, MEASUREMENT, TIMESTAMP);
506 let policy = permissive_policy();
507 let result = verify_attestation(&att, &policy);
508 assert!(result.is_err());
509 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
510 }
511
512 #[test]
515 fn verify_rejects_unaccepted_platform() {
516 let att = valid_attestation(); let policy = TeePolicy {
518 accepted_platforms: vec![TeePlatform::Sgx], required_measurements: vec![],
520 max_age_ms: 0,
521 current_time_ms: TIMESTAMP,
522 environment: TeeEnvironment::Testing,
523 };
524 let result = verify_attestation(&att, &policy);
525 assert!(result.is_err());
526 assert!(format!("{}", result.unwrap_err()).contains("not accepted"));
527 }
528
529 #[test]
532 fn verify_rejects_measurement_mismatch() {
533 let att = valid_attestation();
534 let policy = TeePolicy {
535 accepted_platforms: vec![TeePlatform::Simulated],
536 required_measurements: vec![[0u8; 32]], max_age_ms: 0,
538 current_time_ms: TIMESTAMP,
539 environment: TeeEnvironment::Testing,
540 };
541 let result = verify_attestation(&att, &policy);
542 assert!(result.is_err());
543 assert!(format!("{}", result.unwrap_err()).contains("Measurement"));
544 }
545
546 #[test]
547 fn verify_passes_when_measurement_matches() {
548 let att = valid_attestation();
549 let policy = TeePolicy {
550 accepted_platforms: vec![TeePlatform::Simulated],
551 required_measurements: vec![att.measurement_hash],
552 max_age_ms: 0,
553 current_time_ms: TIMESTAMP,
554 environment: TeeEnvironment::Testing,
555 };
556 assert!(verify_attestation(&att, &policy).is_ok());
557 }
558
559 #[test]
562 fn verify_rejects_empty_signature() {
563 let mut att = valid_attestation();
564 att.signature = vec![]; let policy = permissive_policy();
566 let result = verify_attestation(&att, &policy);
567 assert!(result.is_err());
568 assert!(format!("{}", result.unwrap_err()).contains("empty"));
569 }
570
571 #[test]
574 fn verify_rejects_expired_attestation() {
575 let att = valid_attestation(); let policy = TeePolicy {
577 accepted_platforms: vec![TeePlatform::Simulated],
578 required_measurements: vec![],
579 max_age_ms: 1000, current_time_ms: TIMESTAMP + 5000, environment: TeeEnvironment::Testing,
582 };
583 let result = verify_attestation(&att, &policy);
584 assert!(result.is_err());
585 assert!(format!("{}", result.unwrap_err()).contains("too old"));
586 }
587
588 #[test]
589 fn verify_passes_within_age_limit() {
590 let att = valid_attestation();
591 let policy = TeePolicy {
592 accepted_platforms: vec![TeePlatform::Simulated],
593 required_measurements: vec![],
594 max_age_ms: 10_000,
595 current_time_ms: TIMESTAMP + 5_000,
596 environment: TeeEnvironment::Testing,
597 };
598 assert!(verify_attestation(&att, &policy).is_ok());
599 }
600
601 #[test]
602 fn verify_rejects_future_dated_attestation_when_age_limit_is_enforced() {
603 let att = generate_attestation(&TeePlatform::Simulated, MEASUREMENT, TIMESTAMP + 5_000);
604 let policy = TeePolicy {
605 accepted_platforms: vec![TeePlatform::Simulated],
606 required_measurements: vec![],
607 max_age_ms: 1_000,
608 current_time_ms: TIMESTAMP,
609 environment: TeeEnvironment::Testing,
610 };
611
612 let result = verify_attestation(&att, &policy);
613
614 assert!(result.is_err());
615 assert!(format!("{}", result.unwrap_err()).contains("future"));
616 }
617
618 #[test]
619 fn verify_no_age_limit_passes_old_attestation() {
620 let att = valid_attestation();
621 let policy = TeePolicy {
622 accepted_platforms: vec![TeePlatform::Simulated],
623 required_measurements: vec![],
624 max_age_ms: 0, current_time_ms: TIMESTAMP + 999_999_999,
626 environment: TeeEnvironment::Testing,
627 };
628 assert!(verify_attestation(&att, &policy).is_ok());
629 }
630
631 #[test]
634 fn verify_rejects_tampered_signature() {
635 let mut att = valid_attestation();
636 att.signature = vec![0xDE, 0xAD, 0xBE, 0xEF]; let policy = permissive_policy();
638 let result = verify_attestation(&att, &policy);
639 assert!(result.is_err());
640 assert!(format!("{}", result.unwrap_err()).contains("verification failed"));
641 }
642
643 #[test]
644 fn verify_rejects_tampered_timestamp() {
645 let mut att = valid_attestation();
646 att.timestamp += 1; let policy = permissive_policy();
648 let result = verify_attestation(&att, &policy);
649 assert!(result.is_err());
650 }
651
652 #[test]
653 fn verify_rejects_tampered_measurement() {
654 let mut att = valid_attestation();
655 att.measurement_hash[0] ^= 0xFF; let policy = permissive_policy();
657 let result = verify_attestation(&att, &policy);
658 assert!(result.is_err());
659 }
660
661 #[test]
664 fn verify_passes_when_one_of_multiple_measurements_matches() {
665 let att = valid_attestation();
666 let policy = TeePolicy {
667 accepted_platforms: vec![TeePlatform::Simulated],
668 required_measurements: vec![[0u8; 32], att.measurement_hash, [1u8; 32]],
669 max_age_ms: 0,
670 current_time_ms: TIMESTAMP,
671 environment: TeeEnvironment::Testing,
672 };
673 assert!(verify_attestation(&att, &policy).is_ok());
674 }
675
676 #[test]
677 fn required_measurement_matcher_handles_empty_match_and_mismatch() {
678 let att = valid_attestation();
679
680 assert!(!required_measurement_matches(&[], &att.measurement_hash));
681 assert!(required_measurement_matches(
682 &[[0u8; 32], att.measurement_hash, [1u8; 32]],
683 &att.measurement_hash
684 ));
685 assert!(!required_measurement_matches(
686 &[[0u8; 32], [1u8; 32]],
687 &att.measurement_hash
688 ));
689 }
690
691 #[test]
692 fn measurement_policy_does_not_use_short_circuit_contains() {
693 let source = include_str!("tee.rs");
694 let start = source
695 .find("fn check_attestation_policy(")
696 .expect("check_attestation_policy source exists");
697 let end = source[start..]
698 .find("// Check signature is non-empty.")
699 .expect("signature check marker exists");
700 let measurement_check = &source[start..start + end];
701
702 assert!(
703 !measurement_check.contains(".contains(&attestation.measurement_hash)"),
704 "measurement hash matching must not use short-circuiting Vec::contains"
705 );
706 }
707
708 #[test]
711 fn simulated_rejected_in_production() {
712 let att = generate_attestation(&TeePlatform::Simulated, MEASUREMENT, TIMESTAMP);
713 let mut policy = TeePolicy::production();
714 policy.accepted_platforms.push(TeePlatform::Simulated);
717 policy.current_time_ms = TIMESTAMP;
718 assert!(verify_attestation(&att, &policy).is_err());
719 }
720
721 #[test]
722 fn simulated_accepted_in_testing() {
723 let att = generate_attestation(&TeePlatform::Simulated, MEASUREMENT, TIMESTAMP);
724 let mut policy = TeePolicy::testing();
725 policy.current_time_ms = TIMESTAMP;
726 assert!(verify_attestation(&att, &policy).is_ok());
727 }
728
729 #[test]
730 fn synthetic_sgx_attestation_rejected_in_production() {
731 let att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
732 let mut policy = TeePolicy::production();
733 policy.current_time_ms = TIMESTAMP;
734 let result = verify_attestation(&att, &policy);
735 assert!(result.is_err());
736 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
737 }
738
739 #[test]
740 fn synthetic_trustzone_attestation_rejected_in_production() {
741 let att = generate_attestation(&TeePlatform::TrustZone, MEASUREMENT, TIMESTAMP);
742 let mut policy = TeePolicy::production();
743 policy.current_time_ms = TIMESTAMP;
744 let result = verify_attestation(&att, &policy);
745 assert!(result.is_err());
746 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
747 }
748
749 #[test]
750 fn synthetic_sev_attestation_rejected_in_production() {
751 let att = generate_attestation(&TeePlatform::Sev, MEASUREMENT, TIMESTAMP);
752 let mut policy = TeePolicy::production();
753 policy.current_time_ms = TIMESTAMP;
754 let result = verify_attestation(&att, &policy);
755 assert!(result.is_err());
756 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
757 }
758
759 #[test]
760 fn synthetic_hardware_attestation_rejected_in_testing() {
761 let att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
762 let mut policy = TeePolicy::testing();
763 policy.current_time_ms = TIMESTAMP;
764 let result = verify_attestation(&att, &policy);
765 assert!(result.is_err());
766 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
767 }
768
769 #[test]
770 fn sgx_non_synthetic_signature_rejected_without_quote_verifier_in_production() {
771 let mut att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
772 att.signature = vec![0xA5; 64];
773 let mut policy = TeePolicy::production();
774 policy.current_time_ms = TIMESTAMP;
775 let result = verify_attestation(&att, &policy);
776 assert!(result.is_err());
777 assert!(format!("{}", result.unwrap_err()).contains("quote verifier"));
778 }
779
780 #[test]
781 fn hardware_non_synthetic_signature_is_accepted_when_quote_verifier_accepts() {
782 let mut att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
783 att.signature = vec![0xA5; 64];
784 let mut policy = TeePolicy::production();
785 policy.current_time_ms = TIMESTAMP;
786
787 let result =
788 verify_attestation_with_quote_verifier(&att, &policy, &accepting_sgx_quote_verifier);
789
790 assert!(result.is_ok());
791 }
792
793 #[test]
794 fn hardware_quote_verifier_error_is_propagated() {
795 let mut att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
796 att.signature = vec![0xA5; 64];
797 let mut policy = TeePolicy::production();
798 policy.current_time_ms = TIMESTAMP;
799
800 let result = verify_attestation_with_quote_verifier(&att, &policy, &revoked_quote_verifier);
801
802 assert!(result.is_err());
803 assert!(format!("{}", result.unwrap_err()).contains("quote revoked"));
804 }
805
806 #[test]
807 fn synthetic_hardware_attestation_is_rejected_before_quote_verifier() {
808 let att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
809 let mut policy = TeePolicy::production();
810 policy.current_time_ms = TIMESTAMP;
811
812 let result = verify_attestation_with_quote_verifier(&att, &policy, &panic_quote_verifier);
813
814 assert!(result.is_err());
815 assert!(format!("{}", result.unwrap_err()).contains("synthetic"));
816 }
817
818 #[test]
819 fn sgx_non_synthetic_signature_rejected_without_quote_verifier_in_testing() {
820 let mut att = generate_attestation(&TeePlatform::Sgx, MEASUREMENT, TIMESTAMP);
821 att.signature = vec![0xA5; 64];
822 let mut policy = TeePolicy::testing();
823 policy.current_time_ms = TIMESTAMP;
824 let result = verify_attestation(&att, &policy);
825 assert!(result.is_err());
826 assert!(format!("{}", result.unwrap_err()).contains("quote verifier"));
827 }
828
829 #[test]
830 fn trustzone_non_synthetic_signature_rejected_without_quote_verifier() {
831 let mut att = generate_attestation(&TeePlatform::TrustZone, MEASUREMENT, TIMESTAMP);
832 att.signature = vec![0xA5; 64];
833 let mut policy = TeePolicy::production();
834 policy.current_time_ms = TIMESTAMP;
835 let result = verify_attestation(&att, &policy);
836 assert!(result.is_err());
837 assert!(format!("{}", result.unwrap_err()).contains("quote verifier"));
838 }
839
840 #[test]
841 fn sev_non_synthetic_signature_rejected_without_quote_verifier() {
842 let mut att = generate_attestation(&TeePlatform::Sev, MEASUREMENT, TIMESTAMP);
843 att.signature = vec![0xA5; 64];
844 let mut policy = TeePolicy::production();
845 policy.current_time_ms = TIMESTAMP;
846 let result = verify_attestation(&att, &policy);
847 assert!(result.is_err());
848 assert!(format!("{}", result.unwrap_err()).contains("quote verifier"));
849 }
850
851 #[test]
852 fn default_policy_is_production() {
853 let policy = TeePolicy::default();
854 assert_eq!(policy.environment, TeeEnvironment::Production);
855 }
856
857 #[test]
858 fn production_constructor_excludes_simulated() {
859 let policy = TeePolicy::production();
860 assert!(!policy.accepted_platforms.contains(&TeePlatform::Simulated));
861 }
862
863 #[test]
864 fn testing_constructor_includes_simulated() {
865 let policy = TeePolicy::testing();
866 assert!(policy.accepted_platforms.contains(&TeePlatform::Simulated));
867 }
868
869 #[test]
870 fn synthetic_attestation_signature_uses_stable_platform_tags() {
871 let source = include_str!("tee.rs");
872 let production = source
873 .split("// ===========================================================================\n// Tests")
874 .next()
875 .expect("tests marker present");
876
877 assert!(
878 !production.contains("format!(\"{:?}\", platform)"),
879 "synthetic attestation fixture signatures must not depend on Rust Debug output"
880 );
881 }
882}