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