oasis_core_runtime/common/sgx/pcs/
quote.rs

1use std::{borrow::Cow, convert::TryInto, ffi::CString, mem};
2
3use byteorder::{ByteOrder, LittleEndian};
4use chrono::prelude::*;
5use mbedtls::{
6    alloc::List as MbedtlsList,
7    ecp::{EcGroup, EcPoint},
8    hash::{self, Md},
9    pk::{EcGroupId, Pk},
10    x509::certificate::Certificate,
11};
12use num_derive::{FromPrimitive, ToPrimitive};
13use num_traits::FromPrimitive;
14use sgx_isa::AttributesFlags;
15
16use super::{
17    certificates::PCS_TRUST_ROOT,
18    constants::*,
19    policy::QuotePolicy,
20    report::{SgxReport, TdAttributes, TdReport},
21    tcb::{QEIdentity, TCBBundle, TCBInfo, TCBLevel, TCBStatus},
22    utils::{x509_custom_ts_verify_cb, TakePrefix},
23    Error,
24};
25use crate::common::sgx::{EnclaveIdentity, MrEnclave, MrSigner, VerifiedQuote};
26
27/// An attestation quote together with the TCB bundle required for its verification.
28#[derive(Clone, Debug, Default, PartialEq, Eq, cbor::Encode, cbor::Decode)]
29pub struct QuoteBundle {
30    #[cbor(rename = "quote")]
31    pub quote: Vec<u8>,
32
33    #[cbor(rename = "tcb")]
34    pub tcb: TCBBundle,
35}
36
37impl QuoteBundle {
38    /// Verify the quote bundle.
39    pub fn verify(&self, policy: &QuotePolicy, ts: DateTime<Utc>) -> Result<VerifiedQuote, Error> {
40        if policy.disabled {
41            return Err(Error::Disabled);
42        }
43
44        // XXX: We reuse the IAS specific variables to avoid having additional environment
45        // variables. Rename these variables when IAS support is removed.
46        let unsafe_skip_quote_verification = option_env!("OASIS_UNSAFE_SKIP_AVR_VERIFY").is_some();
47        let unsafe_lax_quote_verification = option_env!("OASIS_UNSAFE_LAX_AVR_VERIFY").is_some();
48
49        // Parse the quote.
50        let quote = Quote::parse(&self.quote)?;
51        let tee_type = quote.header().tee_type();
52
53        // Ensure given TEE type is allowed by the policy.
54        match (tee_type, &policy.tdx) {
55            (TeeType::SGX, _) => { /* Ok. */ }
56            (TeeType::TDX, &None) => return Err(Error::TeeTypeNotAllowed),
57            (TeeType::TDX, &Some(_)) => { /* Ok. */ }
58        }
59
60        // Ensure correct QE vendor.
61        if quote.header().qe_vendor_id() != QE_VENDOR_ID_INTEL {
62            return Err(Error::UnsupportedQEVendor);
63        }
64
65        // Verify TCB bundle and get TCB info and QE identity.
66        let mut tcb_cert = self.tcb.verify_certificates(ts)?;
67        let qe_identity =
68            self.tcb
69                .qe_identity
70                .open(tee_type, ts, policy, tcb_cert.public_key_mut())?;
71        let tcb_info = self
72            .tcb
73            .tcb_info
74            .open(tee_type, ts, policy, tcb_cert.public_key_mut())?;
75
76        // We use the TCB info issue date as the timestamp.
77        let timestamp = NaiveDateTime::parse_from_str(&tcb_info.issue_date, PCS_TS_FMT)
78            .map_err(|err| Error::TCBParseError(err.into()))?
79            .and_utc()
80            .timestamp();
81
82        // Perform quote verification.
83        if !unsafe_skip_quote_verification {
84            let tcb_level = quote.verify(tcb_info, qe_identity, ts)?;
85
86            // Validate TCB level.
87            match tcb_level.status {
88                TCBStatus::UpToDate | TCBStatus::SWHardeningNeeded => {}
89                TCBStatus::OutOfDate
90                | TCBStatus::ConfigurationNeeded
91                | TCBStatus::OutOfDateConfigurationNeeded
92                    if unsafe_lax_quote_verification => {}
93                _ => {
94                    return Err(Error::TCBOutOfDate);
95                }
96            }
97        }
98
99        // Disallow debug enclaves, if we are in production environment and disallow production
100        // enclaves, if we are in debug environment.
101        let is_debug = quote.report_body().is_debug();
102        let allow_debug = option_env!("OASIS_UNSAFE_ALLOW_DEBUG_ENCLAVES").is_some();
103        if is_debug && !allow_debug {
104            return Err(Error::DebugEnclave);
105        } else if !is_debug && allow_debug {
106            return Err(Error::ProductionEnclave);
107        }
108
109        // Verify report against TDX policy.
110        if let ReportBody::Tdx(report) = quote.report_body() {
111            let tdx_policy = policy.tdx.as_ref().ok_or(Error::TeeTypeNotAllowed)?;
112            tdx_policy.verify(report)?;
113        }
114
115        Ok(VerifiedQuote {
116            report_data: quote.report_body().report_data(),
117            identity: quote.report_body().as_enclave_identity(),
118            timestamp,
119        })
120    }
121}
122
123/// An enclave quote.
124#[derive(Debug)]
125pub struct Quote<'a> {
126    header: Header<'a>,
127    report_body: ReportBody,
128    signature: QuoteSignatureEcdsaP256<'a>,
129    signed_data: Cow<'a, [u8]>,
130}
131
132impl<'a> Quote<'a> {
133    pub fn parse<T: Into<Cow<'a, [u8]>>>(quote: T) -> Result<Quote<'a>, Error> {
134        let mut quote = quote.into();
135        let mut raw = quote.clone();
136
137        // Parse header, depending on version.
138        let version = quote
139            .take_prefix(mem::size_of::<u16>())
140            .map(|v| LittleEndian::read_u16(&v))?;
141        match version {
142            QUOTE_VERSION_3 => {
143                // Version 3 (SGX-ECDSA).
144                let att_key_type = quote
145                    .take_prefix(mem::size_of::<u16>())
146                    .map(|v| LittleEndian::read_u16(&v))?;
147                let attestation_key_type = AttestationKeyType::from_u16(att_key_type)
148                    .ok_or(Error::UnsupportedAttestationKeyType)?;
149                let reserved = quote
150                    .take_prefix(mem::size_of::<u32>())
151                    .map(|v| LittleEndian::read_u32(&v))?;
152                if reserved != 0 {
153                    return Err(Error::QuoteParseError("data in reserved field".to_string()));
154                }
155
156                let qe_svn = quote
157                    .take_prefix(mem::size_of::<u16>())
158                    .map(|v| LittleEndian::read_u16(&v))?;
159                let pce_svn = quote
160                    .take_prefix(mem::size_of::<u16>())
161                    .map(|v| LittleEndian::read_u16(&v))?;
162                let qe_vendor_id = quote.take_prefix(QE_VENDOR_ID_LEN)?;
163                let user_data = quote.take_prefix(QE_USER_DATA_LEN)?;
164                let report_body = quote.take_prefix(SGX_REPORT_BODY_LEN)?;
165                let report_body = ReportBody::parse(TeeType::SGX, &report_body)?;
166
167                if attestation_key_type != AttestationKeyType::EcdsaP256 {
168                    return Err(Error::UnsupportedAttestationKeyType);
169                }
170                let signature = QuoteSignatureEcdsaP256::parse(version, quote)?;
171                let signed_data = raw.take_prefix(QUOTE_HEADER_LEN + SGX_REPORT_BODY_LEN)?;
172
173                Ok(Quote {
174                    header: Header::V3 {
175                        attestation_key_type,
176                        qe_svn,
177                        pce_svn,
178                        qe_vendor_id,
179                        user_data,
180                    },
181                    report_body,
182                    signature,
183                    signed_data,
184                })
185            }
186            QUOTE_VERSION_4 => {
187                // Version 4 (TDX-ECDSA, SGX-ECDSA).
188                let att_key_type = quote
189                    .take_prefix(mem::size_of::<u16>())
190                    .map(|v| LittleEndian::read_u16(&v))?;
191                let attestation_key_type = AttestationKeyType::from_u16(att_key_type)
192                    .ok_or(Error::UnsupportedAttestationKeyType)?;
193
194                let tee_type_raw = quote
195                    .take_prefix(mem::size_of::<u32>())
196                    .map(|v| LittleEndian::read_u32(&v))?;
197                let tee_type = TeeType::from_u32(tee_type_raw).ok_or(Error::UnsupportedTeeType)?;
198
199                let reserved1 = quote
200                    .take_prefix(mem::size_of::<u16>())
201                    .map(|v| LittleEndian::read_u16(&v))?;
202                let reserved2 = quote
203                    .take_prefix(mem::size_of::<u16>())
204                    .map(|v| LittleEndian::read_u16(&v))?;
205
206                if reserved1 != 0 || reserved2 != 0 {
207                    return Err(Error::QuoteParseError("data in reserved field".to_string()));
208                }
209
210                let qe_vendor_id = quote.take_prefix(QE_VENDOR_ID_LEN)?;
211                let user_data = quote.take_prefix(QE_USER_DATA_LEN)?;
212
213                let header = Header::V4 {
214                    attestation_key_type,
215                    tee_type,
216                    qe_vendor_id,
217                    user_data,
218                };
219                let report_body = quote.take_prefix(header.report_body_len())?;
220                let report_body = ReportBody::parse(tee_type, &report_body)?;
221
222                if attestation_key_type != AttestationKeyType::EcdsaP256 {
223                    return Err(Error::UnsupportedAttestationKeyType);
224                }
225                let signature = QuoteSignatureEcdsaP256::parse(version, quote)?;
226                let signed_data = raw.take_prefix(QUOTE_HEADER_LEN + header.report_body_len())?;
227
228                Ok(Quote {
229                    header,
230                    report_body,
231                    signature,
232                    signed_data,
233                })
234            }
235            _ => Err(Error::QuoteParseError(format!(
236                "unsupported quote version: {}",
237                version
238            ))),
239        }
240    }
241
242    /// Quote header.
243    pub fn header(&self) -> &Header<'a> {
244        &self.header
245    }
246
247    /// Report body.
248    pub fn report_body(&self) -> &ReportBody {
249        &self.report_body
250    }
251
252    /// Verify quote.
253    pub fn verify(
254        &self,
255        tcb_info: TCBInfo,
256        qe_identity: QEIdentity,
257        ts: DateTime<Utc>,
258    ) -> Result<TCBLevel, Error> {
259        let tdx_comp_svn = self.report_body.tdx_comp_svn();
260
261        let mut verifier: QeEcdsaP256Verifier =
262            QeEcdsaP256Verifier::new(tcb_info, qe_identity, tdx_comp_svn, ts);
263        self.signature.verify(&self.signed_data, &mut verifier)?;
264
265        Ok(verifier.tcb_level().unwrap())
266    }
267}
268
269/// An enclave quote header.
270#[derive(Debug)]
271pub enum Header<'a> {
272    V3 {
273        attestation_key_type: AttestationKeyType,
274        qe_svn: u16,
275        pce_svn: u16,
276        qe_vendor_id: Cow<'a, [u8]>,
277        user_data: Cow<'a, [u8]>,
278    },
279
280    V4 {
281        attestation_key_type: AttestationKeyType,
282        tee_type: TeeType,
283        qe_vendor_id: Cow<'a, [u8]>,
284        user_data: Cow<'a, [u8]>,
285    },
286}
287
288impl Header<'_> {
289    /// Quote header version.
290    pub fn version(&self) -> u16 {
291        match self {
292            Self::V3 { .. } => QUOTE_VERSION_3,
293            Self::V4 { .. } => QUOTE_VERSION_4,
294        }
295    }
296
297    /// Attestation key type.
298    pub fn attestation_key_type(&self) -> AttestationKeyType {
299        match self {
300            Self::V3 {
301                attestation_key_type,
302                ..
303            } => *attestation_key_type,
304            Self::V4 {
305                attestation_key_type,
306                ..
307            } => *attestation_key_type,
308        }
309    }
310
311    /// TEE type the quote is for.
312    pub fn tee_type(&self) -> TeeType {
313        match self {
314            Self::V3 { .. } => TeeType::SGX,
315            Self::V4 { tee_type, .. } => *tee_type,
316        }
317    }
318
319    /// Quoting Enclave (QE) vendor identifier.
320    pub fn qe_vendor_id(&self) -> &[u8] {
321        match self {
322            Self::V3 { qe_vendor_id, .. } => qe_vendor_id,
323            Self::V4 { qe_vendor_id, .. } => qe_vendor_id,
324        }
325    }
326
327    /// Length of the report body field.
328    pub fn report_body_len(&self) -> usize {
329        match self.tee_type() {
330            TeeType::SGX => SGX_REPORT_BODY_LEN,
331            TeeType::TDX => TDX_REPORT_BODY_LEN,
332        }
333    }
334}
335
336/// TEE type.
337#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
338#[repr(u32)]
339pub enum TeeType {
340    SGX = 0x00000000,
341    TDX = 0x00000081,
342}
343
344/// Attestation key type.
345#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
346#[repr(u16)]
347pub enum AttestationKeyType {
348    EcdsaP256 = 2,
349}
350
351/// Report body.
352#[derive(Debug)]
353pub enum ReportBody {
354    Sgx(SgxReport),
355    Tdx(TdReport),
356}
357
358impl ReportBody {
359    /// Parse the report body.
360    pub fn parse(tee_type: TeeType, raw: &[u8]) -> Result<Self, Error> {
361        match tee_type {
362            TeeType::SGX => {
363                // Parse SGX report body.
364                let mut report_body = Vec::with_capacity(SgxReport::UNPADDED_SIZE);
365                report_body.extend(raw);
366                report_body.resize_with(SgxReport::UNPADDED_SIZE, Default::default);
367                let report =
368                    SgxReport::try_copy_from(&report_body).ok_or(Error::MalformedReport)?;
369
370                Ok(Self::Sgx(report))
371            }
372            TeeType::TDX => {
373                // Parse TDX TD report body.
374                let report = TdReport::parse(raw)?;
375
376                Ok(Self::Tdx(report))
377            }
378        }
379    }
380
381    /// TDX TEE Component SVNs.
382    ///
383    /// Returns `None` in case of a non-TDX report body.
384    pub fn tdx_comp_svn(&self) -> Option<[u32; 16]> {
385        match self {
386            Self::Sgx(_) => None,
387            Self::Tdx(report) => Some(
388                report
389                    .tee_tcb_svn
390                    .iter()
391                    .map(|x| *x as u32)
392                    .collect::<Vec<u32>>()
393                    .try_into()
394                    .unwrap(),
395            ),
396        }
397    }
398
399    /// Whether the report indicates a debug TEE.
400    pub fn is_debug(&self) -> bool {
401        match self {
402            Self::Sgx(report) => report.attributes.flags.contains(AttributesFlags::DEBUG),
403            Self::Tdx(report) => report.td_attributes.contains(TdAttributes::DEBUG),
404        }
405    }
406
407    /// Converts this report into an enclave identity.
408    pub fn as_enclave_identity(&self) -> EnclaveIdentity {
409        match self {
410            Self::Sgx(report) => EnclaveIdentity {
411                mr_enclave: MrEnclave::from(report.mrenclave.to_vec()),
412                mr_signer: MrSigner::from(report.mrsigner.to_vec()),
413            },
414            Self::Tdx(report) => report.as_enclave_identity(),
415        }
416    }
417
418    /// Data contained in the report.
419    pub fn report_data(&self) -> Vec<u8> {
420        match self {
421            Self::Sgx(report) => report.reportdata.to_vec(),
422            Self::Tdx(report) => report.report_data.to_vec(),
423        }
424    }
425}
426
427/// Quote signature trait.
428pub trait QuoteSignature<'a>: Sized {
429    /// Parse the quote signature from the passed data.
430    fn parse(version: u16, data: Cow<'a, [u8]>) -> Result<Self, Error>;
431}
432
433/// ECDSA-P256 quote signature.
434#[derive(Debug)]
435pub struct QuoteSignatureEcdsaP256<'a> {
436    signature: Cow<'a, [u8]>,
437    attestation_public_key: Cow<'a, [u8]>,
438
439    qe: CertificationDataQeReport<'a>,
440}
441
442impl<'a> QuoteSignature<'a> for QuoteSignatureEcdsaP256<'a> {
443    fn parse(version: u16, mut data: Cow<'a, [u8]>) -> Result<Self, Error> {
444        let sig_len = data
445            .take_prefix(mem::size_of::<u32>())
446            .map(|v| LittleEndian::read_u32(&v))?;
447        if sig_len as usize != data.len() {
448            return Err(Error::QuoteParseError(
449                "unexpected trailing data after signature".to_string(),
450            ));
451        }
452        let signature = data.take_prefix(ECDSA_P256_SIGNATURE_LEN)?;
453        let attestation_public_key = data.take_prefix(ECDSA_P256_PUBLIC_KEY_LEN)?;
454
455        // In version 4 quotes, there is an intermediate certification data tuple.
456        if version == QUOTE_VERSION_4 {
457            let cd_type = data
458                .take_prefix(mem::size_of::<u16>())
459                .map(|v| LittleEndian::read_u16(&v))?;
460            let certification_data_type =
461                CertificationDataType::from_u16(cd_type).ok_or_else(|| {
462                    Error::QuoteParseError(format!("unknown certification data type: {}", cd_type))
463                })?;
464            let certdata_len = data
465                .take_prefix(mem::size_of::<u32>())
466                .map(|v| LittleEndian::read_u32(&v))?;
467            if certdata_len as usize != data.len() {
468                return Err(Error::QuoteParseError(
469                    "invalid certification data length".to_string(),
470                ));
471            }
472
473            if certification_data_type != CertificationDataType::QeReport {
474                return Err(Error::UnexpectedCertificationData);
475            }
476        }
477
478        let qe = CertificationDataQeReport::parse(data)?;
479
480        Ok(QuoteSignatureEcdsaP256 {
481            signature,
482            attestation_public_key,
483            qe,
484        })
485    }
486}
487
488impl<'a> QuoteSignatureEcdsaP256<'a> {
489    /// Raw signature.
490    pub fn signature(&self) -> &[u8] {
491        &self.signature
492    }
493
494    /// Raw attestation public key.
495    pub fn attestation_public_key(&self) -> &[u8] {
496        &self.attestation_public_key
497    }
498
499    /// Verify signature against quote using the attestation public key.
500    ///
501    /// The passed `data` must cover the Quote Header and the Report Data.
502    pub fn verify_quote_signature(&'a self, data: &[u8]) -> Result<&'a Self, Error> {
503        let sig = raw_ecdsa_sig_to_der(self.signature())?;
504        let mut pk = parse_ecdsa_pk(self.attestation_public_key())?;
505
506        let mut hash = [0u8; 32];
507        Md::hash(hash::Type::Sha256, data, &mut hash).map_err(|err| Error::Other(err.into()))?;
508        pk.verify(hash::Type::Sha256, &hash, &sig)
509            .map_err(|_| Error::VerificationFailed("quote signature is invalid".to_string()))?;
510
511        Ok(self)
512    }
513
514    /// Verify QE Report signature using the PCK public key.
515    pub fn verify_qe_report_signature(&self, pck_pk: &[u8]) -> Result<(), Error> {
516        self.qe
517            .verify_qe_report_signature(self.attestation_public_key(), pck_pk)
518    }
519}
520
521/// Convert IEEE P1363 ECDSA signature to RFC5480 ASN.1 representation.
522fn raw_ecdsa_sig_to_der(sig: &[u8]) -> Result<Vec<u8>, Error> {
523    if sig.len() % 2 != 0 {
524        return Err(Error::QuoteParseError(
525            "malformed ECDSA signature".to_string(),
526        ));
527    }
528
529    let (r_bytes, s_bytes) = sig.split_at(sig.len() / 2);
530    let r = num_bigint::BigUint::from_bytes_be(r_bytes);
531    let s = num_bigint::BigUint::from_bytes_be(s_bytes);
532
533    let der = yasna::construct_der(|writer| {
534        writer.write_sequence(|writer| {
535            writer.next().write_biguint(&r);
536            writer.next().write_biguint(&s);
537        })
538    });
539
540    Ok(der)
541}
542
543/// Parse Secp256r1 public key.
544fn parse_ecdsa_pk(pk: &[u8]) -> Result<Pk, Error> {
545    let mut pt = vec![0x4]; // Add SEC 1 tag (uncompressed).
546    pt.extend_from_slice(pk);
547
548    let group = EcGroup::new(EcGroupId::SecP256R1).map_err(|err| Error::Other(err.into()))?;
549    let pt = EcPoint::from_binary(&group, &pt).map_err(|err| Error::Other(err.into()))?;
550    Pk::public_from_ec_components(group, pt).map_err(|err| Error::Other(err.into()))
551}
552
553/// Quote signature verifier for ECDSA-P256 signatures.
554pub trait QuoteSignatureEcdsaP256Verifier {
555    /// Verify the platform certification data.
556    ///
557    /// The certification data is in `signature.certification_data()`.
558    ///
559    /// On success, should return the platform certification public key (PCK) in DER format.
560    fn verify_certification_data(
561        &mut self,
562        signature: &QuoteSignatureEcdsaP256,
563    ) -> Result<Vec<u8>, Error>;
564
565    /// Verify the quoting enclave.
566    fn verify_qe(&mut self, qe_report: &[u8], authentication_data: &[u8]) -> Result<(), Error>;
567}
568
569pub trait QuoteSignatureVerify<'a>: QuoteSignature<'a> {
570    type TrustRoot;
571
572    fn verify(&self, quote: &[u8], root_of_trust: Self::TrustRoot) -> Result<(), Error>;
573}
574
575impl<'a> QuoteSignatureVerify<'a> for QuoteSignatureEcdsaP256<'a> {
576    type TrustRoot = &'a mut dyn QuoteSignatureEcdsaP256Verifier;
577
578    fn verify(&self, quote: &[u8], verifier: Self::TrustRoot) -> Result<(), Error> {
579        let pck_pk = verifier.verify_certification_data(self)?;
580        self.verify_qe_report_signature(&pck_pk)?;
581        verifier.verify_qe(self.qe.qe_report(), self.qe.authentication_data())?;
582        self.verify_quote_signature(quote)?;
583        Ok(())
584    }
585}
586
587/// Certification data type.
588#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
589#[repr(u16)]
590pub enum CertificationDataType {
591    PpidCleartext = 1,
592    PpidEncryptedRsa2048 = 2,
593    PpidEncryptedRsa3072 = 3,
594    PckCertificate = 4,
595    PckCertificateChain = 5,
596    QeReport = 6,
597    PlatformManifest = 7,
598}
599
600/// Certification data trait.
601pub trait CertificationData<'a>: Sized {
602    /// Parse certification data of the given type from the given raw data.
603    fn parse(r#type: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self, Error>;
604}
605
606/// PPID certification data.
607#[derive(Clone, Debug, Hash, PartialEq, Eq)]
608pub struct CertificationDataPpid<'a> {
609    pub ppid: Cow<'a, [u8]>,
610    pub cpusvn: Cow<'a, [u8]>,
611    pub pcesvn: u16,
612    pub pceid: u16,
613}
614
615impl<'a> CertificationData<'a> for CertificationDataPpid<'a> {
616    fn parse(r#type: CertificationDataType, mut data: Cow<'a, [u8]>) -> Result<Self, Error> {
617        let ppid_len = match r#type {
618            CertificationDataType::PpidEncryptedRsa2048 => 256,
619            CertificationDataType::PpidEncryptedRsa3072 => 384,
620            _ => return Err(Error::UnexpectedCertificationData),
621        };
622
623        let ppid = data.take_prefix(ppid_len)?;
624        let cpusvn = data.take_prefix(CPUSVN_LEN)?;
625        let pcesvn = data
626            .take_prefix(mem::size_of::<u16>())
627            .map(|v| LittleEndian::read_u16(&v))?;
628        let pceid = data
629            .take_prefix(mem::size_of::<u16>())
630            .map(|v| LittleEndian::read_u16(&v))?;
631        if !data.is_empty() {
632            return Err(Error::MalformedCertificationData);
633        }
634
635        Ok(CertificationDataPpid {
636            ppid,
637            cpusvn,
638            pcesvn,
639            pceid,
640        })
641    }
642}
643
644/// PCK certificate chain certification data.
645#[derive(Clone, Debug, Hash, PartialEq, Eq)]
646pub struct CertificationDataPckCertificateChain<'a> {
647    pub certs: Vec<Cow<'a, str>>,
648}
649
650impl<'a> CertificationData<'a> for CertificationDataPckCertificateChain<'a> {
651    fn parse(r#type: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self, Error> {
652        if r#type != CertificationDataType::PckCertificateChain {
653            return Err(Error::UnexpectedCertificationData);
654        }
655
656        let mut data = match data {
657            Cow::Borrowed(s) => std::str::from_utf8(s)
658                .map(Cow::Borrowed)
659                .map_err(|_| Error::MalformedPCK)?,
660            Cow::Owned(s) => String::from_utf8(s)
661                .map(Cow::Owned)
662                .map_err(|_| Error::MalformedPCK)?,
663        };
664
665        let mut certs = vec![];
666        let mark = "-----END CERTIFICATE-----";
667        while let Some(pos) = data.find(mark) {
668            certs.push(data.take_prefix(pos + mark.len()).unwrap()); // Pos is always valid.
669            if let Some(start) = data.find("-") {
670                data.take_prefix(start).unwrap(); // Start is always valid.
671            }
672        }
673
674        Ok(CertificationDataPckCertificateChain { certs })
675    }
676}
677
678/// QE report certification data.
679#[derive(Debug)]
680pub struct CertificationDataQeReport<'a> {
681    qe_report: Cow<'a, [u8]>,
682    qe_report_signature: Cow<'a, [u8]>,
683    authentication_data: Cow<'a, [u8]>,
684    certification_data_type: CertificationDataType,
685    certification_data: Cow<'a, [u8]>,
686}
687
688impl<'a> CertificationDataQeReport<'a> {
689    fn parse(mut data: Cow<'a, [u8]>) -> Result<Self, Error> {
690        let qe_report = data.take_prefix(SGX_REPORT_BODY_LEN)?;
691        let qe_report_signature = data.take_prefix(ECDSA_P256_SIGNATURE_LEN)?;
692        let authdata_len = data
693            .take_prefix(mem::size_of::<u16>())
694            .map(|v| LittleEndian::read_u16(&v))?;
695        let authentication_data = data.take_prefix(authdata_len as _)?;
696
697        let cd_type = data
698            .take_prefix(mem::size_of::<u16>())
699            .map(|v| LittleEndian::read_u16(&v))?;
700        let certification_data_type =
701            CertificationDataType::from_u16(cd_type).ok_or_else(|| {
702                Error::QuoteParseError(format!("unknown certification data type: {}", cd_type))
703            })?;
704        let certdata_len = data
705            .take_prefix(mem::size_of::<u32>())
706            .map(|v| LittleEndian::read_u32(&v))?;
707        if certdata_len as usize != data.len() {
708            return Err(Error::QuoteParseError(
709                "invalid certification data length".to_string(),
710            ));
711        }
712
713        Ok(CertificationDataQeReport {
714            qe_report,
715            qe_report_signature,
716            authentication_data,
717            certification_data_type,
718            certification_data: data,
719        })
720    }
721
722    /// Raw QE report.
723    pub fn qe_report(&self) -> &[u8] {
724        &self.qe_report
725    }
726
727    /// Raw QE report signature.
728    pub fn qe_report_signature(&self) -> &[u8] {
729        &self.qe_report_signature
730    }
731
732    /// Raw authentication data.
733    pub fn authentication_data(&self) -> &[u8] {
734        &self.authentication_data
735    }
736
737    /// Parse inner certification data.
738    pub fn certification_data<T: CertificationData<'a>>(&self) -> Result<T, Error> {
739        T::parse(
740            self.certification_data_type,
741            self.certification_data.clone(),
742        )
743    }
744
745    /// Verify QE Report signature using the PCK public key.
746    pub fn verify_qe_report_signature(
747        &self,
748        attestation_pk: &[u8],
749        pck_pk: &[u8],
750    ) -> Result<(), Error> {
751        // Verify QE report signature using PCK public key.
752        let sig = raw_ecdsa_sig_to_der(self.qe_report_signature())?;
753        let mut hash = [0u8; 32];
754        Md::hash(hash::Type::Sha256, self.qe_report(), &mut hash)
755            .map_err(|err| Error::Other(err.into()))?;
756        let mut pck_pk = Pk::from_public_key(pck_pk).map_err(|err| Error::Other(err.into()))?;
757        pck_pk
758            .verify(mbedtls::hash::Type::Sha256, &hash, &sig)
759            .map_err(|_| Error::VerificationFailed("QE report signature is invalid".to_string()))?;
760
761        // Verify QE report data. First 32 bytes MUST be:
762        //   SHA-256(AttestationPublicKey || AuthenticationData)
763        // and the remaining 32 bytes MUST be zero.
764        let mut hash = [0u8; 32];
765        let mut sha256 = Md::new(hash::Type::Sha256).map_err(|err| Error::Other(err.into()))?;
766        sha256
767            .update(attestation_pk)
768            .map_err(|err| Error::Other(err.into()))?;
769        sha256
770            .update(self.authentication_data())
771            .map_err(|err| Error::Other(err.into()))?;
772        sha256
773            .finish(&mut hash)
774            .map_err(|err| Error::Other(err.into()))?;
775
776        let mut qe_report = Vec::with_capacity(SgxReport::UNPADDED_SIZE);
777        qe_report.extend(self.qe_report());
778        qe_report.resize_with(SgxReport::UNPADDED_SIZE, Default::default);
779        let qe_report = SgxReport::try_copy_from(&qe_report).ok_or(Error::MalformedQEReport)?;
780
781        if qe_report.reportdata[0..32] != hash {
782            return Err(Error::VerificationFailed(
783                "QE report data does not match expected value".to_string(),
784            ));
785        }
786        if qe_report.reportdata[32..64] != [0; 32] {
787            return Err(Error::VerificationFailed(
788                "QE report data does not match expected value".to_string(),
789            ));
790        }
791
792        Ok(())
793    }
794}
795
796/// Quoting Enclave ECDSA P-256 verifier.
797pub struct QeEcdsaP256Verifier {
798    ts: DateTime<Utc>,
799    tcb_info: TCBInfo,
800    qe_identity: QEIdentity,
801    tdx_comp_svn: Option<[u32; 16]>,
802    tcb_level: Option<TCBLevel>,
803}
804
805impl QeEcdsaP256Verifier {
806    /// Create a new verifier.
807    pub fn new(
808        tcb_info: TCBInfo,
809        qe_identity: QEIdentity,
810        tdx_comp_svn: Option<[u32; 16]>,
811        ts: DateTime<Utc>,
812    ) -> Self {
813        Self {
814            ts,
815            tcb_info,
816            qe_identity,
817            tdx_comp_svn,
818            tcb_level: None,
819        }
820    }
821
822    /// Get the TCB level.
823    /// This will return `None` if the quote has not been verified yet.
824    pub fn tcb_level(&self) -> Option<TCBLevel> {
825        self.tcb_level.clone()
826    }
827}
828
829impl QuoteSignatureEcdsaP256Verifier for QeEcdsaP256Verifier {
830    fn verify_certification_data(
831        &mut self,
832        signature: &QuoteSignatureEcdsaP256,
833    ) -> Result<Vec<u8>, Error> {
834        // Only PCK certificate chain is supported as certification data.
835        let certs = signature
836            .qe
837            .certification_data::<CertificationDataPckCertificateChain>()?
838            .certs;
839        if certs.len() != 3 {
840            return Err(Error::UnexpectedCertificateChain);
841        }
842
843        // Verify certificate chain.
844        let mut cert_chain = MbedtlsList::new();
845        for raw_cert in &certs {
846            let raw_cert = CString::new(raw_cert.as_ref()).map_err(|_| Error::MalformedPCK)?;
847            let cert = Certificate::from_pem(raw_cert.as_bytes_with_nul())
848                .map_err(|_| Error::MalformedPCK)?;
849            cert_chain.push(cert);
850        }
851        Certificate::verify_with_callback(
852            &cert_chain,
853            &PCS_TRUST_ROOT,
854            None,
855            None,
856            x509_custom_ts_verify_cb(self.ts),
857        )
858        .map_err(|_| Error::VerificationFailed("PCK certificate chain is invalid".to_string()))?;
859
860        // Extract TCB parameters from the PCK certificate.
861        let mut pck_cert = cert_chain.pop_front().unwrap();
862
863        let sgx_extensions = pck_cert
864            .extensions()
865            .map_err(|_| Error::MalformedPCK)?
866            .into_iter()
867            .find(|ext| ext.oid.as_ref() == PCK_SGX_EXTENSIONS_OID)
868            .ok_or(Error::TCBVerificationFailed)?;
869        let mut fmspc: Option<Vec<u8>> = None;
870        let mut tcb_comp_svn: Option<[u32; 16]> = None;
871        let mut pcesvn: Option<u32> = None;
872        yasna::parse_der(&sgx_extensions.value, |reader| {
873            reader.read_sequence_of(|reader| {
874                reader.read_sequence(|reader| {
875                    match reader.next().read_oid()?.as_ref() {
876                        PCK_SGX_EXTENSIONS_FMSPC_OID => {
877                            // FMSPC
878                            let raw_fmspc = reader.next().read_bytes()?;
879                            if raw_fmspc.len() != 6 {
880                                return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
881                            }
882                            fmspc = Some(raw_fmspc);
883                        }
884                        PCK_SGX_EXTENSIONS_TCB_OID => {
885                            // TCB
886                            reader.next().read_sequence_of(|reader| {
887                                reader.read_sequence(|reader| {
888                                    let comp_id =
889                                        *reader.next().read_oid()?.as_ref().last().unwrap();
890                                    if (1..=16).contains(&comp_id) {
891                                        // TCB Component SVNs
892                                        tcb_comp_svn.get_or_insert([0; 16])
893                                            [(comp_id - 1) as usize] = reader.next().read_u32()?;
894                                    } else if comp_id == 17 {
895                                        // PCESVN
896                                        pcesvn = Some(reader.next().read_u32()?);
897                                    } else if comp_id == 18 {
898                                        // CPUSVN
899                                        reader.next().read_bytes()?;
900                                    }
901                                    Ok(())
902                                })
903                            })?;
904                        }
905                        _ => {
906                            reader.next().read_der()?;
907                        }
908                    }
909
910                    Ok(())
911                })
912            })
913        })
914        .map_err(|_| Error::MalformedPCK)?;
915        if fmspc.is_none() || tcb_comp_svn.is_none() || pcesvn.is_none() {
916            return Err(Error::MalformedPCK);
917        }
918
919        // Verify TCB level.
920        let tcb_level = self.tcb_info.verify(
921            &fmspc.unwrap(),
922            &tcb_comp_svn.unwrap(),
923            self.tdx_comp_svn.as_ref(),
924            pcesvn.unwrap(),
925        )?;
926        self.tcb_level = Some(tcb_level);
927
928        // Extract PCK public key.
929        let pck_pk = pck_cert
930            .public_key_mut()
931            .write_public_der_vec()
932            .map_err(|_| Error::MalformedPCK)?;
933
934        Ok(pck_pk)
935    }
936
937    fn verify_qe(&mut self, qe_report: &[u8], _authentication_data: &[u8]) -> Result<(), Error> {
938        let mut report = Vec::with_capacity(SgxReport::UNPADDED_SIZE);
939        report.extend(qe_report);
940        report.resize_with(SgxReport::UNPADDED_SIZE, Default::default);
941
942        let report = SgxReport::try_copy_from(&report).ok_or(Error::MalformedQEReport)?;
943        self.qe_identity.verify(&report)?;
944
945        Ok(())
946    }
947}