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::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)?;
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(&self, tcb_info: TCBInfo, qe_identity: QEIdentity) -> Result<TCBLevel, Error> {
254        let tdx_comp_svn = self.report_body.tdx_comp_svn();
255
256        let mut verifier: QeEcdsaP256Verifier =
257            QeEcdsaP256Verifier::new(tcb_info, qe_identity, tdx_comp_svn);
258        self.signature.verify(&self.signed_data, &mut verifier)?;
259
260        Ok(verifier.tcb_level().unwrap())
261    }
262}
263
264/// An enclave quote header.
265#[derive(Debug)]
266pub enum Header<'a> {
267    V3 {
268        attestation_key_type: AttestationKeyType,
269        qe_svn: u16,
270        pce_svn: u16,
271        qe_vendor_id: Cow<'a, [u8]>,
272        user_data: Cow<'a, [u8]>,
273    },
274
275    V4 {
276        attestation_key_type: AttestationKeyType,
277        tee_type: TeeType,
278        qe_vendor_id: Cow<'a, [u8]>,
279        user_data: Cow<'a, [u8]>,
280    },
281}
282
283impl<'a> Header<'a> {
284    /// Quote header version.
285    pub fn version(&self) -> u16 {
286        match self {
287            Self::V3 { .. } => QUOTE_VERSION_3,
288            Self::V4 { .. } => QUOTE_VERSION_4,
289        }
290    }
291
292    /// Attestation key type.
293    pub fn attestation_key_type(&self) -> AttestationKeyType {
294        match self {
295            Self::V3 {
296                attestation_key_type,
297                ..
298            } => *attestation_key_type,
299            Self::V4 {
300                attestation_key_type,
301                ..
302            } => *attestation_key_type,
303        }
304    }
305
306    /// TEE type the quote is for.
307    pub fn tee_type(&self) -> TeeType {
308        match self {
309            Self::V3 { .. } => TeeType::SGX,
310            Self::V4 { tee_type, .. } => *tee_type,
311        }
312    }
313
314    /// Quoting Enclave (QE) vendor identifier.
315    pub fn qe_vendor_id(&self) -> &[u8] {
316        match self {
317            Self::V3 { qe_vendor_id, .. } => qe_vendor_id,
318            Self::V4 { qe_vendor_id, .. } => qe_vendor_id,
319        }
320    }
321
322    /// Length of the report body field.
323    pub fn report_body_len(&self) -> usize {
324        match self.tee_type() {
325            TeeType::SGX => SGX_REPORT_BODY_LEN,
326            TeeType::TDX => TDX_REPORT_BODY_LEN,
327        }
328    }
329}
330
331/// TEE type.
332#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
333#[repr(u32)]
334pub enum TeeType {
335    SGX = 0x00000000,
336    TDX = 0x00000081,
337}
338
339/// Attestation key type.
340#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
341#[repr(u16)]
342pub enum AttestationKeyType {
343    EcdsaP256 = 2,
344}
345
346/// Report body.
347#[derive(Debug)]
348pub enum ReportBody {
349    Sgx(SgxReport),
350    Tdx(TdReport),
351}
352
353impl ReportBody {
354    /// Parse the report body.
355    pub fn parse(tee_type: TeeType, raw: &[u8]) -> Result<Self, Error> {
356        match tee_type {
357            TeeType::SGX => {
358                // Parse SGX report body.
359                let mut report_body = Vec::with_capacity(SgxReport::UNPADDED_SIZE);
360                report_body.extend(raw);
361                report_body.resize_with(SgxReport::UNPADDED_SIZE, Default::default);
362                let report =
363                    SgxReport::try_copy_from(&report_body).ok_or(Error::MalformedReport)?;
364
365                Ok(Self::Sgx(report))
366            }
367            TeeType::TDX => {
368                // Parse TDX TD report body.
369                let report = TdReport::parse(raw)?;
370
371                Ok(Self::Tdx(report))
372            }
373        }
374    }
375
376    /// TDX TEE Component SVNs.
377    ///
378    /// Returns `None` in case of a non-TDX report body.
379    pub fn tdx_comp_svn(&self) -> Option<[u32; 16]> {
380        match self {
381            Self::Sgx(_) => None,
382            Self::Tdx(report) => Some(
383                report
384                    .tee_tcb_svn
385                    .iter()
386                    .map(|x| *x as u32)
387                    .collect::<Vec<u32>>()
388                    .try_into()
389                    .unwrap(),
390            ),
391        }
392    }
393
394    /// Whether the report indicates a debug TEE.
395    pub fn is_debug(&self) -> bool {
396        match self {
397            Self::Sgx(report) => report.attributes.flags.contains(AttributesFlags::DEBUG),
398            Self::Tdx(report) => report.td_attributes.contains(TdAttributes::DEBUG),
399        }
400    }
401
402    /// Converts this report into an enclave identity.
403    pub fn as_enclave_identity(&self) -> EnclaveIdentity {
404        match self {
405            Self::Sgx(report) => EnclaveIdentity {
406                mr_enclave: MrEnclave::from(report.mrenclave.to_vec()),
407                mr_signer: MrSigner::from(report.mrsigner.to_vec()),
408            },
409            Self::Tdx(report) => report.as_enclave_identity(),
410        }
411    }
412
413    /// Data contained in the report.
414    pub fn report_data(&self) -> Vec<u8> {
415        match self {
416            Self::Sgx(report) => report.reportdata.to_vec(),
417            Self::Tdx(report) => report.report_data.to_vec(),
418        }
419    }
420}
421
422/// Quote signature trait.
423pub trait QuoteSignature<'a>: Sized {
424    /// Parse the quote signature from the passed data.
425    fn parse(version: u16, data: Cow<'a, [u8]>) -> Result<Self, Error>;
426}
427
428/// ECDSA-P256 quote signature.
429#[derive(Debug)]
430pub struct QuoteSignatureEcdsaP256<'a> {
431    signature: Cow<'a, [u8]>,
432    attestation_public_key: Cow<'a, [u8]>,
433
434    qe: CertificationDataQeReport<'a>,
435}
436
437impl<'a> QuoteSignature<'a> for QuoteSignatureEcdsaP256<'a> {
438    fn parse(version: u16, mut data: Cow<'a, [u8]>) -> Result<Self, Error> {
439        let sig_len = data
440            .take_prefix(mem::size_of::<u32>())
441            .map(|v| LittleEndian::read_u32(&v))?;
442        if sig_len as usize != data.len() {
443            return Err(Error::QuoteParseError(
444                "unexpected trailing data after signature".to_string(),
445            ));
446        }
447        let signature = data.take_prefix(ECDSA_P256_SIGNATURE_LEN)?;
448        let attestation_public_key = data.take_prefix(ECDSA_P256_PUBLIC_KEY_LEN)?;
449
450        // In version 4 quotes, there is an intermediate certification data tuple.
451        if version == QUOTE_VERSION_4 {
452            let cd_type = data
453                .take_prefix(mem::size_of::<u16>())
454                .map(|v| LittleEndian::read_u16(&v))?;
455            let certification_data_type =
456                CertificationDataType::from_u16(cd_type).ok_or_else(|| {
457                    Error::QuoteParseError(format!("unknown certification data type: {}", cd_type))
458                })?;
459            let certdata_len = data
460                .take_prefix(mem::size_of::<u32>())
461                .map(|v| LittleEndian::read_u32(&v))?;
462            if certdata_len as usize != data.len() {
463                return Err(Error::QuoteParseError(
464                    "invalid certification data length".to_string(),
465                ));
466            }
467
468            if certification_data_type != CertificationDataType::QeReport {
469                return Err(Error::UnexpectedCertificationData);
470            }
471        }
472
473        let qe = CertificationDataQeReport::parse(data)?;
474
475        Ok(QuoteSignatureEcdsaP256 {
476            signature,
477            attestation_public_key,
478            qe,
479        })
480    }
481}
482
483impl<'a> QuoteSignatureEcdsaP256<'a> {
484    /// Raw signature.
485    pub fn signature(&self) -> &[u8] {
486        &self.signature
487    }
488
489    /// Raw attestation public key.
490    pub fn attestation_public_key(&self) -> &[u8] {
491        &self.attestation_public_key
492    }
493
494    /// Verify signature against quote using the attestation public key.
495    ///
496    /// The passed `data` must cover the Quote Header and the Report Data.
497    pub fn verify_quote_signature(&'a self, data: &[u8]) -> Result<&'a Self, Error> {
498        let sig = raw_ecdsa_sig_to_der(self.signature())?;
499        let mut pk = parse_ecdsa_pk(self.attestation_public_key())?;
500
501        let mut hash = [0u8; 32];
502        Md::hash(hash::Type::Sha256, data, &mut hash).map_err(|err| Error::Other(err.into()))?;
503        pk.verify(hash::Type::Sha256, &hash, &sig)
504            .map_err(|_| Error::VerificationFailed("quote signature is invalid".to_string()))?;
505
506        Ok(self)
507    }
508
509    /// Verify QE Report signature using the PCK public key.
510    pub fn verify_qe_report_signature(&self, pck_pk: &[u8]) -> Result<(), Error> {
511        self.qe
512            .verify_qe_report_signature(self.attestation_public_key(), pck_pk)
513    }
514}
515
516/// Convert IEEE P1363 ECDSA signature to RFC5480 ASN.1 representation.
517fn raw_ecdsa_sig_to_der(sig: &[u8]) -> Result<Vec<u8>, Error> {
518    if sig.len() % 2 != 0 {
519        return Err(Error::QuoteParseError(
520            "malformed ECDSA signature".to_string(),
521        ));
522    }
523
524    let (r_bytes, s_bytes) = sig.split_at(sig.len() / 2);
525    let r = num_bigint::BigUint::from_bytes_be(r_bytes);
526    let s = num_bigint::BigUint::from_bytes_be(s_bytes);
527
528    let der = yasna::construct_der(|writer| {
529        writer.write_sequence(|writer| {
530            writer.next().write_biguint(&r);
531            writer.next().write_biguint(&s);
532        })
533    });
534
535    Ok(der)
536}
537
538/// Parse Secp256r1 public key.
539fn parse_ecdsa_pk(pk: &[u8]) -> Result<Pk, Error> {
540    let mut pt = vec![0x4]; // Add SEC 1 tag (uncompressed).
541    pt.extend_from_slice(pk);
542
543    let group = EcGroup::new(EcGroupId::SecP256R1).map_err(|err| Error::Other(err.into()))?;
544    let pt = EcPoint::from_binary(&group, &pt).map_err(|err| Error::Other(err.into()))?;
545    Pk::public_from_ec_components(group, pt).map_err(|err| Error::Other(err.into()))
546}
547
548/// Quote signature verifier for ECDSA-P256 signatures.
549pub trait QuoteSignatureEcdsaP256Verifier {
550    /// Verify the platform certification data.
551    ///
552    /// The certification data is in `signature.certification_data()`.
553    ///
554    /// On success, should return the platform certification public key (PCK) in DER format.
555    fn verify_certification_data(
556        &mut self,
557        signature: &QuoteSignatureEcdsaP256,
558    ) -> Result<Vec<u8>, Error>;
559
560    /// Verify the quoting enclave.
561    fn verify_qe(&mut self, qe_report: &[u8], authentication_data: &[u8]) -> Result<(), Error>;
562}
563
564pub trait QuoteSignatureVerify<'a>: QuoteSignature<'a> {
565    type TrustRoot;
566
567    fn verify(&self, quote: &[u8], root_of_trust: Self::TrustRoot) -> Result<(), Error>;
568}
569
570impl<'a> QuoteSignatureVerify<'a> for QuoteSignatureEcdsaP256<'a> {
571    type TrustRoot = &'a mut dyn QuoteSignatureEcdsaP256Verifier;
572
573    fn verify(&self, quote: &[u8], verifier: Self::TrustRoot) -> Result<(), Error> {
574        let pck_pk = verifier.verify_certification_data(self)?;
575        self.verify_qe_report_signature(&pck_pk)?;
576        verifier.verify_qe(self.qe.qe_report(), self.qe.authentication_data())?;
577        self.verify_quote_signature(quote)?;
578        Ok(())
579    }
580}
581
582/// Certification data type.
583#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
584#[repr(u16)]
585pub enum CertificationDataType {
586    PpidCleartext = 1,
587    PpidEncryptedRsa2048 = 2,
588    PpidEncryptedRsa3072 = 3,
589    PckCertificate = 4,
590    PckCertificateChain = 5,
591    QeReport = 6,
592    PlatformManifest = 7,
593}
594
595/// Certification data trait.
596pub trait CertificationData<'a>: Sized {
597    /// Parse certification data of the given type from the given raw data.
598    fn parse(r#type: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self, Error>;
599}
600
601/// PPID certification data.
602#[derive(Clone, Debug, Hash, PartialEq, Eq)]
603pub struct CertificationDataPpid<'a> {
604    pub ppid: Cow<'a, [u8]>,
605    pub cpusvn: Cow<'a, [u8]>,
606    pub pcesvn: u16,
607    pub pceid: u16,
608}
609
610impl<'a> CertificationData<'a> for CertificationDataPpid<'a> {
611    fn parse(r#type: CertificationDataType, mut data: Cow<'a, [u8]>) -> Result<Self, Error> {
612        let ppid_len = match r#type {
613            CertificationDataType::PpidEncryptedRsa2048 => 256,
614            CertificationDataType::PpidEncryptedRsa3072 => 384,
615            _ => return Err(Error::UnexpectedCertificationData),
616        };
617
618        let ppid = data.take_prefix(ppid_len)?;
619        let cpusvn = data.take_prefix(CPUSVN_LEN)?;
620        let pcesvn = data
621            .take_prefix(mem::size_of::<u16>())
622            .map(|v| LittleEndian::read_u16(&v))?;
623        let pceid = data
624            .take_prefix(mem::size_of::<u16>())
625            .map(|v| LittleEndian::read_u16(&v))?;
626        if !data.is_empty() {
627            return Err(Error::MalformedCertificationData);
628        }
629
630        Ok(CertificationDataPpid {
631            ppid,
632            cpusvn,
633            pcesvn,
634            pceid,
635        })
636    }
637}
638
639/// PCK certificate chain certification data.
640#[derive(Clone, Debug, Hash, PartialEq, Eq)]
641pub struct CertificationDataPckCertificateChain<'a> {
642    pub certs: Vec<Cow<'a, str>>,
643}
644
645impl<'a> CertificationData<'a> for CertificationDataPckCertificateChain<'a> {
646    fn parse(r#type: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self, Error> {
647        if r#type != CertificationDataType::PckCertificateChain {
648            return Err(Error::UnexpectedCertificationData);
649        }
650
651        let mut data = match data {
652            Cow::Borrowed(s) => std::str::from_utf8(s)
653                .map(Cow::Borrowed)
654                .map_err(|_| Error::MalformedPCK)?,
655            Cow::Owned(s) => String::from_utf8(s)
656                .map(Cow::Owned)
657                .map_err(|_| Error::MalformedPCK)?,
658        };
659
660        let mut certs = vec![];
661        let mark = "-----END CERTIFICATE-----";
662        while let Some(pos) = data.find(mark) {
663            certs.push(data.take_prefix(pos + mark.len()).unwrap()); // Pos is always valid.
664            if let Some(start) = data.find("-") {
665                data.take_prefix(start).unwrap(); // Start is always valid.
666            }
667        }
668
669        Ok(CertificationDataPckCertificateChain { certs })
670    }
671}
672
673/// QE report certification data.
674#[derive(Debug)]
675pub struct CertificationDataQeReport<'a> {
676    qe_report: Cow<'a, [u8]>,
677    qe_report_signature: Cow<'a, [u8]>,
678    authentication_data: Cow<'a, [u8]>,
679    certification_data_type: CertificationDataType,
680    certification_data: Cow<'a, [u8]>,
681}
682
683impl<'a> CertificationDataQeReport<'a> {
684    fn parse(mut data: Cow<'a, [u8]>) -> Result<Self, Error> {
685        let qe_report = data.take_prefix(SGX_REPORT_BODY_LEN)?;
686        let qe_report_signature = data.take_prefix(ECDSA_P256_SIGNATURE_LEN)?;
687        let authdata_len = data
688            .take_prefix(mem::size_of::<u16>())
689            .map(|v| LittleEndian::read_u16(&v))?;
690        let authentication_data = data.take_prefix(authdata_len as _)?;
691
692        let cd_type = data
693            .take_prefix(mem::size_of::<u16>())
694            .map(|v| LittleEndian::read_u16(&v))?;
695        let certification_data_type =
696            CertificationDataType::from_u16(cd_type).ok_or_else(|| {
697                Error::QuoteParseError(format!("unknown certification data type: {}", cd_type))
698            })?;
699        let certdata_len = data
700            .take_prefix(mem::size_of::<u32>())
701            .map(|v| LittleEndian::read_u32(&v))?;
702        if certdata_len as usize != data.len() {
703            return Err(Error::QuoteParseError(
704                "invalid certification data length".to_string(),
705            ));
706        }
707
708        Ok(CertificationDataQeReport {
709            qe_report,
710            qe_report_signature,
711            authentication_data,
712            certification_data_type,
713            certification_data: data,
714        })
715    }
716
717    /// Raw QE report.
718    pub fn qe_report(&self) -> &[u8] {
719        &self.qe_report
720    }
721
722    /// Raw QE report signature.
723    pub fn qe_report_signature(&self) -> &[u8] {
724        &self.qe_report_signature
725    }
726
727    /// Raw authentication data.
728    pub fn authentication_data(&self) -> &[u8] {
729        &self.authentication_data
730    }
731
732    /// Inner certification data type.
733    pub fn certification_data_type(&self) -> CertificationDataType {
734        self.certification_data_type
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    tcb_info: TCBInfo,
799    qe_identity: QEIdentity,
800    tdx_comp_svn: Option<[u32; 16]>,
801    tcb_level: Option<TCBLevel>,
802}
803
804impl QeEcdsaP256Verifier {
805    /// Create a new verifier.
806    pub fn new(
807        tcb_info: TCBInfo,
808        qe_identity: QEIdentity,
809        tdx_comp_svn: Option<[u32; 16]>,
810    ) -> Self {
811        Self {
812            tcb_info,
813            qe_identity,
814            tdx_comp_svn,
815            tcb_level: None,
816        }
817    }
818
819    /// Get the TCB level.
820    /// This will return `None` if the quote has not been verified yet.
821    pub fn tcb_level(&self) -> Option<TCBLevel> {
822        self.tcb_level.clone()
823    }
824}
825
826impl QuoteSignatureEcdsaP256Verifier for QeEcdsaP256Verifier {
827    fn verify_certification_data(
828        &mut self,
829        signature: &QuoteSignatureEcdsaP256,
830    ) -> Result<Vec<u8>, Error> {
831        // Only PCK certificate chain is supported as certification data.
832        let certs = signature
833            .qe
834            .certification_data::<CertificationDataPckCertificateChain>()?
835            .certs;
836        if certs.len() != 3 {
837            return Err(Error::UnexpectedCertificateChain);
838        }
839
840        // Verify certificate chain.
841        let mut cert_chain = MbedtlsList::new();
842        for raw_cert in &certs {
843            let raw_cert = CString::new(raw_cert.as_ref()).map_err(|_| Error::MalformedPCK)?;
844            let cert = Certificate::from_pem(raw_cert.as_bytes_with_nul())
845                .map_err(|_| Error::MalformedPCK)?;
846            cert_chain.push(cert);
847        }
848        // TODO: Specify current timestamp.
849        Certificate::verify(&cert_chain, &PCS_TRUST_ROOT, None, None).map_err(|_| {
850            Error::VerificationFailed("PCK certificate chain is invalid".to_string())
851        })?;
852
853        // Extract TCB parameters from the PCK certificate.
854        let mut pck_cert = cert_chain.pop_front().unwrap();
855
856        let sgx_extensions = pck_cert
857            .extensions()
858            .map_err(|_| Error::MalformedPCK)?
859            .into_iter()
860            .find(|ext| ext.oid.as_ref() == PCK_SGX_EXTENSIONS_OID)
861            .ok_or(Error::TCBVerificationFailed)?;
862        let mut fmspc: Option<Vec<u8>> = None;
863        let mut tcb_comp_svn: Option<[u32; 16]> = None;
864        let mut pcesvn: Option<u32> = None;
865        yasna::parse_der(&sgx_extensions.value, |reader| {
866            reader.read_sequence_of(|reader| {
867                reader.read_sequence(|reader| {
868                    match reader.next().read_oid()?.as_ref() {
869                        PCK_SGX_EXTENSIONS_FMSPC_OID => {
870                            // FMSPC
871                            let raw_fmspc = reader.next().read_bytes()?;
872                            if raw_fmspc.len() != 6 {
873                                return Err(yasna::ASN1Error::new(yasna::ASN1ErrorKind::Invalid));
874                            }
875                            fmspc = Some(raw_fmspc);
876                        }
877                        PCK_SGX_EXTENSIONS_TCB_OID => {
878                            // TCB
879                            reader.next().read_sequence_of(|reader| {
880                                reader.read_sequence(|reader| {
881                                    let comp_id =
882                                        *reader.next().read_oid()?.as_ref().last().unwrap();
883                                    if (1..=16).contains(&comp_id) {
884                                        // TCB Component SVNs
885                                        tcb_comp_svn.get_or_insert([0; 16])
886                                            [(comp_id - 1) as usize] = reader.next().read_u32()?;
887                                    } else if comp_id == 17 {
888                                        // PCESVN
889                                        pcesvn = Some(reader.next().read_u32()?);
890                                    } else if comp_id == 18 {
891                                        // CPUSVN
892                                        reader.next().read_bytes()?;
893                                    }
894                                    Ok(())
895                                })
896                            })?;
897                        }
898                        _ => {
899                            reader.next().read_der()?;
900                        }
901                    }
902
903                    Ok(())
904                })
905            })
906        })
907        .map_err(|_| Error::MalformedPCK)?;
908        if fmspc.is_none() || tcb_comp_svn.is_none() || pcesvn.is_none() {
909            return Err(Error::MalformedPCK);
910        }
911
912        // Verify TCB level.
913        let tcb_level = self.tcb_info.verify(
914            &fmspc.unwrap(),
915            &tcb_comp_svn.unwrap(),
916            self.tdx_comp_svn.as_ref(),
917            pcesvn.unwrap(),
918        )?;
919        self.tcb_level = Some(tcb_level);
920
921        // Extract PCK public key.
922        let pck_pk = pck_cert
923            .public_key_mut()
924            .write_public_der_vec()
925            .map_err(|_| Error::MalformedPCK)?;
926
927        Ok(pck_pk)
928    }
929
930    fn verify_qe(&mut self, qe_report: &[u8], _authentication_data: &[u8]) -> Result<(), Error> {
931        let mut report = Vec::with_capacity(SgxReport::UNPADDED_SIZE);
932        report.extend(qe_report);
933        report.resize_with(SgxReport::UNPADDED_SIZE, Default::default);
934
935        let report = SgxReport::try_copy_from(&report).ok_or(Error::MalformedQEReport)?;
936        self.qe_identity.verify(&report)?;
937
938        Ok(())
939    }
940}