1use std::collections::BTreeMap;
2
3use aws_lc_rs::signature::{ECDSA_P384_SHA384_FIXED, UnparsedPublicKey};
4use aws_nitro_enclaves_cose::{
5 CoseSign1,
6 crypto::{Hash, MessageDigest, SignatureAlgorithm, SigningPublicKey},
7 error::CoseError,
8};
9use serde_cbor::{self, value::Value};
10use sha2::{Digest, Sha256};
11use thiserror::Error;
12use x509_parser::{
13 oid_registry::{OID_KEY_TYPE_EC_PUBLIC_KEY, OID_SIG_ECDSA_WITH_SHA384},
14 prelude::{FromDer, TbsCertificate, X509Certificate},
15 time::ASN1Time,
16};
17
18pub const AWS_ROOT_KEY: [u8; 96] = hex_literal::hex!(
19 "fc0254eba608c1f36870e29ada90be46383292736e894bfff672d989444b5051e534a4b1f6dbe3c0bc581a32b7b176070ede12d69a3fea211b66e752cf7dd1dd095f6f1370f4170843d9dc100121e4cf63012809664487c9796284304dc53ff4"
20);
21pub const MOCK_ROOT_KEY: [u8; 96] = hex_literal::hex!(
22 "6c79411ebaae7489a4e8355545c0346784b31df5d08cb1f7c0097836a82f67240f2a7201862880a1d09a0bb326637188fbbafab47a10abe3630fcf8c18d35d96532184985e582c0dce3dace8441f37b9cc9211dff935baae69e4872cc3494410"
23);
24
25#[derive(Debug)]
26pub struct AttestationDecoded {
27 pub root_public_key: Box<[u8]>,
28 pub image_id: [u8; 32],
29 pub pcrs: [[u8; 48]; 12],
30 pub timestamp_ms: u64,
31 pub public_key: Box<[u8]>,
32 pub user_data: Box<[u8]>,
33}
34
35#[derive(Error, Debug)]
36pub enum AttestationError {
37 #[error("failed to parse cose")]
39 InvalidCose(#[source] CoseError),
40 #[error("failed to parse cbor")]
41 InvalidCbor(#[source] serde_cbor::Error),
42 #[error("x509 error in {context}: {error}")]
43 X509 {
44 context: String,
45 #[source]
46 error: x509_parser::nom::Err<x509_parser::error::X509Error>,
47 },
48 #[error("missing field: {0}")]
49 MissingField(String),
50 #[error("field {0} has invalid type")]
51 InvalidType(String),
52 #[error("field {0} has invalid length: {1}")]
53 InvalidLength(String, String),
54 #[error("timestamp conversion error: {0}")]
55 TimestampConversion(#[from] std::num::TryFromIntError),
56 #[error("cose signature verification failed")]
58 CoseSignatureVerifyFailed(#[source] CoseError),
59 #[error("leaf signature verification failed")]
60 LeafSignatureVerifyFailed,
61 #[error("certificate chain signature verification failed at index {index}")]
62 CertChainSignatureFailed { index: usize },
63 #[error("certificate chain issuer or subject mismatch at index {index}")]
64 CertChainIssuerOrSubjectMismatch { index: usize },
65 #[error("certificate chain expired at index {index}")]
66 CertChainExpired { index: usize },
67 #[error("timestamp mismatch: expected {expected}, got {got}")]
69 TimestampMismatch { expected: u64, got: u64 },
70 #[error("too old: expected age {age}, got {got}, now {now}")]
71 TooOld { age: u64, got: u64, now: u64 },
72 #[error("pcr{idx} mismatch: expected {expected:?}, got {got:?}")]
73 PcrsMismatch {
74 idx: usize,
75 expected: [u8; 48],
76 got: [u8; 48],
77 },
78 #[error("image id mismatch: expected {expected}, got {got}")]
79 ImageIdMismatch { expected: String, got: String },
80 #[error("root public key mismatch: expected {expected}, got {got}")]
81 RootPublicKeyMismatch { expected: String, got: String },
82 #[error("public key mismatch: expected {expected}, got {got}")]
83 PublicKeyMismatch { expected: String, got: String },
84 #[error("user data mismatch: expected {expected}, got {got}")]
85 UserDataMismatch { expected: String, got: String },
86}
87
88#[derive(Debug, Default, Clone)]
89pub struct AttestationExpectations<'a> {
90 pub root_public_key: Option<&'a [u8]>,
91 pub pcrs: [Option<[u8; 48]>; 12],
92 pub image_id: Option<&'a [u8; 32]>,
93 pub timestamp_ms: Option<u64>,
94 pub age_ms: Option<(u64, u64)>,
96 pub public_key: Option<&'a [u8]>,
97 pub user_data: Option<&'a [u8]>,
98}
99
100pub fn verify(
101 attestation_doc: &[u8],
102 expectations: AttestationExpectations,
103) -> Result<AttestationDecoded, AttestationError> {
104 let mut result = AttestationDecoded {
105 root_public_key: Default::default(),
106 image_id: Default::default(),
107 pcrs: [[0; 48]; 12],
108 timestamp_ms: 0,
109 public_key: Default::default(),
110 user_data: Default::default(),
111 };
112
113 let (cosesign1, mut attestation_doc) = parse_attestation_doc(attestation_doc)?;
115
116 result.timestamp_ms = parse_timestamp(&mut attestation_doc)?;
118
119 if let Some(expected_ts) = expectations.timestamp_ms
121 && result.timestamp_ms != expected_ts
122 {
123 return Err(AttestationError::TimestampMismatch {
124 expected: expected_ts,
125 got: result.timestamp_ms,
126 });
127 }
128
129 if let Some((max_age, current_ts)) = expectations.age_ms
131 && result.timestamp_ms <= current_ts
132 && current_ts - result.timestamp_ms > max_age
133 {
134 return Err(AttestationError::TooOld {
135 age: max_age,
136 got: result.timestamp_ms,
137 now: current_ts,
138 });
139 }
140
141 result.pcrs = parse_pcrs(&mut attestation_doc)?;
143
144 if let Some((idx, _)) = expectations
146 .pcrs
147 .iter()
148 .enumerate()
150 .filter_map(|(idx, &expected_pcr)| Some((idx, expected_pcr? == result.pcrs[idx])))
153 .find(|&(_, res)| !res)
155 {
156 return Err(AttestationError::PcrsMismatch {
157 idx,
158 expected: expectations.pcrs[idx].unwrap_or([0; 48]),
159 got: result.pcrs[idx],
160 });
161 };
162
163 let mut hasher = Sha256::new();
165 hasher.update((4..=15).fold(0u32, |acc, x| acc | (1 << x)).to_be_bytes());
168 hasher.update(result.pcrs.as_flattened());
169 result.image_id = hasher.finalize().into();
170
171 if let Some(image_id) = expectations.image_id
173 && &result.image_id != image_id
174 {
175 return Err(AttestationError::ImageIdMismatch {
176 expected: hex::encode(image_id),
177 got: hex::encode(result.image_id),
178 });
179 }
180
181 result.root_public_key =
183 verify_root_of_trust(&mut attestation_doc, &cosesign1, result.timestamp_ms)?;
184
185 if let Some(root_public_key) = expectations.root_public_key
187 && result.root_public_key.as_ref() != root_public_key
188 {
189 return Err(AttestationError::RootPublicKeyMismatch {
190 expected: hex::encode(root_public_key),
191 got: hex::encode(&result.root_public_key),
192 });
193 }
194
195 result.public_key = parse_enclave_key(&mut attestation_doc)?;
197
198 if let Some(public_key) = expectations.public_key
200 && result.public_key.as_ref() != public_key
201 {
202 return Err(AttestationError::PublicKeyMismatch {
203 expected: hex::encode(public_key),
204 got: hex::encode(&result.public_key),
205 });
206 }
207
208 result.user_data = parse_user_data(&mut attestation_doc)?;
210
211 if let Some(user_data) = expectations.user_data
213 && result.user_data.as_ref() != user_data
214 {
215 return Err(AttestationError::UserDataMismatch {
216 expected: hex::encode(user_data),
217 got: hex::encode(&result.user_data),
218 });
219 }
220
221 Ok(result)
222}
223
224fn parse_attestation_doc(
225 attestation_doc: &[u8],
226) -> Result<(CoseSign1, BTreeMap<Value, Value>), AttestationError> {
227 let cosesign1 =
228 CoseSign1::from_bytes(attestation_doc).map_err(AttestationError::InvalidCose)?;
229 let payload = cosesign1
231 .get_payload::<CertHasher>(None)
232 .expect("cannot fail");
233 let cbor = serde_cbor::from_slice::<BTreeMap<Value, Value>>(&payload)
234 .map_err(AttestationError::InvalidCbor)?;
235
236 Ok((cosesign1, cbor))
237}
238
239fn parse_timestamp(attestation_doc: &mut BTreeMap<Value, Value>) -> Result<u64, AttestationError> {
240 let timestamp = attestation_doc
241 .remove(&"timestamp".to_owned().into())
242 .ok_or(AttestationError::MissingField("timestamp".into()))?;
243 let timestamp = (match timestamp {
244 Value::Integer(b) => Ok(b),
245 _ => Err(AttestationError::InvalidType("timestamp".into())),
246 })?;
247 let timestamp = timestamp.try_into()?;
248
249 Ok(timestamp)
250}
251
252fn parse_pcrs(
253 attestation_doc: &mut BTreeMap<Value, Value>,
254) -> Result<[[u8; 48]; 12], AttestationError> {
255 let pcrs_arr = attestation_doc
256 .remove(&"nitrotpm_pcrs".to_owned().into())
257 .ok_or(AttestationError::MissingField("nitrotpm_pcrs".into()))?;
258 let mut pcrs_arr = (match pcrs_arr {
259 Value::Map(b) => Ok(b),
260 _ => Err(AttestationError::InvalidType("nitrotpm_pcrs".to_string())),
261 })?;
262
263 let mut result = [[0; 48]; 12];
264 for (i, result_pcr) in result.iter_mut().take(12).enumerate() {
265 let i = i + 4;
266 let pcr = pcrs_arr
267 .remove(&(i as u32).into())
268 .ok_or(AttestationError::MissingField(format!("pcr{i}")))?;
269 let pcr = (match pcr {
270 Value::Bytes(b) => Ok(b),
271 _ => Err(AttestationError::InvalidType(format!("pcr{i}"))),
272 })?;
273 *result_pcr = pcr
274 .as_slice()
275 .try_into()
276 .map_err(|e| AttestationError::InvalidLength(format!("pcr{i}"), format!("{e}")))?;
277 }
278
279 Ok(result)
280}
281
282fn verify_root_of_trust(
283 attestation_doc: &mut BTreeMap<Value, Value>,
284 cosesign1: &CoseSign1,
285 timestamp: u64,
286) -> Result<Box<[u8]>, AttestationError> {
287 let enclave_certificate_bytes = attestation_doc
289 .remove(&"certificate".to_owned().into())
290 .ok_or(AttestationError::MissingField("certificate".into()))?;
291 let enclave_certificate_bytes = (match enclave_certificate_bytes {
292 Value::Bytes(b) => Ok(b),
293 _ => Err(AttestationError::InvalidType("enclave certificate".into())),
294 })?;
295 let (_, cert) = X509Certificate::from_der(&enclave_certificate_bytes).map_err(|e| {
296 AttestationError::X509 {
297 context: "leaf".into(),
298 error: e,
299 }
300 })?;
301
302 let verifier_cert = CertWrapper(&cert.tbs_certificate);
304
305 let verify_result = cosesign1
306 .verify_signature::<CertHasher>(&verifier_cert)
307 .map_err(AttestationError::CoseSignatureVerifyFailed)?;
308
309 if !verify_result {
310 return Err(AttestationError::LeafSignatureVerifyFailed);
311 }
312
313 let cabundle = attestation_doc
315 .remove(&"cabundle".to_owned().into())
316 .ok_or(AttestationError::MissingField("cabundle".into()))?;
317 let mut cabundle = (match cabundle {
318 Value::Array(b) => Ok(b),
319 _ => Err(AttestationError::InvalidType("cabundle".into())),
320 })?;
321 cabundle.reverse();
322
323 let root_public_key = verify_cert_chain(cert, &cabundle, timestamp)?;
324
325 Ok(root_public_key)
326}
327
328fn verify_cert_chain(
329 cert: X509Certificate,
330 cabundle: &[Value],
331 timestamp: u64,
332) -> Result<Box<[u8]>, AttestationError> {
333 let mut certs = Vec::with_capacity(cabundle.len() + 1);
334 certs.push(cert);
335
336 for (i, cert_val) in cabundle.iter().enumerate() {
337 let cert_der = (match cert_val {
338 Value::Bytes(b) => Ok(b),
339 _ => Err(AttestationError::InvalidType("cert decode".into())),
340 })?;
341 let (_, cert) =
342 X509Certificate::from_der(cert_der).map_err(|e| AttestationError::X509 {
343 context: format!("bundle {}", i),
344 error: e,
345 })?;
346 certs.push(cert);
347 }
348
349 for i in 0..(certs.len() - 1) {
350 let issuer_spki = &certs[i + 1].tbs_certificate.subject_pki;
351
352 certs[i]
354 .verify_signature(Some(issuer_spki))
355 .map_err(|_| AttestationError::CertChainSignatureFailed { index: i })?;
356
357 if certs[i + 1].tbs_certificate.subject != certs[i].tbs_certificate.issuer {
358 return Err(AttestationError::CertChainIssuerOrSubjectMismatch { index: i });
359 }
360
361 let current_time = ASN1Time::from_timestamp((timestamp / 1000) as i64).map_err(|e| {
362 AttestationError::X509 {
363 context: format!("timestamp {}", i),
364 error: e.into(),
365 }
366 })?;
367
368 if certs[i].tbs_certificate.validity.not_after < current_time
369 || certs[i].tbs_certificate.validity.not_before > current_time
370 {
371 return Err(AttestationError::CertChainExpired { index: i });
372 }
373 }
374
375 let root_public_key = certs
376 .last()
377 .ok_or(AttestationError::MissingField("root".into()))?
378 .tbs_certificate
379 .subject_pki
380 .subject_public_key
381 .data[1..]
382 .to_vec()
383 .into_boxed_slice();
384
385 Ok(root_public_key)
386}
387
388fn parse_enclave_key(
389 attestation_doc: &mut BTreeMap<Value, Value>,
390) -> Result<Box<[u8]>, AttestationError> {
391 let public_key = attestation_doc
392 .remove(&"public_key".to_owned().into())
393 .ok_or(AttestationError::MissingField("public_key".into()))?;
394 let public_key = (match public_key {
395 Value::Bytes(b) => Ok(b),
396 _ => Err(AttestationError::InvalidType("public_key".into())),
397 })?;
398
399 Ok(public_key.into_boxed_slice())
400}
401
402fn parse_user_data(
403 attestation_doc: &mut BTreeMap<Value, Value>,
404) -> Result<Box<[u8]>, AttestationError> {
405 let user_data = attestation_doc
406 .remove(&"user_data".to_owned().into())
407 .ok_or(AttestationError::MissingField("user_data".into()))?;
408 let user_data = (match user_data {
409 Value::Bytes(b) => Ok(b),
410 Value::Null => Ok(vec![]),
411 _ => Err(AttestationError::InvalidType("user_data".into())),
412 })?;
413
414 Ok(user_data.into_boxed_slice())
415}
416
417pub struct CertHasher;
418
419impl Hash for CertHasher {
420 fn hash(_algorithm: MessageDigest, data: &[u8]) -> Result<Vec<u8>, CoseError> {
421 Ok(data.into())
429 }
430}
431
432struct CertWrapper<'a>(&'a TbsCertificate<'a>);
433
434impl<'a> SigningPublicKey for CertWrapper<'a> {
435 fn get_parameters(&self) -> Result<(SignatureAlgorithm, MessageDigest), CoseError> {
436 if self.0.subject_pki.algorithm.algorithm != OID_KEY_TYPE_EC_PUBLIC_KEY {
437 return Err(CoseError::UnsupportedError("Unsupported key type".into()));
438 }
439 match self.0.subject_pki.subject_public_key.data.len() {
440 65 => Ok((SignatureAlgorithm::ES256, MessageDigest::Sha256)),
441 97 => Ok((SignatureAlgorithm::ES384, MessageDigest::Sha384)),
442 129 => Ok((SignatureAlgorithm::ES512, MessageDigest::Sha512)),
443 _ => Err(CoseError::UnsupportedError("Unsupported key type".into())),
444 }
445 }
446
447 fn verify(&self, digest: &[u8], signature: &[u8]) -> Result<bool, CoseError> {
448 if self.0.signature.algorithm != OID_SIG_ECDSA_WITH_SHA384 {
449 return Err(CoseError::UnsupportedError(
450 "Unsupported signature type".into(),
451 ));
452 }
453 let pubkey = UnparsedPublicKey::new(
454 &ECDSA_P384_SHA384_FIXED,
455 &self.0.subject_pki.subject_public_key.data,
456 );
457 pubkey
458 .verify(digest, signature)
459 .map_err(|_| CoseError::UnverifiedSignature)
460 .map(|_| true)
461 }
462}
463
464#[cfg(test)]
465mod tests {
466 use hex_literal::hex;
467
468 use crate::attestation::{AWS_ROOT_KEY, AttestationExpectations, MOCK_ROOT_KEY};
469
470 use super::verify;
471
472 #[test]
475 fn test_aws_none_specified() {
476 let attestation =
477 std::fs::read(file!().rsplit_once('/').unwrap().0.to_owned() + "/testcases/aws.bin")
478 .unwrap();
479
480 let decoded = verify(&attestation, Default::default()).unwrap();
481
482 assert_eq!(decoded.timestamp_ms, 0x0000019ba6c8dab7);
483 assert_eq!(
484 decoded.pcrs[0],
485 hex!(
486 "6e7fd58cca2dcd0b6ca3186c260e47f33d33e4c63b9819609d5b75efb55453529fed4098a7383bfca18d3ca869ff202f"
487 )
488 );
489 assert_eq!(
490 decoded.pcrs[1],
491 hex!(
492 "c10ef05cbff856c3b0b83793118e887985b0ab263162db25badf0affcb01746494a984db2ba517608c2eade447d2dbe9"
493 )
494 );
495 assert_eq!(
496 decoded.pcrs[2],
497 hex!(
498 "518923b0f955d08da077c96aaba522b9decede61c599cea6c41889cfbea4ae4d50529d96fe4d1afdafb65e7f95bf23c4"
499 )
500 );
501 assert_eq!(
502 decoded.pcrs[3],
503 hex!(
504 "98441c7f7625d10058c47683aec486ce311c633235eb555593a7ee791121e3578ae72d04ecef661f272d59058b77af35"
505 )
506 );
507 assert_eq!(
508 decoded.pcrs[4],
509 hex!(
510 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
511 )
512 );
513 assert_eq!(
514 decoded.pcrs[5],
515 hex!(
516 "39c33c06e6a163f8f04f23b43d1b342677abfcd3bebbd3777bf0429fadb6a7ef5e2e533c0bf7d6dc9aec44370b29d5f4"
517 )
518 );
519 assert_eq!(
520 decoded.pcrs[6],
521 hex!(
522 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
523 )
524 );
525 assert_eq!(
526 decoded.pcrs[7],
527 hex!(
528 "6dac65857819c581c671ab7edafac3cdf85700c880392d98127af1c94e305f96baa73564f721bf0cee3b190d90c2750f"
529 )
530 );
531 assert_eq!(
532 decoded.pcrs[8],
533 hex!(
534 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
535 )
536 );
537 assert_eq!(
538 decoded.pcrs[9],
539 hex!(
540 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
541 )
542 );
543 assert_eq!(
544 decoded.pcrs[10],
545 hex!(
546 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
547 )
548 );
549 assert_eq!(
550 decoded.pcrs[11],
551 hex!(
552 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
553 )
554 );
555 assert_eq!(decoded.user_data.as_ref(), hex!("1234"));
556 assert_eq!(decoded.public_key.as_ref(), hex!("abcd"));
557 assert_eq!(decoded.root_public_key.as_ref(), AWS_ROOT_KEY);
558 assert_eq!(
559 decoded.image_id,
560 hex!("b3dc7d48651dec3f088737ce37e0806a28e516e0caa01d54cf9438176a1c4d00")
561 );
562 }
563
564 #[test]
567 fn test_aws_all_specified() {
568 let attestation =
569 std::fs::read(file!().rsplit_once('/').unwrap().0.to_owned() + "/testcases/aws.bin")
570 .unwrap();
571
572 let decoded = verify(
573 &attestation,
574 AttestationExpectations {
575 timestamp_ms: Some(0x0000019ba6c8dab7),
576 age_ms: Some((
577 300000,
578 0x0000019ba6c8dab7 + 300000,
579 )),
580 pcrs: [
581 Some(hex!( "6e7fd58cca2dcd0b6ca3186c260e47f33d33e4c63b9819609d5b75efb55453529fed4098a7383bfca18d3ca869ff202f")),
582 Some(hex!( "c10ef05cbff856c3b0b83793118e887985b0ab263162db25badf0affcb01746494a984db2ba517608c2eade447d2dbe9")),
583 Some(hex!("518923b0f955d08da077c96aaba522b9decede61c599cea6c41889cfbea4ae4d50529d96fe4d1afdafb65e7f95bf23c4")),
584 Some(hex!("98441c7f7625d10058c47683aec486ce311c633235eb555593a7ee791121e3578ae72d04ecef661f272d59058b77af35")),
585 Some(hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
586 Some(hex!("39c33c06e6a163f8f04f23b43d1b342677abfcd3bebbd3777bf0429fadb6a7ef5e2e533c0bf7d6dc9aec44370b29d5f4")),
587 Some(hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
588 Some(hex!("6dac65857819c581c671ab7edafac3cdf85700c880392d98127af1c94e305f96baa73564f721bf0cee3b190d90c2750f")),
589 Some(hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
590 Some(hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
591 Some(hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")),
592 Some(hex!("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))
593 ],
594 public_key: Some(&hex!("abcd")),
595 user_data: Some(&hex!("1234")),
596 root_public_key: Some(&AWS_ROOT_KEY),
597 image_id: Some(&hex!("b3dc7d48651dec3f088737ce37e0806a28e516e0caa01d54cf9438176a1c4d00")),
598 },
599 )
600 .unwrap();
601
602 assert_eq!(decoded.timestamp_ms, 0x0000019ba6c8dab7);
603 assert_eq!(
604 decoded.pcrs[0],
605 hex!(
606 "6e7fd58cca2dcd0b6ca3186c260e47f33d33e4c63b9819609d5b75efb55453529fed4098a7383bfca18d3ca869ff202f"
607 )
608 );
609 assert_eq!(
610 decoded.pcrs[1],
611 hex!(
612 "c10ef05cbff856c3b0b83793118e887985b0ab263162db25badf0affcb01746494a984db2ba517608c2eade447d2dbe9"
613 )
614 );
615 assert_eq!(
616 decoded.pcrs[2],
617 hex!(
618 "518923b0f955d08da077c96aaba522b9decede61c599cea6c41889cfbea4ae4d50529d96fe4d1afdafb65e7f95bf23c4"
619 )
620 );
621 assert_eq!(
622 decoded.pcrs[3],
623 hex!(
624 "98441c7f7625d10058c47683aec486ce311c633235eb555593a7ee791121e3578ae72d04ecef661f272d59058b77af35"
625 )
626 );
627 assert_eq!(
628 decoded.pcrs[4],
629 hex!(
630 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
631 )
632 );
633 assert_eq!(
634 decoded.pcrs[5],
635 hex!(
636 "39c33c06e6a163f8f04f23b43d1b342677abfcd3bebbd3777bf0429fadb6a7ef5e2e533c0bf7d6dc9aec44370b29d5f4"
637 )
638 );
639 assert_eq!(
640 decoded.pcrs[6],
641 hex!(
642 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
643 )
644 );
645 assert_eq!(
646 decoded.pcrs[7],
647 hex!(
648 "6dac65857819c581c671ab7edafac3cdf85700c880392d98127af1c94e305f96baa73564f721bf0cee3b190d90c2750f"
649 )
650 );
651 assert_eq!(
652 decoded.pcrs[8],
653 hex!(
654 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
655 )
656 );
657 assert_eq!(
658 decoded.pcrs[9],
659 hex!(
660 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
661 )
662 );
663 assert_eq!(
664 decoded.pcrs[10],
665 hex!(
666 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
667 )
668 );
669 assert_eq!(
670 decoded.pcrs[11],
671 hex!(
672 "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
673 )
674 );
675 assert_eq!(decoded.user_data.as_ref(), hex!("1234"));
676 assert_eq!(decoded.public_key.as_ref(), hex!("abcd"));
677 assert_eq!(decoded.root_public_key.as_ref(), AWS_ROOT_KEY);
678 assert_eq!(
679 decoded.image_id,
680 hex!("b3dc7d48651dec3f088737ce37e0806a28e516e0caa01d54cf9438176a1c4d00")
681 );
682 }
683
684 #[test]
687 fn test_mock_none_specified() {
688 let attestation =
689 std::fs::read(file!().rsplit_once('/').unwrap().0.to_owned() + "/testcases/mock.bin")
690 .unwrap();
691
692 let decoded = verify(&attestation, Default::default()).unwrap();
693
694 assert_eq!(decoded.timestamp_ms, 0x0000019ba7060dce);
695 assert_eq!(decoded.pcrs[0], [4; 48]);
696 assert_eq!(decoded.pcrs[1], [5; 48]);
697 assert_eq!(decoded.pcrs[2], [6; 48]);
698 assert_eq!(decoded.pcrs[3], [7; 48]);
699 assert_eq!(decoded.pcrs[4], [8; 48]);
700 assert_eq!(decoded.pcrs[5], [9; 48]);
701 assert_eq!(decoded.pcrs[6], [10; 48]);
702 assert_eq!(decoded.pcrs[7], [11; 48]);
703 assert_eq!(decoded.pcrs[8], [12; 48]);
704 assert_eq!(decoded.pcrs[9], [13; 48]);
705 assert_eq!(decoded.pcrs[10], [14; 48]);
706 assert_eq!(decoded.pcrs[11], [15; 48]);
707 assert_eq!(decoded.user_data.as_ref(), hex!("1234"));
708 assert_eq!(decoded.public_key.as_ref(), hex!("abcd"));
709 assert_eq!(decoded.root_public_key.as_ref(), MOCK_ROOT_KEY);
710 assert_eq!(
711 decoded.image_id,
712 hex!("e3caee4ad768d705b977a3687c74b25ff89e4dbd71091e16e04b3f9f867c926b")
713 );
714 }
715
716 #[test]
719 fn test_mock_all_specified() {
720 let attestation =
721 std::fs::read(file!().rsplit_once('/').unwrap().0.to_owned() + "/testcases/mock.bin")
722 .unwrap();
723
724 let decoded = verify(
725 &attestation,
726 AttestationExpectations {
727 timestamp_ms: Some(0x0000019ba7060dce),
728 age_ms: Some((300000, 0x0000019ba7060dce + 300000)),
729 pcrs: [
730 Some([4; 48]),
731 Some([5; 48]),
732 Some([6; 48]),
733 Some([7; 48]),
734 Some([8; 48]),
735 Some([9; 48]),
736 Some([10; 48]),
737 Some([11; 48]),
738 Some([12; 48]),
739 Some([13; 48]),
740 Some([14; 48]),
741 Some([15; 48]),
742 ],
743 public_key: Some(&hex!("abcd")),
744 user_data: Some(&hex!("1234")),
745 root_public_key: Some(&MOCK_ROOT_KEY),
746 image_id: Some(&hex!(
747 "e3caee4ad768d705b977a3687c74b25ff89e4dbd71091e16e04b3f9f867c926b"
748 )),
749 },
750 )
751 .unwrap();
752
753 assert_eq!(decoded.timestamp_ms, 0x0000019ba7060dce);
754 assert_eq!(decoded.pcrs[0], [4; 48]);
755 assert_eq!(decoded.pcrs[1], [5; 48]);
756 assert_eq!(decoded.pcrs[2], [6; 48]);
757 assert_eq!(decoded.pcrs[3], [7; 48]);
758 assert_eq!(decoded.pcrs[4], [8; 48]);
759 assert_eq!(decoded.pcrs[5], [9; 48]);
760 assert_eq!(decoded.pcrs[6], [10; 48]);
761 assert_eq!(decoded.pcrs[7], [11; 48]);
762 assert_eq!(decoded.pcrs[8], [12; 48]);
763 assert_eq!(decoded.pcrs[9], [13; 48]);
764 assert_eq!(decoded.pcrs[10], [14; 48]);
765 assert_eq!(decoded.pcrs[11], [15; 48]);
766 assert_eq!(decoded.user_data.as_ref(), hex!("1234"));
767 assert_eq!(decoded.public_key.as_ref(), hex!("abcd"));
768 assert_eq!(decoded.root_public_key.as_ref(), MOCK_ROOT_KEY);
769 assert_eq!(
770 decoded.image_id,
771 hex!("e3caee4ad768d705b977a3687c74b25ff89e4dbd71091e16e04b3f9f867c926b")
772 );
773 }
774}