1use std::ffi::CString;
2
3use byteorder::{ByteOrder, LittleEndian};
4use chrono::{prelude::*, Duration};
5use mbedtls::{alloc::Box as MbedtlsBox, x509::certificate::Certificate};
6use rustc_hex::FromHex;
7use serde_json::value::RawValue;
8use sgx_isa::Report;
9
10use super::{
11 certificates::PCS_TRUST_ROOT, constants::*, policy::QuotePolicy, quote::TeeType,
12 utils::x509_custom_ts_verify_cb, Error,
13};
14
15#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
17pub struct TCBBundle {
18 #[cbor(rename = "tcb_info")]
19 pub tcb_info: SignedTCBInfo,
20
21 #[cbor(rename = "qe_id")]
22 pub qe_identity: SignedQEIdentity,
23
24 #[cbor(rename = "certs")]
25 pub certificates: Vec<u8>,
26}
27
28impl TCBBundle {
29 pub(super) fn verify_certificates(
30 &self,
31 ts: DateTime<Utc>,
32 ) -> Result<MbedtlsBox<Certificate>, Error> {
33 let raw_certs =
34 CString::new(&*self.certificates).map_err(|err| Error::TCBParseError(err.into()))?;
35 let mut cert_chain = Certificate::from_pem_multiple(raw_certs.as_bytes_with_nul())
36 .map_err(|err| Error::TCBParseError(err.into()))?;
37 if cert_chain.iter().count() != 2 {
38 return Err(Error::UnexpectedCertificateChain);
39 }
40
41 Certificate::verify_with_callback(
42 &cert_chain,
43 &PCS_TRUST_ROOT,
44 None,
45 None,
46 x509_custom_ts_verify_cb(ts),
47 )
48 .map_err(|_| Error::TCBVerificationFailed)?;
49
50 Ok(cert_chain.pop_front().unwrap())
51 }
52}
53
54#[inline]
55fn encode_raw_value(value: &RawValue) -> Vec<u8> {
56 value.get().as_bytes().to_owned()
57}
58
59#[inline]
60fn decode_raw_value(value: Vec<u8>) -> Result<Box<RawValue>, cbor::DecodeError> {
61 RawValue::from_string(String::from_utf8(value).map_err(|_| cbor::DecodeError::UnexpectedType)?)
62 .map_err(|_| cbor::DecodeError::UnexpectedType)
63}
64
65#[derive(Clone, Debug, Default, serde::Deserialize, cbor::Encode, cbor::Decode)]
67pub struct SignedTCBInfo {
68 #[cbor(
69 rename = "tcb_info",
70 serialize_with = "encode_raw_value",
71 deserialize_with = "decode_raw_value"
72 )]
73 #[serde(rename = "tcbInfo")]
74 pub tcb_info: Box<RawValue>,
75
76 #[cbor(rename = "signature")]
77 #[serde(rename = "signature")]
78 pub signature: String,
79}
80
81impl PartialEq for SignedTCBInfo {
82 fn eq(&self, other: &SignedTCBInfo) -> bool {
83 self.tcb_info.get() == other.tcb_info.get() && self.signature == other.signature
84 }
85}
86
87impl Eq for SignedTCBInfo {}
88
89fn open_signed_tcb<'a, T: serde::Deserialize<'a>>(
90 data: &'a str,
91 signature: &str,
92 pk: &mut mbedtls::pk::Pk,
93) -> Result<T, Error> {
94 let mut hash = [0u8; 32];
95 mbedtls::hash::Md::hash(mbedtls::hash::Type::Sha256, data.as_bytes(), &mut hash)
96 .map_err(|_| Error::TCBVerificationFailed)?;
97 let sig: Vec<u8> = signature
98 .from_hex()
99 .map_err(|_| Error::TCBVerificationFailed)?;
100
101 if !sig.len().is_multiple_of(2) {
103 return Err(Error::TCBVerificationFailed);
104 }
105
106 let (r_bytes, s_bytes) = sig.split_at(sig.len() / 2);
107 let r = num_bigint::BigUint::from_bytes_be(r_bytes);
108 let s = num_bigint::BigUint::from_bytes_be(s_bytes);
109
110 let sig = yasna::construct_der(|writer| {
111 writer.write_sequence(|writer| {
112 writer.next().write_biguint(&r);
113 writer.next().write_biguint(&s);
114 })
115 });
116
117 pk.verify(mbedtls::hash::Type::Sha256, &hash, &sig)
118 .map_err(|_| Error::TCBVerificationFailed)?;
119
120 serde_json::from_str(data).map_err(|err| Error::TCBParseError(err.into()))
121}
122
123impl SignedTCBInfo {
124 pub fn open(
125 &self,
126 tee_type: TeeType,
127 ts: DateTime<Utc>,
128 policy: &QuotePolicy,
129 pk: &mut mbedtls::pk::Pk,
130 ) -> Result<TCBInfo, Error> {
131 let ti: TCBInfo = open_signed_tcb(self.tcb_info.get(), &self.signature, pk)?;
132 ti.validate(tee_type, ts, policy)?;
133
134 Ok(ti)
135 }
136}
137
138#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
140pub enum TCBInfoID {
141 #[serde(rename = "SGX")]
142 Sgx,
143 #[serde(rename = "TDX")]
144 Tdx,
145 #[serde(other)]
146 #[default]
147 Invalid,
148}
149
150#[derive(Clone, Debug, Default, serde::Deserialize)]
152pub struct TCBInfo {
153 #[serde(rename = "id")]
154 pub id: TCBInfoID,
155
156 #[serde(rename = "version")]
157 pub version: u32,
158
159 #[serde(rename = "issueDate")]
160 pub issue_date: String,
161
162 #[serde(rename = "nextUpdate")]
163 pub next_update: String,
164
165 #[serde(rename = "fmspc")]
166 pub fmspc: String,
167
168 #[serde(rename = "pceId")]
169 pub pceid: String,
170
171 #[serde(rename = "tcbType")]
172 pub tcb_type: u32,
173
174 #[serde(rename = "tcbEvaluationDataNumber")]
175 pub tcb_evaluation_data_number: u32,
176
177 #[serde(default, rename = "tdxModule")]
178 pub tdx_module: TDXModule,
179
180 #[serde(default, rename = "tdxModuleIdentities")]
181 pub tdx_module_identities: Vec<TDXModuleIdentity>,
182
183 #[serde(rename = "tcbLevels")]
184 pub tcb_levels: Vec<TCBLevel>,
185}
186
187impl TCBInfo {
188 pub fn validate(
190 &self,
191 tee_type: TeeType,
192 ts: DateTime<Utc>,
193 policy: &QuotePolicy,
194 ) -> Result<(), Error> {
195 match (self.id, tee_type) {
196 (TCBInfoID::Sgx, TeeType::Sgx) => {}
197 (TCBInfoID::Tdx, TeeType::Tdx) => {}
198 _ => {
199 return Err(Error::TCBParseError(anyhow::anyhow!(
200 "unexpected TCB info identifier"
201 )))
202 }
203 }
204
205 if self.version != REQUIRED_TCB_INFO_VERSION {
206 return Err(Error::TCBParseError(anyhow::anyhow!(
207 "unexpected TCB info version"
208 )));
209 }
210
211 let issue_date = NaiveDateTime::parse_from_str(&self.issue_date, PCS_TS_FMT)
213 .map_err(|err| Error::TCBParseError(err.into()))?
214 .and_utc();
215 let _next_update = NaiveDateTime::parse_from_str(&self.next_update, PCS_TS_FMT)
216 .map_err(|err| Error::TCBParseError(err.into()))?
217 .and_utc();
218 if issue_date > ts {
219 return Err(Error::TCBExpired);
220 }
221 if ts - issue_date
222 > Duration::try_days(policy.tcb_validity_period.into())
223 .unwrap_or(DEFAULT_TCB_VALIDITY_PERIOD)
224 {
225 return Err(Error::TCBExpired);
226 }
227
228 if self.tcb_evaluation_data_number < policy.min_tcb_evaluation_data_number {
229 return Err(Error::TCBEvaluationDataNumberInvalid);
230 }
231
232 let blocked = policy
234 .fmspc_blacklist
235 .iter()
236 .any(|blocked| blocked == &self.fmspc);
237 if blocked {
238 return Err(Error::BlacklistedFMSPC);
239 }
240
241 Ok(())
242 }
243
244 pub fn verify(
246 &self,
247 fmspc: &[u8],
248 sgx_comp_svn: &[u32; 16],
249 tdx_comp_svn: Option<&[u32; 16]>,
250 pcesvn: u32,
251 ) -> Result<TCBLevel, Error> {
252 let expected_fmspc: Vec<u8> = self
254 .fmspc
255 .from_hex()
256 .map_err(|err| Error::TCBParseError(err.into()))?;
257 if fmspc != expected_fmspc {
258 return Err(Error::TCBMismatch);
259 }
260
261 let level = self
263 .tcb_levels
264 .iter()
265 .find(|level| level.matches(sgx_comp_svn, tdx_comp_svn, pcesvn))
266 .ok_or(Error::TCBOutOfDate)?
267 .clone();
268
269 if self.id == TCBInfoID::Tdx {
270 let tdx_comp_svn = tdx_comp_svn.ok_or(Error::TCBMismatch)?;
273 let tdx_module_version = tdx_comp_svn[1];
274 if tdx_module_version >= 1 {
275 let tdx_module_id = format!("TDX_{tdx_module_version:02}");
280 let tdx_module = self
281 .tdx_module_identities
282 .iter()
283 .find(|tm| tm.id == tdx_module_id)
284 .ok_or(Error::TCBOutOfDate)?;
285
286 let tdx_module_level = tdx_module
292 .tcb_levels
293 .iter()
294 .find(|level| level.tcb.isv_svn as u32 <= tdx_comp_svn[0])
295 .ok_or(Error::TCBOutOfDate)?;
296 if tdx_module_level.status != TCBStatus::UpToDate {
297 return Err(Error::TCBOutOfDate);
298 }
299 }
300 }
301
302 Ok(level)
303 }
304}
305
306#[derive(Clone, Debug, Default, serde::Deserialize)]
308pub struct TDXModule {
309 #[serde(rename = "mrsigner")]
310 pub mr_signer: String,
311
312 #[serde(rename = "attributes")]
313 pub attributes: String,
314
315 #[serde(rename = "attributesMask")]
316 pub attributes_mask: String,
317}
318
319#[derive(Clone, Debug, Default, serde::Deserialize)]
322pub struct TDXModuleIdentity {
323 #[serde(rename = "id")]
324 pub id: String,
325
326 #[serde(flatten)]
327 pub module: TDXModule,
328
329 #[serde(rename = "tcbLevels")]
330 pub tcb_levels: Vec<EnclaveTCBLevel>,
331}
332
333#[derive(Clone, Debug, Default, serde::Deserialize)]
335pub struct TCBLevel {
336 #[serde(rename = "tcb")]
337 pub tcb: TCBVersions,
338
339 #[serde(rename = "tcbDate")]
340 pub date: String,
341
342 #[serde(rename = "tcbStatus")]
343 pub status: TCBStatus,
344
345 #[serde(default, rename = "advisoryIDs")]
346 pub advisory_ids: Vec<String>,
347}
348
349impl TCBLevel {
350 pub fn matches(
352 &self,
353 sgx_comp_svn: &[u32],
354 tdx_comp_svn: Option<&[u32; 16]>,
355 pcesvn: u32,
356 ) -> bool {
357 for (i, comp) in self.tcb.sgx_components.iter().enumerate() {
362 if sgx_comp_svn[i] < comp.svn {
364 return false;
365 }
366 }
367
368 if self.tcb.pcesvn < pcesvn {
373 return false;
374 }
375
376 if let Some(tdx_comp_svn) = tdx_comp_svn {
377 let comps = self.tcb.tdx_components.iter().enumerate();
384 let offset = if tdx_comp_svn[1] != 0 { 2 } else { 0 };
385
386 for (i, comp) in comps.skip(offset) {
387 if tdx_comp_svn[i] < comp.svn {
389 return false;
390 }
391 }
392 }
393
394 true
396 }
397}
398
399#[derive(Clone, Debug, Default, serde::Deserialize)]
401pub struct TCBVersions {
402 #[serde(rename = "pcesvn")]
403 pub pcesvn: u32,
404
405 #[serde(rename = "sgxtcbcomponents")]
406 pub sgx_components: [TCBComponent; 16],
407
408 #[serde(default, rename = "tdxtcbcomponents")]
409 pub tdx_components: [TCBComponent; 16],
410}
411
412#[derive(Clone, Debug, Default, serde::Deserialize)]
414pub struct TCBComponent {
415 #[serde(rename = "svn")]
416 pub svn: u32,
417
418 #[serde(default, rename = "category")]
419 pub category: String,
420
421 #[serde(default, rename = "type")]
422 pub tcb_comp_type: String,
423}
424
425#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
427pub enum TCBStatus {
428 UpToDate,
429 SWHardeningNeeded,
430 ConfigurationNeeded,
431 ConfigurationAndSWHardeningNeeded,
432 OutOfDate,
433 OutOfDateConfigurationNeeded,
434 Revoked,
435 #[serde(other)]
436 #[default]
437 Invalid,
438}
439
440#[derive(Clone, Debug, Default, serde::Deserialize, cbor::Encode, cbor::Decode)]
442pub struct SignedQEIdentity {
443 #[cbor(
444 rename = "enclave_identity",
445 serialize_with = "encode_raw_value",
446 deserialize_with = "decode_raw_value"
447 )]
448 #[serde(rename = "enclaveIdentity")]
449 pub enclave_identity: Box<RawValue>,
450
451 #[cbor(rename = "signature")]
452 #[serde(rename = "signature")]
453 pub signature: String,
454}
455
456impl PartialEq for SignedQEIdentity {
457 fn eq(&self, other: &SignedQEIdentity) -> bool {
458 self.enclave_identity.get() == other.enclave_identity.get()
459 && self.signature == other.signature
460 }
461}
462
463impl Eq for SignedQEIdentity {}
464
465impl SignedQEIdentity {
466 pub fn open(
467 &self,
468 tee_type: TeeType,
469 ts: DateTime<Utc>,
470 policy: &QuotePolicy,
471 pk: &mut mbedtls::pk::Pk,
472 ) -> Result<QEIdentity, Error> {
473 let qe: QEIdentity = open_signed_tcb(self.enclave_identity.get(), &self.signature, pk)?;
474 qe.validate(tee_type, ts, policy)?;
475
476 Ok(qe)
477 }
478}
479
480#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
482#[allow(non_camel_case_types)]
483pub enum QEIdentityID {
484 QE,
485 TD_QE,
486 #[serde(other)]
487 #[default]
488 Invalid,
489}
490
491#[derive(Clone, Debug, Default, serde::Deserialize)]
493pub struct QEIdentity {
494 #[serde(rename = "id")]
495 pub id: QEIdentityID,
496
497 #[serde(rename = "version")]
498 pub version: u32,
499
500 #[serde(rename = "issueDate")]
501 pub issue_date: String,
502
503 #[serde(rename = "nextUpdate")]
504 pub next_update: String,
505
506 #[serde(rename = "tcbEvaluationDataNumber")]
507 pub tcb_evaluation_data_number: u32,
508
509 #[serde(rename = "miscselect")]
510 pub miscselect: String,
511
512 #[serde(rename = "miscselectMask")]
513 pub miscselect_mask: String,
514
515 #[serde(rename = "attributes")]
516 pub attributes: String,
517
518 #[serde(rename = "attributesMask")]
519 pub attributes_mask: String,
520
521 #[serde(rename = "mrsigner")]
522 pub mr_signer: String,
523
524 #[serde(rename = "isvprodid")]
525 pub isv_prod_id: u16,
526
527 #[serde(rename = "tcbLevels")]
528 pub tcb_levels: Vec<EnclaveTCBLevel>,
529
530 #[serde(default, rename = "advisoryIDs")]
531 pub advisory_ids: Vec<String>,
532}
533
534impl QEIdentity {
535 pub fn validate(
537 &self,
538 tee_type: TeeType,
539 ts: DateTime<Utc>,
540 policy: &QuotePolicy,
541 ) -> Result<(), Error> {
542 match (self.id, tee_type) {
543 (QEIdentityID::QE, TeeType::Sgx) => {}
544 (QEIdentityID::TD_QE, TeeType::Tdx) => {}
545 _ => return Err(Error::TCBParseError(anyhow::anyhow!("unexpected QE ID"))),
546 }
547
548 if self.version != REQUIRED_QE_IDENTITY_VERSION {
549 return Err(Error::TCBParseError(anyhow::anyhow!(
550 "unexpected QE identity version"
551 )));
552 }
553
554 let issue_date = NaiveDateTime::parse_from_str(&self.issue_date, PCS_TS_FMT)
556 .map_err(|err| Error::TCBParseError(err.into()))?
557 .and_utc();
558 let _next_update = NaiveDateTime::parse_from_str(&self.next_update, PCS_TS_FMT)
559 .map_err(|err| Error::TCBParseError(err.into()))?
560 .and_utc();
561 if issue_date > ts {
562 return Err(Error::TCBExpired);
563 }
564 if ts - issue_date
565 > Duration::try_days(policy.tcb_validity_period.into())
566 .unwrap_or(DEFAULT_TCB_VALIDITY_PERIOD)
567 {
568 return Err(Error::TCBExpired);
569 }
570
571 if self.tcb_evaluation_data_number < policy.min_tcb_evaluation_data_number {
572 return Err(Error::TCBEvaluationDataNumberInvalid);
573 }
574
575 Ok(())
576 }
577
578 pub fn verify(&self, report: &Report) -> Result<(), Error> {
580 let expected_mr_signer: Vec<u8> = self
583 .mr_signer
584 .from_hex()
585 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE MRSIGNER")))?;
586 if expected_mr_signer != report.mrsigner {
587 return Err(Error::TCBVerificationFailed);
588 }
589
590 if self.isv_prod_id != report.isvprodid {
593 return Err(Error::TCBVerificationFailed);
594 }
595
596 let raw_miscselect: Vec<u8> = self
600 .miscselect
601 .from_hex()
602 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE miscselect")))?;
603 if raw_miscselect.len() != 4 {
604 return Err(Error::TCBParseError(anyhow::anyhow!(
605 "malformed QE miscselect"
606 )));
607 }
608 let raw_miscselect_mask: Vec<u8> = self
609 .miscselect_mask
610 .from_hex()
611 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE miscselect mask")))?;
612 if raw_miscselect_mask.len() != 4 {
613 return Err(Error::TCBParseError(anyhow::anyhow!(
614 "malformed QE miscselect"
615 )));
616 }
617 let expected_miscselect = LittleEndian::read_u32(&raw_miscselect);
618 let miscselect_mask = LittleEndian::read_u32(&raw_miscselect_mask);
619 if report.miscselect.bits() & miscselect_mask != expected_miscselect {
620 return Err(Error::TCBVerificationFailed);
621 }
622
623 let raw_attributes: Vec<u8> = self
627 .attributes
628 .from_hex()
629 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE attributes")))?;
630 if raw_attributes.len() != 16 {
631 return Err(Error::TCBParseError(anyhow::anyhow!(
632 "malformed QE attributes"
633 )));
634 }
635 let raw_attributes_mask: Vec<u8> = self
636 .attributes_mask
637 .from_hex()
638 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE attributes mask")))?;
639 if raw_attributes_mask.len() != 16 {
640 return Err(Error::TCBParseError(anyhow::anyhow!(
641 "malformed QE attributes"
642 )));
643 }
644 let expected_flags = LittleEndian::read_u64(&raw_attributes[..8]);
645 let expected_xfrm = LittleEndian::read_u64(&raw_attributes[8..]);
646 let flags_mask = LittleEndian::read_u64(&raw_attributes_mask[..8]);
647 let xfrm_mask = LittleEndian::read_u64(&raw_attributes_mask[8..]);
648 if report.attributes.flags.bits() & flags_mask != expected_flags {
649 return Err(Error::TCBVerificationFailed);
650 }
651 if report.attributes.xfrm & xfrm_mask != expected_xfrm {
652 return Err(Error::TCBVerificationFailed);
653 }
654
655 if let Some(level) = self
660 .tcb_levels
661 .iter()
662 .find(|level| level.tcb.isv_svn <= report.isvsvn)
663 {
664 if level.status == TCBStatus::UpToDate {
666 return Ok(());
667 }
668 }
669
670 Err(Error::TCBOutOfDate)
671 }
672}
673
674#[derive(Clone, Debug, Default, serde::Deserialize)]
676pub struct EnclaveTCBLevel {
677 #[serde(rename = "tcb")]
678 pub tcb: EnclaveTCBVersions,
679
680 #[serde(rename = "tcbDate")]
681 pub date: String,
682
683 #[serde(rename = "tcbStatus")]
684 pub status: TCBStatus,
685
686 #[serde(default, rename = "advisoryIDs")]
687 pub advisory_ids: Vec<String>,
688}
689
690#[derive(Clone, Debug, Default, serde::Deserialize)]
692pub struct EnclaveTCBVersions {
693 #[serde(rename = "isvsvn")]
694 pub isv_svn: u16,
695}