• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

oasisprotocol / oasis-core / #6058

23 May 2025 08:25AM UTC coverage: 47.717% (+0.06%) from 47.66%
#6058

Pull #6197

kostko
go/runtime: Add log manager
Pull Request #6197: go/runtime: Add log manager

19 of 29 new or added lines in 5 files covered. (65.52%)

113 existing lines in 3 files now uncovered.

4578 of 9594 relevant lines covered (47.72%)

1.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.57
/runtime/src/common/sgx/pcs/tcb.rs
1
use std::ffi::CString;
2

3
use byteorder::{ByteOrder, LittleEndian};
4
use chrono::{prelude::*, Duration};
5
use mbedtls::{alloc::Box as MbedtlsBox, x509::certificate::Certificate};
6
use rustc_hex::FromHex;
7
use serde_json::value::RawValue;
8
use sgx_isa::Report;
9

10
use 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)]
17
pub 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

28
impl TCBBundle {
29
    pub(super) fn verify_certificates(
1✔
30
        &self,
31
        ts: DateTime<Utc>,
32
    ) -> Result<MbedtlsBox<Certificate>, Error> {
33
        let raw_certs =
1✔
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())
2✔
UNCOV
36
            .map_err(|err| Error::TCBParseError(err.into()))?;
×
37
        if cert_chain.iter().count() != 2 {
5✔
UNCOV
38
            return Err(Error::UnexpectedCertificateChain);
×
39
        }
40

41
        Certificate::verify_with_callback(
42
            &cert_chain,
43
            &PCS_TRUST_ROOT,
4✔
44
            None,
1✔
45
            None,
1✔
46
            x509_custom_ts_verify_cb(ts),
1✔
47
        )
NEW
UNCOV
48
        .map_err(|_| Error::TCBVerificationFailed)?;
×
49

50
        Ok(cert_chain.pop_front().unwrap())
3✔
51
    }
52
}
53

54
#[inline]
UNCOV
55
fn encode_raw_value(value: &Box<RawValue>) -> Vec<u8> {
×
56
    value.get().as_bytes().to_owned()
×
57
}
58

59
#[inline]
60
fn decode_raw_value(value: Vec<u8>) -> Result<Box<RawValue>, cbor::DecodeError> {
2✔
61
    RawValue::from_string(String::from_utf8(value).map_err(|_| cbor::DecodeError::UnexpectedType)?)
2✔
UNCOV
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)]
67
pub 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

81
impl PartialEq for SignedTCBInfo {
UNCOV
82
    fn eq(&self, other: &SignedTCBInfo) -> bool {
×
UNCOV
83
        self.tcb_info.get() == other.tcb_info.get() && self.signature == other.signature
×
84
    }
85
}
86

87
impl Eq for SignedTCBInfo {}
88

89
fn open_signed_tcb<'a, T: serde::Deserialize<'a>>(
2✔
90
    data: &'a str,
91
    signature: &str,
92
    pk: &mut mbedtls::pk::Pk,
93
) -> Result<T, Error> {
94
    let mut hash = [0u8; 32];
2✔
95
    mbedtls::hash::Md::hash(mbedtls::hash::Type::Sha256, data.as_bytes(), &mut hash)
2✔
UNCOV
96
        .map_err(|_| Error::TCBVerificationFailed)?;
×
97
    let sig: Vec<u8> = signature
4✔
98
        .from_hex()
UNCOV
99
        .map_err(|_| Error::TCBVerificationFailed)?;
×
100

101
    // Convert IEEE P1363 ECDSA signature to RFC5480 ASN.1 representation.
102
    if sig.len() % 2 != 0 {
4✔
UNCOV
103
        return Err(Error::TCBVerificationFailed);
×
104
    }
105

106
    let (r_bytes, s_bytes) = sig.split_at(sig.len() / 2);
6✔
107
    let r = num_bigint::BigUint::from_bytes_be(r_bytes);
2✔
108
    let s = num_bigint::BigUint::from_bytes_be(s_bytes);
2✔
109

110
    let sig = yasna::construct_der(|writer| {
7✔
111
        writer.write_sequence(|writer| {
5✔
112
            writer.next().write_biguint(&r);
4✔
113
            writer.next().write_biguint(&s);
4✔
114
        })
115
    });
116

117
    pk.verify(mbedtls::hash::Type::Sha256, &hash, &sig)
9✔
UNCOV
118
        .map_err(|_| Error::TCBVerificationFailed)?;
×
119

120
    serde_json::from_str(data).map_err(|err| Error::TCBParseError(err.into()))
6✔
121
}
122

