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: &Box<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() % 2 != 0 {
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 SGX,
142 TDX,
143 #[serde(other)]
144 #[default]
145 Invalid,
146}
147
148#[derive(Clone, Debug, Default, serde::Deserialize)]
150pub struct TCBInfo {
151 #[serde(rename = "id")]
152 pub id: TCBInfoID,
153
154 #[serde(rename = "version")]
155 pub version: u32,
156
157 #[serde(rename = "issueDate")]
158 pub issue_date: String,
159
160 #[serde(rename = "nextUpdate")]
161 pub next_update: String,
162
163 #[serde(rename = "fmspc")]
164 pub fmspc: String,
165
166 #[serde(rename = "pceId")]
167 pub pceid: String,
168
169 #[serde(rename = "tcbType")]
170 pub tcb_type: u32,
171
172 #[serde(rename = "tcbEvaluationDataNumber")]
173 pub tcb_evaluation_data_number: u32,
174
175 #[serde(default, rename = "tdxModule")]
176 pub tdx_module: TDXModule,
177
178 #[serde(default, rename = "tdxModuleIdentities")]
179 pub tdx_module_identities: Vec<TDXModuleIdentity>,
180
181 #[serde(rename = "tcbLevels")]
182 pub tcb_levels: Vec<TCBLevel>,
183}
184
185impl TCBInfo {
186 pub fn validate(
188 &self,
189 tee_type: TeeType,
190 ts: DateTime<Utc>,
191 policy: &QuotePolicy,
192 ) -> Result<(), Error> {
193 match (self.id, tee_type) {
194 (TCBInfoID::SGX, TeeType::SGX) => {}
195 (TCBInfoID::TDX, TeeType::TDX) => {}
196 _ => {
197 return Err(Error::TCBParseError(anyhow::anyhow!(
198 "unexpected TCB info identifier"
199 )))
200 }
201 }
202
203 if self.version != REQUIRED_TCB_INFO_VERSION {
204 return Err(Error::TCBParseError(anyhow::anyhow!(
205 "unexpected TCB info version"
206 )));
207 }
208
209 let issue_date = NaiveDateTime::parse_from_str(&self.issue_date, PCS_TS_FMT)
211 .map_err(|err| Error::TCBParseError(err.into()))?
212 .and_utc();
213 let _next_update = NaiveDateTime::parse_from_str(&self.next_update, PCS_TS_FMT)
214 .map_err(|err| Error::TCBParseError(err.into()))?
215 .and_utc();
216 if issue_date > ts {
217 return Err(Error::TCBExpired);
218 }
219 if ts - issue_date
220 > Duration::try_days(policy.tcb_validity_period.into())
221 .unwrap_or(DEFAULT_TCB_VALIDITY_PERIOD)
222 {
223 return Err(Error::TCBExpired);
224 }
225
226 if self.tcb_evaluation_data_number < policy.min_tcb_evaluation_data_number {
227 return Err(Error::TCBEvaluationDataNumberInvalid);
228 }
229
230 let blocked = policy
232 .fmspc_blacklist
233 .iter()
234 .any(|blocked| blocked == &self.fmspc);
235 if blocked {
236 return Err(Error::BlacklistedFMSPC);
237 }
238
239 Ok(())
240 }
241
242 pub fn verify(
244 &self,
245 fmspc: &[u8],
246 sgx_comp_svn: &[u32; 16],
247 tdx_comp_svn: Option<&[u32; 16]>,
248 pcesvn: u32,
249 ) -> Result<TCBLevel, Error> {
250 let expected_fmspc: Vec<u8> = self
252 .fmspc
253 .from_hex()
254 .map_err(|err| Error::TCBParseError(err.into()))?;
255 if fmspc != expected_fmspc {
256 return Err(Error::TCBMismatch);
257 }
258
259 let level = self
261 .tcb_levels
262 .iter()
263 .find(|level| level.matches(sgx_comp_svn, tdx_comp_svn, pcesvn))
264 .ok_or(Error::TCBOutOfDate)?
265 .clone();
266
267 if self.id == TCBInfoID::TDX {
268 let tdx_comp_svn = tdx_comp_svn.ok_or(Error::TCBMismatch)?;
271 let tdx_module_version = tdx_comp_svn[1];
272 if tdx_module_version >= 1 {
273 let tdx_module_id = format!("TDX_{:02}", tdx_module_version);
278 let tdx_module = self
279 .tdx_module_identities
280 .iter()
281 .find(|tm| tm.id == tdx_module_id)
282 .ok_or(Error::TCBOutOfDate)?;
283
284 let tdx_module_level = tdx_module
290 .tcb_levels
291 .iter()
292 .find(|level| level.tcb.isv_svn as u32 <= tdx_comp_svn[0])
293 .ok_or(Error::TCBOutOfDate)?;
294 if tdx_module_level.status != TCBStatus::UpToDate {
295 return Err(Error::TCBOutOfDate);
296 }
297 }
298 }
299
300 Ok(level)
301 }
302}
303
304#[derive(Clone, Debug, Default, serde::Deserialize)]
306pub struct TDXModule {
307 #[serde(rename = "mrsigner")]
308 pub mr_signer: String,
309
310 #[serde(rename = "attributes")]
311 pub attributes: String,
312
313 #[serde(rename = "attributesMask")]
314 pub attributes_mask: String,
315}
316
317#[derive(Clone, Debug, Default, serde::Deserialize)]
320pub struct TDXModuleIdentity {
321 #[serde(rename = "id")]
322 pub id: String,
323
324 #[serde(flatten)]
325 pub module: TDXModule,
326
327 #[serde(rename = "tcbLevels")]
328 pub tcb_levels: Vec<EnclaveTCBLevel>,
329}
330
331#[derive(Clone, Debug, Default, serde::Deserialize)]
333pub struct TCBLevel {
334 #[serde(rename = "tcb")]
335 pub tcb: TCBVersions,
336
337 #[serde(rename = "tcbDate")]
338 pub date: String,
339
340 #[serde(rename = "tcbStatus")]
341 pub status: TCBStatus,
342
343 #[serde(default, rename = "advisoryIDs")]
344 pub advisory_ids: Vec<String>,
345}
346
347impl TCBLevel {
348 pub fn matches(
350 &self,
351 sgx_comp_svn: &[u32],
352 tdx_comp_svn: Option<&[u32; 16]>,
353 pcesvn: u32,
354 ) -> bool {
355 for (i, comp) in self.tcb.sgx_components.iter().enumerate() {
360 if sgx_comp_svn[i] < comp.svn {
362 return false;
363 }
364 }
365
366 if self.tcb.pcesvn < pcesvn {
371 return false;
372 }
373
374 if let Some(tdx_comp_svn) = tdx_comp_svn {
375 let comps = self.tcb.tdx_components.iter().enumerate();
382 let offset = if tdx_comp_svn[1] != 0 { 2 } else { 0 };
383
384 for (i, comp) in comps.skip(offset) {
385 if tdx_comp_svn[i] < comp.svn {
387 return false;
388 }
389 }
390 }
391
392 true
394 }
395}
396
397#[derive(Clone, Debug, Default, serde::Deserialize)]
399pub struct TCBVersions {
400 #[serde(rename = "pcesvn")]
401 pub pcesvn: u32,
402
403 #[serde(rename = "sgxtcbcomponents")]
404 pub sgx_components: [TCBComponent; 16],
405
406 #[serde(default, rename = "tdxtcbcomponents")]
407 pub tdx_components: [TCBComponent; 16],
408}
409
410#[derive(Clone, Debug, Default, serde::Deserialize)]
412pub struct TCBComponent {
413 #[serde(rename = "svn")]
414 pub svn: u32,
415
416 #[serde(default, rename = "category")]
417 pub category: String,
418
419 #[serde(default, rename = "type")]
420 pub tcb_comp_type: String,
421}
422
423#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
425pub enum TCBStatus {
426 UpToDate,
427 SWHardeningNeeded,
428 ConfigurationNeeded,
429 ConfigurationAndSWHardeningNeeded,
430 OutOfDate,
431 OutOfDateConfigurationNeeded,
432 Revoked,
433 #[serde(other)]
434 #[default]
435 Invalid,
436}
437
438#[derive(Clone, Debug, Default, serde::Deserialize, cbor::Encode, cbor::Decode)]
440pub struct SignedQEIdentity {
441 #[cbor(
442 rename = "enclave_identity",
443 serialize_with = "encode_raw_value",
444 deserialize_with = "decode_raw_value"
445 )]
446 #[serde(rename = "enclaveIdentity")]
447 pub enclave_identity: Box<RawValue>,
448
449 #[cbor(rename = "signature")]
450 #[serde(rename = "signature")]
451 pub signature: String,
452}
453
454impl PartialEq for SignedQEIdentity {
455 fn eq(&self, other: &SignedQEIdentity) -> bool {
456 self.enclave_identity.get() == other.enclave_identity.get()
457 && self.signature == other.signature
458 }
459}
460
461impl Eq for SignedQEIdentity {}
462
463impl SignedQEIdentity {
464 pub fn open(
465 &self,
466 tee_type: TeeType,
467 ts: DateTime<Utc>,
468 policy: &QuotePolicy,
469 pk: &mut mbedtls::pk::Pk,
470 ) -> Result<QEIdentity, Error> {
471 let qe: QEIdentity = open_signed_tcb(self.enclave_identity.get(), &self.signature, pk)?;
472 qe.validate(tee_type, ts, policy)?;
473
474 Ok(qe)
475 }
476}
477
478#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
480#[allow(non_camel_case_types)]
481pub enum QEIdentityID {
482 QE,
483 TD_QE,
484 #[serde(other)]
485 #[default]
486 Invalid,
487}
488
489#[derive(Clone, Debug, Default, serde::Deserialize)]
491pub struct QEIdentity {
492 #[serde(rename = "id")]
493 pub id: QEIdentityID,
494
495 #[serde(rename = "version")]
496 pub version: u32,
497
498 #[serde(rename = "issueDate")]
499 pub issue_date: String,
500
501 #[serde(rename = "nextUpdate")]
502 pub next_update: String,
503
504 #[serde(rename = "tcbEvaluationDataNumber")]
505 pub tcb_evaluation_data_number: u32,
506
507 #[serde(rename = "miscselect")]
508 pub miscselect: String,
509
510 #[serde(rename = "miscselectMask")]
511 pub miscselect_mask: String,
512
513 #[serde(rename = "attributes")]
514 pub attributes: String,
515
516 #[serde(rename = "attributesMask")]
517 pub attributes_mask: String,
518
519 #[serde(rename = "mrsigner")]
520 pub mr_signer: String,
521
522 #[serde(rename = "isvprodid")]
523 pub isv_prod_id: u16,
524
525 #[serde(rename = "tcbLevels")]
526 pub tcb_levels: Vec<EnclaveTCBLevel>,
527
528 #[serde(default, rename = "advisoryIDs")]
529 pub advisory_ids: Vec<String>,
530}
531
532impl QEIdentity {
533 pub fn validate(
535 &self,
536 tee_type: TeeType,
537 ts: DateTime<Utc>,
538 policy: &QuotePolicy,
539 ) -> Result<(), Error> {
540 match (self.id, tee_type) {
541 (QEIdentityID::QE, TeeType::SGX) => {}
542 (QEIdentityID::TD_QE, TeeType::TDX) => {}
543 _ => return Err(Error::TCBParseError(anyhow::anyhow!("unexpected QE ID"))),
544 }
545
546 if self.version != REQUIRED_QE_IDENTITY_VERSION {
547 return Err(Error::TCBParseError(anyhow::anyhow!(
548 "unexpected QE identity version"
549 )));
550 }
551
552 let issue_date = NaiveDateTime::parse_from_str(&self.issue_date, PCS_TS_FMT)
554 .map_err(|err| Error::TCBParseError(err.into()))?
555 .and_utc();
556 let _next_update = NaiveDateTime::parse_from_str(&self.next_update, PCS_TS_FMT)
557 .map_err(|err| Error::TCBParseError(err.into()))?
558 .and_utc();
559 if issue_date > ts {
560 return Err(Error::TCBExpired);
561 }
562 if ts - issue_date
563 > Duration::try_days(policy.tcb_validity_period.into())
564 .unwrap_or(DEFAULT_TCB_VALIDITY_PERIOD)
565 {
566 return Err(Error::TCBExpired);
567 }
568
569 if self.tcb_evaluation_data_number < policy.min_tcb_evaluation_data_number {
570 return Err(Error::TCBEvaluationDataNumberInvalid);
571 }
572
573 Ok(())
574 }
575
576 pub fn verify(&self, report: &Report) -> Result<(), Error> {
578 let expected_mr_signer: Vec<u8> = self
581 .mr_signer
582 .from_hex()
583 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE MRSIGNER")))?;
584 if expected_mr_signer != report.mrsigner {
585 return Err(Error::TCBVerificationFailed);
586 }
587
588 if self.isv_prod_id != report.isvprodid {
591 return Err(Error::TCBVerificationFailed);
592 }
593
594 let raw_miscselect: Vec<u8> = self
598 .miscselect
599 .from_hex()
600 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE miscselect")))?;
601 if raw_miscselect.len() != 4 {
602 return Err(Error::TCBParseError(anyhow::anyhow!(
603 "malformed QE miscselect"
604 )));
605 }
606 let raw_miscselect_mask: Vec<u8> = self
607 .miscselect_mask
608 .from_hex()
609 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE miscselect mask")))?;
610 if raw_miscselect_mask.len() != 4 {
611 return Err(Error::TCBParseError(anyhow::anyhow!(
612 "malformed QE miscselect"
613 )));
614 }
615 let expected_miscselect = LittleEndian::read_u32(&raw_miscselect);
616 let miscselect_mask = LittleEndian::read_u32(&raw_miscselect_mask);
617 if report.miscselect.bits() & miscselect_mask != expected_miscselect {
618 return Err(Error::TCBVerificationFailed);
619 }
620
621 let raw_attributes: Vec<u8> = self
625 .attributes
626 .from_hex()
627 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE attributes")))?;
628 if raw_attributes.len() != 16 {
629 return Err(Error::TCBParseError(anyhow::anyhow!(
630 "malformed QE attributes"
631 )));
632 }
633 let raw_attributes_mask: Vec<u8> = self
634 .attributes_mask
635 .from_hex()
636 .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE attributes mask")))?;
637 if raw_attributes_mask.len() != 16 {
638 return Err(Error::TCBParseError(anyhow::anyhow!(
639 "malformed QE attributes"
640 )));
641 }
642 let expected_flags = LittleEndian::read_u64(&raw_attributes[..8]);
643 let expected_xfrm = LittleEndian::read_u64(&raw_attributes[8..]);
644 let flags_mask = LittleEndian::read_u64(&raw_attributes_mask[..8]);
645 let xfrm_mask = LittleEndian::read_u64(&raw_attributes_mask[8..]);
646 if report.attributes.flags.bits() & flags_mask != expected_flags {
647 return Err(Error::TCBVerificationFailed);
648 }
649 if report.attributes.xfrm & xfrm_mask != expected_xfrm {
650 return Err(Error::TCBVerificationFailed);
651 }
652
653 if let Some(level) = self
658 .tcb_levels
659 .iter()
660 .find(|level| level.tcb.isv_svn <= report.isvsvn)
661 {
662 if level.status == TCBStatus::UpToDate {
664 return Ok(());
665 }
666 }
667
668 Err(Error::TCBOutOfDate)
669 }
670}
671
672#[derive(Clone, Debug, Default, serde::Deserialize)]
674pub struct EnclaveTCBLevel {
675 #[serde(rename = "tcb")]
676 pub tcb: EnclaveTCBVersions,
677
678 #[serde(rename = "tcbDate")]
679 pub date: String,
680
681 #[serde(rename = "tcbStatus")]
682 pub status: TCBStatus,
683
684 #[serde(default, rename = "advisoryIDs")]
685 pub advisory_ids: Vec<String>,
686}
687
688#[derive(Clone, Debug, Default, serde::Deserialize)]
690pub struct EnclaveTCBVersions {
691 #[serde(rename = "isvsvn")]
692 pub isv_svn: u16,
693}