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: &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().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/// TCB info identifier.
139#[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/// TCB info body.
151#[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    /// Validate the TCB info against the quote policy.
189    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        // Validate TCB info is not expired/not yet valid based on current time.
212        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        // Validate FMSPC not blacklisted.
233        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    /// Verify and return the TCB level matching the given TCB components and PCESVN.
245    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        // Validate FMSPC matches.
253        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        // Find first matching TCB level.
262        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            // Perform additional TCB status evaluation for TDX module in case TEE TCB SVN at index
271            // 1 is greater or equal to 1, otherwise finish the comparison logic.
272            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                // In order to determine TCB status of TDX module, find a matching TDX Module
276                // Identity (in tdxModuleIdentities array of TCB Info) with its id set to
277                // "TDX_<version>" where <version> matches the value of TEE TCB SVN at index 1. If a
278                // matching TDX Module Identity cannot be found, fail.
279                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                // Otherwise, for the selected TDX Module Identity go over the sorted collection of
287                // TCB Levels starting from the first item on the list and compare its isvsvn value
288                // to the TEE TCB SVN at index 0. If TEE TCB SVN at index 0 is greater or equal to
289                // its value, read tcbStatus assigned to this TCB level, otherwise move to the next
290                // item on TCB levels list.
291                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/// A representation of the properties of Intel's TDX SEAM module.
307#[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/// A representation of the identity of the Intel's TDX SEAM module in case the platform supports
320/// more than one TDX SEAM module.
321#[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/// A platform TCB level.
334#[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    /// Whether the TCB level matches the given TCB components and PCESVN.
351    pub fn matches(
352        &self,
353        sgx_comp_svn: &[u32],
354        tdx_comp_svn: Option<&[u32; 16]>,
355        pcesvn: u32,
356    ) -> bool {
357        // a) Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to
358        //    16) with the corresponding values in the TCB Level. If all SGX TCB Comp SVNs in the
359        //    certificate are greater or equal to the corresponding values in TCB Level, go to b,
360        //    otherwise move to the next item on TCB Levels list.
361        for (i, comp) in self.tcb.sgx_components.iter().enumerate() {
362            // At least one SVN is lower, no match.
363            if sgx_comp_svn[i] < comp.svn {
364                return false;
365            }
366        }
367
368        // b) Compare PCESVN value retrieved from the SGX PCK certificate with the corresponding value
369        //    in the TCB Level. If it is greater or equal to the value in TCB Level, read status
370        //    assigned to this TCB level (in case of SGX) or go to c (in case of TDX). Otherwise,
371        //    move to the next item on TCB Levels list.
372        if self.tcb.pcesvn < pcesvn {
373            return false;
374        }
375
376        if let Some(tdx_comp_svn) = tdx_comp_svn {
377            // c) Compare SVNs in TEE TCB SVN array retrieved from TD Report in Quote (from index 0 to
378            //    15 if TEE TCB SVN at index 1 is set to 0, or from index 2 to 15 otherwise) with the
379            //    corresponding values of SVNs in tdxtcbcomponents array of TCB Level. If all TEE TCB
380            //    SVNs in the TD Report are greater or equal to the corresponding values in TCB Level,
381            //    read tcbStatus assigned to this TCB level. Otherwise, move to the next item on TCB
382            //    Levels list.
383            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                // At least one SVN is lower, no match.
388                if tdx_comp_svn[i] < comp.svn {
389                    return false;
390                }
391            }
392        }
393
394        // Match.
395        true
396    }
397}
398
399/// TCB versions.
400#[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/// A TCB component.
413#[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/// TCB status.
426#[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/// A signed QE identity structure.
441#[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/// QE identity identifier.
481#[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/// QE identity body.
492#[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    /// Validate the QE identity against the quote policy.
536    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        // Validate QE identity is not expired/not yet valid based on current time.
555        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    /// Verify the QE report against the QE identity.
579    pub fn verify(&self, report: &Report) -> Result<(), Error> {
580        // Verify if MRSIGNER field retrieved from SGX Enclave Report is equal to the value of
581        // mrsigner field in QE Identity.
582        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        // Verify if ISVPRODID field retrieved from SGX Enclave Report is equal to the value of
591        // isvprodid field in QE Identity.
592        if self.isv_prod_id != report.isvprodid {
593            return Err(Error::TCBVerificationFailed);
594        }
595
596        // Apply miscselectMask (binary mask) from QE Identity to MISCSELECT field retrieved from
597        // SGX Enclave Report. Verify if the outcome (miscselectMask & MISCSELECT) is equal to the
598        // value of miscselect field in QE Identity.
599        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        // Apply attributesMask (binary mask) from QE Identity to ATTRIBUTES field retrieved from
624        // SGX Enclave Report. Verify if the outcome (attributesMask & ATTRIBUTES) is equal to the
625        // value of attributes field in QE Identity.
626        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        // Determine a TCB status of the Quoting Enclave.
656        //
657        // Go over the list of TCB Levels (descending order) and find the one that has ISVSVN that
658        // is lower or equal to the ISVSVN value from SGX Enclave Report.
659        if let Some(level) = self
660            .tcb_levels
661            .iter()
662            .find(|level| level.tcb.isv_svn <= report.isvsvn)
663        {
664            // Ensure that the TCB is up to date.
665            if level.status == TCBStatus::UpToDate {
666                return Ok(());
667            }
668        }
669
670        Err(Error::TCBOutOfDate)
671    }
672}
673
674/// An enclave TCB level.
675#[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/// Enclave TCB versions.
691#[derive(Clone, Debug, Default, serde::Deserialize)]
692pub struct EnclaveTCBVersions {
693    #[serde(rename = "isvsvn")]
694    pub isv_svn: u16,
695}