123
impl SignedTCBInfo {
124
    pub fn open(
1✔
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)?;
1✔
132
        ti.validate(tee_type, ts, policy)?;
5✔
133

134
        Ok(ti)
1✔
135
    }
136
}
137

138
/// TCB info identifier.
139
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
140
pub 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)]
150
pub 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

185
impl TCBInfo {
186
    /// Validate the TCB info against the quote policy.
187
    pub fn validate(
1✔
188
        &self,
189
        tee_type: TeeType,
190
        ts: DateTime<Utc>,
191
        policy: &QuotePolicy,
192
    ) -> Result<(), Error> {
193
        match (self.id, tee_type) {
1✔
194
            (TCBInfoID::SGX, TeeType::SGX) => {}
195
            (TCBInfoID::TDX, TeeType::TDX) => {}
196
            _ => {
UNCOV
197
                return Err(Error::TCBParseError(anyhow::anyhow!(
×
198
                    "unexpected TCB info identifier"
199
                )))
200
            }
201
        }
202

203
        if self.version != REQUIRED_TCB_INFO_VERSION {
1✔
UNCOV
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)
3✔
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)
2✔
UNCOV
214
            .map_err(|err| Error::TCBParseError(err.into()))?
×
215
            .and_utc();
216
        if issue_date > ts {
1✔
217
            return Err(Error::TCBExpired);
×
218
        }
219
        if ts - issue_date
3✔
220
            > Duration::try_days(policy.tcb_validity_period.into())
1✔
221
                .unwrap_or(DEFAULT_TCB_VALIDITY_PERIOD)
222
        {
UNCOV
223
            return Err(Error::TCBExpired);
×
224
        }
225

226
        if self.tcb_evaluation_data_number < policy.min_tcb_evaluation_data_number {
2✔
UNCOV
227
            return Err(Error::TCBEvaluationDataNumberInvalid);
×
228
        }
229

230
        // Validate FMSPC not blacklisted.
231
        let blocked = policy
1✔
232
            .fmspc_blacklist
233
            .iter()
234
            .any(|blocked| blocked == &self.fmspc);
2✔
235
        if blocked {
2✔
236
            return Err(Error::BlacklistedFMSPC);
1✔
237
        }
238

239
        Ok(())
1✔
240
    }
241

242
    /// Verify and return the TCB level matching the given TCB components and PCESVN.
243
    pub fn verify(
4✔
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
4✔
252
            .fmspc
253
            .from_hex()
UNCOV
254
            .map_err(|err| Error::TCBParseError(err.into()))?;
×
255
        if fmspc != expected_fmspc {
8✔
UNCOV
256
            return Err(Error::TCBMismatch);
×
257
        }
258

259
        // Find first matching TCB level.
260
        let level = self
15✔
261
            .tcb_levels
262
            .iter()
263
            .find(|level| level.matches(sgx_comp_svn, tdx_comp_svn, pcesvn))
12✔
264
            .ok_or(Error::TCBOutOfDate)?
2✔
265
            .clone();
266

267
        if self.id == TCBInfoID::TDX {
4✔
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)?;
1✔
271
            let tdx_module_version = tdx_comp_svn[1];
2✔
272
            if tdx_module_version >= 1 {
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);
1✔
278
                let tdx_module = self
3✔
279
                    .tdx_module_identities
280
                    .iter()
281
                    .find(|tm| tm.id == tdx_module_id)
2✔
282
                    .ok_or(Error::TCBOutOfDate)?;
1✔
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
3✔
290
                    .tcb_levels
291
                    .iter()
292
                    .find(|level| level.tcb.isv_svn as u32 <= tdx_comp_svn[0])
2✔
293
                    .ok_or(Error::TCBOutOfDate)?;
1✔
294
                if tdx_module_level.status != TCBStatus::UpToDate {
2✔
UNCOV
295
                    return Err(Error::TCBOutOfDate);
×
296
                }
297
            }
