dcap_ql/
quote.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
8use byteorder::{ByteOrder, LE};
9#[cfg(feature = "verify")]
10use mbedtls::ecp::{EcGroup, EcPoint};
11#[cfg(feature = "verify")]
12use mbedtls::pk::{Pk, EcGroupId};
13#[cfg(feature = "verify")]
14use mbedtls::hash::{self, Md};
15use num_traits::FromPrimitive;
16#[cfg(feature = "serde")]
17use serde::{Deserialize, Serialize};
18#[cfg(feature = "verify")]
19use sgx_isa::Report;
20use std::borrow::Cow;
21use std::mem;
22use anyhow::bail;
23
24// ====================================================
25// ================= TYPE DEFINITIONS =================
26// ====================================================
27
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub struct Quote<'a> {
30    header: QuoteHeader<'a>,
31    report_body: Cow<'a, [u8]>,
32    signature: Cow<'a, [u8]>,
33}
34
35#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
36pub enum QuoteHeader<'a> {
37    V3 {
38        attestation_key_type: Quote3AttestationKeyType,
39        qe3_svn: u16,
40        pce_svn: u16,
41        qe3_vendor_id: Cow<'a, [u8]>,
42        user_data: Cow<'a, [u8]>,
43    },
44}
45
46#[repr(u16)]
47#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
48#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
49pub enum Quote3AttestationKeyType {
50    EcdsaP256 = 2,
51    EcdsaP384 = 3,
52}
53
54pub const QE3_VENDOR_ID_INTEL: [u8; 16] = [
55    0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07,
56];
57
58pub type QeId<'a> = Cow<'a, [u8]>;
59
60pub struct Quote3SignatureEcdsaP256<'a> {
61    signature: Cow<'a, [u8]>,
62    attestation_public_key: Cow<'a, [u8]>,
63    qe3_report: Cow<'a, [u8]>,
64    qe3_signature: Cow<'a, [u8]>,
65    authentication_data: Cow<'a, [u8]>,
66    certification_data_type: CertificationDataType,
67    certification_data: Cow<'a, [u8]>,
68}
69
70#[repr(u16)]
71#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
72pub enum CertificationDataType {
73    PpidCleartext = 1,
74    PpidEncryptedRsa2048 = 2,
75    PpidEncryptedRsa3072 = 3,
76    PckCertificate = 4,
77    PckCertificateChain = 5,
78    EcdsaSignatureAuxiliaryData = 6,
79    PlatformManifest = 7,
80}
81
82#[derive(Clone, Debug, Hash, PartialEq, Eq)]
83pub struct Qe3CertDataPpid<'a> {
84    pub ppid: Cow<'a, [u8]>,
85    pub cpusvn: Cow<'a, [u8]>,
86    pub pcesvn: u16,
87    pub pceid: u16,
88}
89
90#[derive(Clone, Debug, Hash, PartialEq, Eq)]
91pub struct Qe3CertDataPckCertChain<'a> {
92    pub certs: Vec<Cow<'a, str>>,
93}
94
95pub type RawQe3CertData<'a> = Cow<'a, [u8]>;
96
97pub type Result<T> = ::std::result::Result<T, anyhow::Error>;
98
99// ===========================================
100// ================= PARSING =================
101// ===========================================
102
103trait TakePrefix: Sized {
104    fn take_prefix(&mut self, mid: usize) -> Result<Self>;
105}
106
107impl<'a, T: 'a> TakePrefix for &'a [T] {
108    fn take_prefix(&mut self, mid: usize) -> Result<Self> {
109        if let (Some(prefix), Some(rest)) = (self.get(..mid), self.get(mid..)) {
110            *self = rest;
111            Ok(prefix)
112        } else {
113            bail!("Unexpected end of quote")
114        }
115    }
116}
117
118impl<'a, T: 'a + Clone> TakePrefix for Cow<'a, [T]> {
119    fn take_prefix(&mut self, mid: usize) -> Result<Self> {
120        if mid <= self.len() {
121            match self {
122                &mut Cow::Borrowed(ref mut slice) => slice.take_prefix(mid).map(Cow::Borrowed),
123                &mut Cow::Owned(ref mut vec) => {
124                    let rest = vec.split_off(mid);
125                    Ok(Cow::Owned(mem::replace(vec, rest)))
126                },
127            }
128        } else {
129            bail!("Unexpected end of quote")
130        }
131    }
132}
133
134impl<'a> TakePrefix for &'a str {
135    fn take_prefix(&mut self, mid: usize) -> Result<Self> {
136        if let (Some(prefix), Some(rest)) = (self.get(..mid), self.get(mid..)) {
137            *self = rest;
138            Ok(prefix)
139        } else {
140            bail!("Unexpected end of quote")
141        }
142    }
143}
144
145impl<'a> TakePrefix for Cow<'a, str> {
146    fn take_prefix(&mut self, mid: usize) -> Result<Self> {
147        if mid <= self.len() {
148            match self {
149                &mut Cow::Borrowed(ref mut slice) => slice.take_prefix(mid).map(Cow::Borrowed),
150                &mut Cow::Owned(ref mut vec) => {
151                    let rest = vec.split_off(mid);
152                    Ok(Cow::Owned(mem::replace(vec, rest)))
153                },
154            }
155        } else {
156            bail!("Unexpected end of quote")
157        }
158    }
159}
160
161pub trait Quote3Signature<'a>: Sized {
162    fn parse(type_: Quote3AttestationKeyType, data: Cow<'a, [u8]>) -> Result<Self>;
163}
164
165pub trait Qe3CertData<'a>: Sized {
166    fn parse(type_: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self>;
167}
168
169const ECDSA_P256_SIGNATURE_LEN: usize = 64;
170const ECDSA_P256_PUBLIC_KEY_LEN: usize = 64;
171const QE3_VENDOR_ID_LEN: usize = 16;
172const QE3_USER_DATA_LEN: usize = 20;
173const REPORT_BODY_LEN: usize = 384;
174const CPUSVN_LEN: usize = 16;
175const QUOTE_VERSION_3: u16 = 3;
176
177impl<'a> Quote<'a> {
178    pub fn parse<T: Into<Cow<'a, [u8]>>>(quote: T) -> Result<Quote<'a>> {
179        let mut quote = quote.into();
180
181        let version = quote.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
182        if version != QUOTE_VERSION_3 {
183            bail!("Unknown quote version: {}", version);
184        }
185        let att_key_type = quote.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
186        let attestation_key_type = Quote3AttestationKeyType::from_u16(att_key_type)
187            .ok_or_else(|| format_err!("Unknown attestation key type: {}", att_key_type))?;
188        let reserved = quote.take_prefix(mem::size_of::<u32>()).map(|v| LE::read_u32(&v))?;
189        if reserved != 0 {
190            bail!("Data in reserved field: {:08x}", reserved);
191        }
192        let qe3_svn = quote.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
193        let pce_svn = quote.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
194        let qe3_vendor_id = quote.take_prefix(QE3_VENDOR_ID_LEN)?;
195        let user_data = quote.take_prefix(QE3_USER_DATA_LEN)?;
196        let report_body = quote.take_prefix(REPORT_BODY_LEN)?;
197
198        Ok(Quote {
199            header: QuoteHeader::V3 {
200                attestation_key_type,
201                qe3_svn,
202                pce_svn,
203                qe3_vendor_id,
204                user_data,
205            },
206            report_body,
207            signature: quote,
208        })
209    }
210}
211
212/// Convert IEEE P1363 ECDSA signature to RFC5480 ASN.1 representation.
213#[cfg(feature = "verify")]
214fn get_ecdsa_sig_der(sig: &[u8]) -> Result<Vec<u8>> {
215    if sig.len() % 2 != 0 {
216        bail!("sig not even: {}", sig.len());
217    }
218
219    let (r_bytes, s_bytes) = sig.split_at(sig.len() / 2);
220    let r = num::BigUint::from_bytes_be(r_bytes);
221    let s = num::BigUint::from_bytes_be(s_bytes);
222
223    let der = yasna::construct_der(|writer| {
224        writer.write_sequence(|writer| {
225            writer.next().write_biguint(&r);
226            writer.next().write_biguint(&s);
227        })
228    });
229
230    Ok(der)
231}
232
233impl<'a> Quote3Signature<'a> for Quote3SignatureEcdsaP256<'a> {
234    fn parse(type_: Quote3AttestationKeyType, mut data: Cow<'a, [u8]>) -> Result<Self> {
235        if type_ != Quote3AttestationKeyType::EcdsaP256 {
236            bail!("Invalid attestation key type: {:?}", type_)
237        }
238
239        let sig_len = data.take_prefix(mem::size_of::<u32>()).map(|v| LE::read_u32(&v))?;
240        if sig_len as usize != data.len() {
241            bail!(
242                "Invalid signature length. Got {}, expected {}",
243                data.len(),
244                sig_len
245            );
246        }
247        let signature = data.take_prefix(ECDSA_P256_SIGNATURE_LEN)?;
248        let attestation_public_key = data.take_prefix(ECDSA_P256_PUBLIC_KEY_LEN)?;
249        let qe3_report = data.take_prefix(REPORT_BODY_LEN)?;
250        let qe3_signature = data.take_prefix(ECDSA_P256_SIGNATURE_LEN)?;
251        let authdata_len = data.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
252        let authentication_data = data.take_prefix(authdata_len as _)?;
253        let cd_type = data.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
254        let certification_data_type = CertificationDataType::from_u16(cd_type)
255            .ok_or_else(|| format_err!("Unknown certification data type: {}", cd_type))?;
256        let certdata_len = data.take_prefix(mem::size_of::<u32>()).map(|v| LE::read_u32(&v))?;
257        if certdata_len as usize != data.len() {
258            bail!(
259                "Invalid certification data length. Got {}, expected {}",
260                data.len(),
261                certdata_len
262            );
263        }
264
265        Ok(Quote3SignatureEcdsaP256 {
266            signature,
267            attestation_public_key,
268            qe3_report,
269            qe3_signature,
270            authentication_data,
271            certification_data_type,
272            certification_data: data,
273        })
274    }
275}
276
277impl<'a> Qe3CertData<'a> for Qe3CertDataPpid<'a> {
278    fn parse(type_: CertificationDataType, mut data: Cow<'a, [u8]>) -> Result<Self> {
279        let ppid_len = match type_ {
280            CertificationDataType::PpidCleartext => bail!(
281                "PPID clear text not implemented. Data length = {}",
282                data.len()
283            ),
284            CertificationDataType::PpidEncryptedRsa2048 => 256,
285            CertificationDataType::PpidEncryptedRsa3072 => 384,
286            _ => bail!("Invalid certification data type: {:?}", type_),
287        };
288
289        let ppid = data.take_prefix(ppid_len)?;
290        let cpusvn = data.take_prefix(CPUSVN_LEN)?;
291        let pcesvn = data.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
292        let pceid = data.take_prefix(mem::size_of::<u16>()).map(|v| LE::read_u16(&v))?;
293        if !data.is_empty() {
294            bail!(
295                "Invalid certification data length for type {:?}: {}",
296                type_,
297                data.len()
298            );
299        }
300
301        Ok(Qe3CertDataPpid {
302            ppid,
303            cpusvn,
304            pcesvn,
305            pceid,
306        })
307    }
308}
309
310impl<'a> Qe3CertData<'a> for RawQe3CertData<'a>{
311    fn parse(_type_: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self> {
312        Ok(data)
313    }
314}
315
316impl<'a> Qe3CertData<'a> for Qe3CertDataPckCertChain<'a> {
317    fn parse(type_: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self>{
318        if type_ != CertificationDataType::PckCertificateChain {
319            bail!("Invalid certification data type: {:?}", type_);
320        }
321
322        let mut data = match data {
323             Cow::Borrowed(s) =>
324                 std::str::from_utf8(s)
325                     .map(Cow::Borrowed)
326                     .map_err(|e| format_err!("Invalid certificate format: {}", e))?,
327             Cow::Owned(s) =>
328                 String::from_utf8(s)
329                     .map(Cow::Owned)
330                     .map_err(|e| format_err!("Invalid certificate format: {}", e))?,
331        };
332        // TODO: use pkix PemBlock parser
333        let mut certs = vec![];
334        let mark = "-----END CERTIFICATE-----";
335        while let Some(pos) = data.find(mark) {
336            certs.push(data.take_prefix(pos + mark.len()).expect("validated -- pos is always valid"));
337            if let Some(start) = data.find("-") {
338                data.take_prefix(start).unwrap(); //validated -- start is always valid
339            }
340        }
341        Ok(Qe3CertDataPckCertChain{
342            certs
343        })
344    }
345}
346
347// =============================================
348// ================= ACCESSORS =================
349// =============================================
350
351impl<'a> Quote<'a> {
352    pub fn header(&self) -> &QuoteHeader<'a> {
353        &self.header
354    }
355
356    pub fn report_body(&self) -> &[u8] {
357        &self.report_body
358    }
359
360    pub fn signature<T: Quote3Signature<'a>>(&self) -> Result<T> {
361        let QuoteHeader::V3 {
362            attestation_key_type,
363            ..
364        } = self.header;
365        T::parse(attestation_key_type, self.signature.clone())
366    }
367
368    pub fn clone_owned(&self) -> Quote<'static> {
369        Quote {
370            header: self.header.clone_owned(),
371            report_body: (*self.report_body).to_owned().into(),
372            signature: (*self.signature).to_owned().into(),
373        }
374    }
375}
376
377impl<'a> QuoteHeader<'a> {
378    pub fn clone_owned(&self) -> QuoteHeader<'static> {
379        match *self {
380            QuoteHeader::V3 {
381                attestation_key_type,
382                qe3_svn,
383                pce_svn,
384                ref qe3_vendor_id,
385                ref user_data,
386            } => QuoteHeader::V3 {
387                attestation_key_type,
388                qe3_svn,
389                pce_svn,
390                qe3_vendor_id: (**qe3_vendor_id).to_owned().into(),
391                user_data: (**user_data).to_owned().into(),
392            },
393        }
394    }
395}
396
397impl<'a> Quote3SignatureEcdsaP256<'a> {
398    pub fn signature(&self) -> &[u8] {
399        &self.signature
400    }
401
402    pub fn attestation_public_key(&self) -> &[u8] {
403        &self.attestation_public_key
404    }
405
406    #[cfg(feature = "verify")]
407    fn attestation_pk(&self) -> Result<Pk> {
408        let mut pt = vec![0x4];
409        pt.extend_from_slice(&mut self.attestation_public_key());
410        let group = EcGroup::new(EcGroupId::SecP256R1).map_err(|e| format_err!("Cannot create EcGroup: {}", e))?;
411        let pt = EcPoint::from_binary(&group, &pt).map_err(|e| format_err!("Cannot create point from Quote header: {}", e))?;
412        Pk::public_from_ec_components(group, pt).map_err(|e| format_err!("Cannot create pub key from Quote header: {}", e))
413    }
414
415    pub fn qe3_report(&self) -> &[u8] {
416        &self.qe3_report
417    }
418
419    pub fn qe3_signature(&self) -> &[u8] {
420        &self.qe3_signature
421    }
422
423    pub fn authentication_data(&self) -> &[u8] {
424        &self.authentication_data
425    }
426
427    pub fn certification_data_type(&self) -> CertificationDataType {
428        self.certification_data_type
429    }
430
431    pub fn certification_data<T: Qe3CertData<'a>>(&self) -> Result<T> {
432        T::parse(self.certification_data_type, self.certification_data.clone())
433    }
434
435    pub fn clone_owned(&self) -> Quote3SignatureEcdsaP256<'static> {
436        Quote3SignatureEcdsaP256 {
437            signature: (*self.signature).to_owned().into(),
438            attestation_public_key: (*self.attestation_public_key).to_owned().into(),
439            qe3_report: (*self.qe3_report).to_owned().into(),
440            qe3_signature: (*self.qe3_signature).to_owned().into(),
441            authentication_data: (*self.authentication_data).to_owned().into(),
442            certification_data_type: self.certification_data_type,
443            certification_data: (*self.certification_data).to_owned().into(),
444        }
445    }
446
447    // verify signature against quote using attestation_public_key
448    #[cfg(feature = "verify")]
449    pub fn verify_quote_signature(&'a self, quote: &[u8]) -> Result<&'a Self> {
450        let sig = get_ecdsa_sig_der(self.signature())?;
451        let data = &quote[0..432]; // Quote Header + ISV Enclave Report
452        let mut hash = [0u8; 32];
453        Md::hash(hash::Type::Sha256, &data, &mut hash)?;
454        let mut pk = self.attestation_pk()?;
455        pk.verify(mbedtls::hash::Type::Sha256, &hash, &sig)?;
456
457        Ok(self)
458    }
459
460    #[cfg(feature = "verify")]
461    pub fn verify_qe3_report_signature(&self, pck_pk: &[u8]) -> Result<()> {
462        //   verify QE report signature signed by pck
463        let sig = get_ecdsa_sig_der(self.qe3_signature())?;
464        let mut hash = [0u8; 32];
465        Md::hash(hash::Type::Sha256, &self.qe3_report(), &mut hash)?;
466        let mut pck_pk = Pk::from_public_key(&pck_pk)?;
467        pck_pk.verify(mbedtls::hash::Type::Sha256, &hash, &sig)?;
468
469        //   verify QE report::reportdata
470        let mut qe3_report = Vec::with_capacity(Report::UNPADDED_SIZE);
471        qe3_report.extend(self.qe3_report());
472        qe3_report.resize_with(Report::UNPADDED_SIZE, Default::default);
473        let qe3_report = Report::try_copy_from(&qe3_report).ok_or(format_err!("Could not construct Qe3 report"))?;
474
475        let mut hash = [0u8; 32];
476        let mut sha256 = Md::new(hash::Type::Sha256)?;
477        sha256.update(self.attestation_public_key())?;
478        sha256.update(self.authentication_data())?;
479        sha256.finish(&mut hash)?;
480
481        if qe3_report.reportdata[0..32] != hash {
482            bail!("Verification of QE3 report data failed");
483        }
484
485        if qe3_report.reportdata[32..64] != [0; 32] {
486            bail!("Verification of QE3 report data failed (second half not 0)");
487        }
488
489        Ok(())
490    }
491}
492
493impl<'a> Qe3CertDataPpid<'a> {
494    pub fn clone_owned(&self) -> Qe3CertDataPpid<'static> {
495        Qe3CertDataPpid {
496            ppid: (*self.ppid).to_owned().into(),
497            cpusvn: (*self.cpusvn).to_owned().into(),
498            pcesvn: self.pcesvn,
499            pceid: self.pceid,
500        }
501    }
502}
503
504#[cfg(feature = "verify")]
505pub trait Quote3SignatureEcdsaP256Verifier {
506    /// Verify the platform certification data.
507    ///
508    /// The certification data is in `quote3signature.certification_data()`.
509    ///
510    /// On success, should return the platform certification public key (PCK) in DER format.
511    // TODO: pass a container for the certification type and data instead of
512    // the whole signature structure.
513    fn verify_certification_data<'a>(&mut self, quote3signature: &'a Quote3SignatureEcdsaP256) -> Result<Vec<u8>>;
514
515    /// Verify the quoting enclave (QE3).
516    fn verify_qe3(&mut self, qe3_report: &[u8], authentication_data: &[u8]) -> Result<()>;
517}
518
519#[cfg(feature = "verify")]
520pub trait Quote3SignatureVerify<'a>: Quote3Signature<'a> {
521    type TrustRoot;
522
523    fn verify(&self, quote: &[u8], root_of_trust: Self::TrustRoot) -> Result<()>;
524}
525
526#[cfg(feature = "verify")]
527impl<'a> Quote3SignatureVerify<'a> for Quote3SignatureEcdsaP256<'a> {
528    type TrustRoot = &'a mut dyn Quote3SignatureEcdsaP256Verifier;
529
530    fn verify(&self, quote: &[u8], verifier: Self::TrustRoot) -> Result<()> {
531        let pck_pk = verifier.verify_certification_data(self)?;
532        self.verify_qe3_report_signature(&pck_pk)?;
533        verifier.verify_qe3(self.qe3_report(), self.authentication_data())?;
534        self.verify_quote_signature(quote)?;
535        Ok(())
536    }
537}
538
539impl<'a> Quote<'a> {
540    #[cfg(feature = "verify")]
541    pub fn verify<T: Quote3SignatureVerify<'a>>(quote: &'a [u8], root_of_trust: T::TrustRoot) -> Result<Self> {
542        let parsed_quote = Self::parse(quote)?;
543        let sig = parsed_quote.signature::<T>()?;
544        Quote3SignatureVerify::verify(&sig, quote, root_of_trust)?;
545        Ok(parsed_quote)
546    }
547}
548
549#[cfg(test)]
550mod tests {
551    use super::*;
552    #[cfg(feature = "verify")]
553    use mbedtls::x509::certificate::{Certificate};
554    #[cfg(feature = "verify")]
555    use std::ffi::CString;
556    #[cfg(feature = "verify")]
557    use serde::{Deserialize, Serialize};
558
559    #[cfg(feature = "verify")]
560    #[derive(Serialize, Deserialize, PartialEq, Eq, Clone, Debug, Copy)]
561    pub enum TcbStatus {
562        UpToDate,
563        SWHardeningNeeded,
564        ConfigurationNeeded,
565        ConfigurationAndSWHardeningNeeded,
566        OutOfDate,
567        OutOfDateConfigurationNeeded,
568        Revoked,
569    }
570
571    #[cfg(feature = "verify")]
572    #[derive(Clone, Serialize, Deserialize, Debug)]
573    struct Tcb {
574        isvsvn: u16,
575    }
576
577    #[cfg(feature = "verify")]
578    #[serde(rename_all = "camelCase")]
579    #[derive(Clone, Serialize, Deserialize, Debug)]
580    pub struct TcbLevel {
581        tcb: Tcb,
582        tcb_date: String,
583        tcb_status: TcbStatus,
584        #[serde(default, rename = "advisoryIDs", skip_serializing_if = "Vec::is_empty")]
585        advisory_ids: Vec<String>,
586    }
587
588    #[cfg(feature = "verify")]
589    #[serde(rename_all = "camelCase")]
590    #[derive(Clone, Serialize, Deserialize, Debug)]
591    pub struct QeIdentity {
592        version: u16,
593        id: String,
594        issue_date: String,
595        next_update: String,
596        tcb_evaluation_data_number: u32,
597        miscselect: String,
598        miscselect_mask: String,
599        attributes: String,
600        attributes_mask: String,
601        mrsigner: String,
602        isvprodid: u16,
603        tcb_levels: Vec<TcbLevel>,
604    }
605
606    #[cfg(feature = "verify")]
607    #[serde(rename_all = "camelCase")]
608    #[derive(Deserialize)]
609    struct QeIdentitySigned {
610        enclave_identity: QeIdentity,
611        _signature: String,
612    }
613
614    #[test]
615    fn test_parse_certdata() {
616        const TEST_QUOTE: &[u8] = &*include_bytes!("../tests/quote_raw_tcb.bin");
617        const QE_ID: [u8; 16] = [
618            0x00, 0xfb, 0xe6, 0x73, 0x33, 0x36, 0xea, 0xf7, 0xa4, 0xe3, 0xd8, 0xb9, 0x66, 0xa8,
619            0x2e, 0x64,
620        ];
621
622        const EXPECTED_PPID: &[u8; 384] = include_bytes!("../tests/encrypted_ppid.bin");
623        const EXPECTED_CPUSVN: [u8; 16] = [
624            0x05, 0x05, 0x02, 0x05, 0xff, 0x80, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
625            0x00, 0x00,
626        ];
627        const EXPECTED_PCESVN: u16 = 6;
628        const EXPECTED_PCEID: u16 = 0;
629
630        let quote = Quote::parse(TEST_QUOTE).unwrap();
631        let &QuoteHeader::V3 {
632            attestation_key_type,
633            ref qe3_vendor_id,
634            ref user_data,
635            ..
636        } = quote.header();
637
638        assert_eq!(qe3_vendor_id, &&QE3_VENDOR_ID_INTEL[..]);
639        let mut ud = QE_ID.to_vec();
640        ud.resize(20usize, 0u8);
641        assert_eq!(user_data, &ud);
642
643        assert_eq!(attestation_key_type, Quote3AttestationKeyType::EcdsaP256);
644        let sig = quote.signature::<Quote3SignatureEcdsaP256>().unwrap();
645
646        assert_eq!(
647            sig.certification_data_type(),
648            CertificationDataType::PpidEncryptedRsa3072
649        );
650        let cd = sig.certification_data::<Qe3CertDataPpid>().unwrap();
651
652        assert_eq!(cd.ppid, &EXPECTED_PPID[..]);
653        assert_eq!(cd.cpusvn, &EXPECTED_CPUSVN[..]);
654        assert_eq!(cd.pcesvn, EXPECTED_PCESVN);
655        assert_eq!(cd.pceid, EXPECTED_PCEID);
656    }
657
658    #[cfg(feature = "verify")]
659    pub struct MyVerifier{
660        qe3_identity: String,
661    }
662
663    #[cfg(feature = "verify")]
664    impl Quote3SignatureEcdsaP256Verifier for MyVerifier {
665        fn verify_certification_data<'a>(&mut self, quote3signature: &'a Quote3SignatureEcdsaP256) -> Result<Vec<u8>> {
666            let certs = quote3signature.certification_data::<Qe3CertDataPckCertChain>().unwrap().certs;
667            let pck = include_str!("../tests/pck_quote.cert");
668            let processor_ca = include_str!("../tests/processor_ca.cert");
669            let root_ca = include_str!("../tests/root_ca.cert");
670            assert_eq!(certs[0], pck.trim());
671            assert_eq!(certs[1], processor_ca.trim());
672            assert_eq!(certs[2], root_ca.trim());
673            assert_eq!(certs.len(), 3);
674
675            let cert_chain = quote3signature.certification_data::<Qe3CertDataPckCertChain>()?;
676            let pck = CString::new(cert_chain.certs[0].as_ref())?;
677            let mut pck = Certificate::from_pem(pck.as_bytes_with_nul())?;
678            Ok(pck.public_key_mut().write_public_der_vec()?)
679        }
680
681        /// Verify the quoting enclave (QE3).
682        fn verify_qe3(&mut self, qe3_report: &[u8], authentication_data: &[u8]) -> Result<()> {
683            assert_eq!(authentication_data,(0..=31).collect::<Vec<u8>>().as_slice());
684
685            let mut report = Vec::with_capacity(Report::UNPADDED_SIZE);
686            report.extend(qe3_report);
687            report.resize_with(Report::UNPADDED_SIZE, Default::default);
688            let report = Report::try_copy_from(&report).ok_or(format_err!("Could not construct Qe3 report"))?;
689
690            let qe3_identity: QeIdentitySigned = serde_json::from_str(&self.qe3_identity).unwrap();
691            if let Some(tcb_level) = qe3_identity.enclave_identity.tcb_levels.iter().find(|level| level.tcb.isvsvn == report.isvsvn) {
692                if tcb_level.tcb_status == TcbStatus::UpToDate {
693                    // WARNING: other features in the report also need to be verified
694                    return Ok(())
695                }
696            }
697
698            Err(format_err!("QE3 out of date"))
699        }
700    }
701
702    #[test]
703    fn test_quote_verification() {
704        const TEST_QUOTE: &[u8] = &*include_bytes!("../tests/quote_pck_cert_chain.bin");
705        let quote = Quote::parse(TEST_QUOTE).unwrap();
706        let &QuoteHeader::V3 {
707            attestation_key_type,
708            ref qe3_vendor_id,
709            ..
710        } = quote.header();
711
712        assert_eq!(qe3_vendor_id, &&QE3_VENDOR_ID_INTEL[..]);
713
714        assert_eq!(attestation_key_type, Quote3AttestationKeyType::EcdsaP256);
715
716        #[cfg(feature = "verify")]
717        let mut verifier = MyVerifier {
718            // The quote in `quote_pck_cert_chain.bin` is created with an out of date QE3 enclave.
719            // Since we do not have an old, but matching `qe3_identity.json` file, a newer version
720            // has been modified. This obviously will be detected when the signature in that file
721            // is checked, but this is not implemented
722            // TODO: Update the example quote with a matching qe3_identity.json file
723            qe3_identity: include_str!("../tests/corrupt_qe3_identity.json").to_string(),
724        };
725        #[cfg(feature = "verify")]
726        assert!(Quote::verify::<Quote3SignatureEcdsaP256>(TEST_QUOTE, &mut verifier).is_ok())
727    }
728
729    #[test]
730    fn test_quote_verification_qe3_out_of_date() {
731        const TEST_QUOTE: &[u8] = &*include_bytes!("../tests/quote_pck_cert_chain.bin");
732        let quote = Quote::parse(TEST_QUOTE).unwrap();
733        let &QuoteHeader::V3 {
734            attestation_key_type,
735            ref qe3_vendor_id,
736            ..
737        } = quote.header();
738
739        assert_eq!(qe3_vendor_id, &&QE3_VENDOR_ID_INTEL[..]);
740
741        assert_eq!(attestation_key_type, Quote3AttestationKeyType::EcdsaP256);
742
743        #[cfg(feature = "verify")]
744        let mut verifier = MyVerifier {
745            qe3_identity: include_str!("../tests/qe3_identity.json").to_string(),
746        };
747        #[cfg(feature = "verify")]
748        assert!(Quote::verify::<Quote3SignatureEcdsaP256>(TEST_QUOTE, &mut verifier).is_err())
749    }
750
751    #[test]
752    fn test_corrupt_quote_verification() {
753        const TEST_QUOTE: &[u8] = &*include_bytes!("../tests/quote_pck_cert_chain_corrupted.bin");
754        let quote = Quote::parse(TEST_QUOTE).unwrap();
755        let &QuoteHeader::V3 {
756            attestation_key_type,
757            ref qe3_vendor_id,
758            ..
759        } = quote.header();
760
761        assert_eq!(qe3_vendor_id, &&QE3_VENDOR_ID_INTEL[..]);
762
763        assert_eq!(attestation_key_type, Quote3AttestationKeyType::EcdsaP256);
764
765        #[cfg(feature = "verify")]
766        let mut verifier = MyVerifier {
767            // The quote in `quote_pck_cert_chain.bin` is created with an out of date QE3 enclave.
768            // Unfortunately, we do not have a matching `qe3_identity.json` file and the QE3 TCB
769            // state is verified before the PCK cert chain is verified. As the test verifier does
770            // not check the signature in `qe3_identity.json` we can modify it for test purposes.
771            // TODO Update the example quote with a matching QE3 identity file
772            qe3_identity: include_str!("../tests/corrupt_qe3_identity.json").to_string(),
773        };
774        #[cfg(feature = "verify")]
775        assert!(Quote::verify::<Quote3SignatureEcdsaP256>(TEST_QUOTE, &mut verifier).is_err());
776    }
777}