oasis_core_runtime/common/sgx/pcs/
tcb.rs

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/// The TCB bundle contains all the required components to verify a quote's TCB.
16#[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/// A signed TCB info structure.
66#[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    // Convert IEEE P1363 ECDSA signature to RFC5480 ASN.1 representation.
102    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/// TCB info identifier.
139#[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/// TCB info body.
149#[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    /// Validate the TCB info against the quote policy.
187    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        // Validate TCB info is not expired/not yet valid based on current time.
210        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        // Validate FMSPC not blacklisted.
231        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    /// Verify and return the TCB level matching the given TCB components and PCESVN.
243    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        // Validate FMSPC matches.
251        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        // Find first matching TCB level.
260        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            // Perform additional TCB status evaluation for TDX module in case TEE TCB SVN at index
269            // 1 is greater or equal to 1, otherwise finish the comparison logic.
270            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                // In order to determine TCB status of TDX module, find a matching TDX Module
274                // Identity (in tdxModuleIdentities array of TCB Info) with its id set to
275                // "TDX_<version>" where <version> matches the value of TEE TCB SVN at index 1. If a
276                // matching TDX Module Identity cannot be found, fail.
277                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                // Otherwise, for the selected TDX Module Identity go over the sorted collection of
285                // TCB Levels starting from the first item on the list and compare its isvsvn value
286                // to the TEE TCB SVN at index 0. If TEE TCB SVN at index 0 is greater or equal to
287                // its value, read tcbStatus assigned to this TCB level, otherwise move to the next
288                // item on TCB levels list.
289                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/// A representation of the properties of Intel's TDX SEAM module.
305#[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/// A representation of the identity of the Intel's TDX SEAM module in case the platform supports
318/// more than one TDX SEAM module.
319#[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/// A platform TCB level.
332#[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    /// Whether the TCB level matches the given TCB components and PCESVN.
349    pub fn matches(
350        &self,
351        sgx_comp_svn: &[u32],
352        tdx_comp_svn: Option<&[u32; 16]>,
353        pcesvn: u32,
354    ) -> bool {
355        // a) Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to
356        //    16) with the corresponding values in the TCB Level. If all SGX TCB Comp SVNs in the
357        //    certificate are greater or equal to the corresponding values in TCB Level, go to b,
358        //    otherwise move to the next item on TCB Levels list.
359        for (i, comp) in self.tcb.sgx_components.iter().enumerate() {
360            // At least one SVN is lower, no match.
361            if sgx_comp_svn[i] < comp.svn {
362                return false;
363            }
364        }
365
366        // b) Compare PCESVN value retrieved from the SGX PCK certificate with the corresponding value
367        //    in the TCB Level. If it is greater or equal to the value in TCB Level, read status
368        //    assigned to this TCB level (in case of SGX) or go to c (in case of TDX). Otherwise,
369        //    move to the next item on TCB Levels list.
370        if self.tcb.pcesvn < pcesvn {
371            return false;
372        }
373
374        if let Some(tdx_comp_svn) = tdx_comp_svn {
375            // c) Compare SVNs in TEE TCB SVN array retrieved from TD Report in Quote (from index 0 to
376            //    15 if TEE TCB SVN at index 1 is set to 0, or from index 2 to 15 otherwise) with the
377            //    corresponding values of SVNs in tdxtcbcomponents array of TCB Level. If all TEE TCB
378            //    SVNs in the TD Report are greater or equal to the corresponding values in TCB Level,
379            //    read tcbStatus assigned to this TCB level. Otherwise, move to the next item on TCB
380            //    Levels list.
381            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                // At least one SVN is lower, no match.
386                if tdx_comp_svn[i] < comp.svn {
387                    return false;
388                }
389            }
390        }
391
392        // Match.
393        true
394    }
395}
396
397/// TCB versions.
398#[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/// A TCB component.
411#[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/// TCB status.
424#[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/// A signed QE identity structure.
439#[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/// QE identity identifier.
479#[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/// QE identity body.
490#[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    /// Validate the QE identity against the quote policy.
534    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        // Validate QE identity is not expired/not yet valid based on current time.
553        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    /// Verify the QE report against the QE identity.
577    pub fn verify(&self, report: &Report) -> Result<(), Error> {
578        // Verify if MRSIGNER field retrieved from SGX Enclave Report is equal to the value of
579        // mrsigner field in QE Identity.
580        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        // Verify if ISVPRODID field retrieved from SGX Enclave Report is equal to the value of
589        // isvprodid field in QE Identity.
590        if self.isv_prod_id != report.isvprodid {
591            return Err(Error::TCBVerificationFailed);
592        }
593
594        // Apply miscselectMask (binary mask) from QE Identity to MISCSELECT field retrieved from
595        // SGX Enclave Report. Verify if the outcome (miscselectMask & MISCSELECT) is equal to the
596        // value of miscselect field in QE Identity.
597        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        // Apply attributesMask (binary mask) from QE Identity to ATTRIBUTES field retrieved from
622        // SGX Enclave Report. Verify if the outcome (attributesMask & ATTRIBUTES) is equal to the
623        // value of attributes field in QE Identity.
624        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        // Determine a TCB status of the Quoting Enclave.
654        //
655        // Go over the list of TCB Levels (descending order) and find the one that has ISVSVN that
656        // is lower or equal to the ISVSVN value from SGX Enclave Report.
657        if let Some(level) = self
658            .tcb_levels
659            .iter()
660            .find(|level| level.tcb.isv_svn <= report.isvsvn)
661        {
662            // Ensure that the TCB is up to date.
663            if level.status == TCBStatus::UpToDate {
664                return Ok(());
665            }
666        }
667
668        Err(Error::TCBOutOfDate)
669    }
670}
671
672/// An enclave TCB level.
673#[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/// Enclave TCB versions.
689#[derive(Clone, Debug, Default, serde::Deserialize)]
690pub struct EnclaveTCBVersions {
691    #[serde(rename = "isvsvn")]
692    pub isv_svn: u16,
693}