298
        }
299

300
        Ok(level)
2✔
301
    }
302
}
303

304
/// A representation of the properties of Intel's TDX SEAM module.
305
#[derive(Clone, Debug, Default, serde::Deserialize)]
306
pub 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)]
320
pub 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)]
333
pub 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

347
impl TCBLevel {
348
    /// Whether the TCB level matches the given TCB components and PCESVN.
349
    pub fn matches(
4✔
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() {
12✔
360
            // At least one SVN is lower, no match.
361
            if sgx_comp_svn[i] < comp.svn {
8✔
362
                return false;
1✔
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 {
3✔
UNCOV
371
            return false;
×
372
        }
373

374
        if let Some(tdx_comp_svn) = tdx_comp_svn {
4✔
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();
1✔
382
            let offset = if tdx_comp_svn[1] != 0 { 2 } else { 0 };
1✔
383

384
            for (i, comp) in comps.skip(offset) {
2✔
385
                // At least one SVN is lower, no match.
386
                if tdx_comp_svn[i] < comp.svn {
1✔
UNCOV
387
                    return false;
×
388
                }
389
            }
390
        }
391

392
        // Match.
393
        true
2✔
394
    }
395
}
396

397
/// TCB versions.
398
#[derive(Clone, Debug, Default, serde::Deserialize)]
399
pub 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)]
412
pub 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)]
425
pub 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)]
440
pub 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

454
impl PartialEq for SignedQEIdentity {
UNCOV
455
    fn eq(&self, other: &SignedQEIdentity) -> bool {
×
UNCOV
456
        self.enclave_identity.get() == other.enclave_identity.get()
×
UNCOV
457
            && self.signature == other.signature
×
458
    }
459
}
460

461
impl Eq for SignedQEIdentity {}
462

463
impl SignedQEIdentity {
464
    pub fn open(
1✔
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)?;
1✔
472
        qe.validate(tee_type, ts, policy)?;
3✔
473

474
        Ok(qe)
2✔
475
    }
476
}
477

478
/// QE identity identifier.
479
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize)]
480
#[allow(non_camel_case_types)]
481
pub 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)]
491
pub 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

532
impl QEIdentity {
533
    /// Validate the QE identity against the quote policy.
534
    pub fn validate(
1✔
535
        &self,
536
        tee_type: TeeType,
537
        ts: DateTime<Utc>,
538
        policy: &QuotePolicy,
539
    ) -> Result<(), Error> {
540
        match (self.id, tee_type) {
1✔
541
            (QEIdentityID::QE, TeeType::SGX) => {}
542
            (QEIdentityID::TD_QE, TeeType::TDX) => {}
UNCOV
543
            _ => return Err(Error::TCBParseError(anyhow::anyhow!("unexpected QE ID"))),
×
544
        }
545

546
        if self.version != REQUIRED_QE_IDENTITY_VERSION {
1✔
UNCOV
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)
2✔
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)
2✔
UNCOV
557
            .map_err(|err| Error::TCBParseError(err.into()))?
×
558
            .and_utc();
559
        if issue_date > ts {
1✔
560
            return Err(Error::TCBExpired);
×
561
        }
562
        if ts - issue_date
2✔
563
            > Duration::try_days(policy.tcb_validity_period.into())
1✔
564
                .unwrap_or(DEFAULT_TCB_VALIDITY_PERIOD)
565
        {
UNCOV
566
            return Err(Error::TCBExpired);
×
567
        }
568

569
        if self.tcb_evaluation_data_number < policy.min_tcb_evaluation_data_number {
1✔
UNCOV
570
            return Err(Error::TCBEvaluationDataNumberInvalid);
×
571
        }
572

573
        Ok(())
1✔
574
    }
575

576
    /// Verify the QE report against the QE identity.
577
    pub fn verify(&self, report: &Report) -> Result<(), Error> {
3✔
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
3✔
581
            .mr_signer
582
            .from_hex()
UNCOV
583
            .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE MRSIGNER")))?;
×
584
        if expected_mr_signer != report.mrsigner {
6✔
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 {
3✔
UNCOV
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
6✔
598
            .miscselect
599
            .from_hex()
UNCOV
600
            .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE miscselect")))?;
×
601
        if raw_miscselect.len() != 4 {
6✔
UNCOV
602
            return Err(Error::TCBParseError(anyhow::anyhow!(
×
603
                "malformed QE miscselect"
604
            )));
605
        }
606
        let raw_miscselect_mask: Vec<u8> = self
6✔
607
            .miscselect_mask
608
            .from_hex()
UNCOV
609
            .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE miscselect mask")))?;
×
610
        if raw_miscselect_mask.len() != 4 {
6✔
UNCOV
611
            return Err(Error::TCBParseError(anyhow::anyhow!(
×
612
                "malformed QE miscselect"
613
            )));
614
        }
615
        let expected_miscselect = LittleEndian::read_u32(&raw_miscselect);
6✔
616
        let miscselect_mask = LittleEndian::read_u32(&raw_miscselect_mask);
3✔
617
        if report.miscselect.bits() & miscselect_mask != expected_miscselect {
3✔
UNCOV
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
6✔
625
            .attributes
626
            .from_hex()
UNCOV
627
            .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE attributes")))?;
×
628
        if raw_attributes.len() != 16 {
6✔
UNCOV
629
            return Err(Error::TCBParseError(anyhow::anyhow!(
×
630
                "malformed QE attributes"
631
            )));
632
        }
633
        let raw_attributes_mask: Vec<u8> = self
6✔
634
            .attributes_mask
635
            .from_hex()
UNCOV
636
            .map_err(|_| Error::TCBParseError(anyhow::anyhow!("malformed QE attributes mask")))?;
×
637
        if raw_attributes_mask.len() != 16 {
6✔
UNCOV
638
            return Err(Error::TCBParseError(anyhow::anyhow!(
×
639
                "malformed QE attributes"
640
            )));
641
        }
642
        let expected_flags = LittleEndian::read_u64(&raw_attributes[..8]);
6✔
643
        let expected_xfrm = LittleEndian::read_u64(&raw_attributes[8..]);
3✔
644
        let flags_mask = LittleEndian::read_u64(&raw_attributes_mask[..8]);
3✔
645
        let xfrm_mask = LittleEndian::read_u64(&raw_attributes_mask[8..]);
3✔
646
        if report.attributes.flags.bits() & flags_mask != expected_flags {
3✔
UNCOV
647
            return Err(Error::TCBVerificationFailed);
×
648
        }
649
        if report.attributes.xfrm & xfrm_mask != expected_xfrm {
3✔
UNCOV
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
6✔
658
            .tcb_levels
659
            .iter()
660
            .find(|level| level.tcb.isv_svn <= report.isvsvn)
6✔
661
        {
662
            // Ensure that the TCB is up to date.
663
            if level.status == TCBStatus::UpToDate {
6✔
664
                return Ok(());
3✔
665
            }
666
        }
667

UNCOV
668
        Err(Error::TCBOutOfDate)
×
669
    }
670
}
671

672
/// An enclave TCB level.
673
#[derive(Clone, Debug, Default, serde::Deserialize)]
674
pub 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)]
690
pub struct EnclaveTCBVersions {
691
    #[serde(rename = "isvsvn")]
692
    pub isv_svn: u16,
693
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc