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#[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 pub fn verify(&self, policy: &QuotePolicy, ts: DateTime<Utc>) -> Result<VerifiedQuote, Error> {
40 if policy.disabled {
41 return Err(Error::Disabled);
42 }
43
44 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 let quote = Quote::parse(&self.quote)?;
51 let tee_type = quote.header().tee_type();
52
53 match (tee_type, &policy.tdx) {
55 (TeeType::SGX, _) => { }
56 (TeeType::TDX, &None) => return Err(Error::TeeTypeNotAllowed),
57 (TeeType::TDX, &Some(_)) => { }
58 }
59
60 if quote.header().qe_vendor_id() != QE_VENDOR_ID_INTEL {
62 return Err(Error::UnsupportedQEVendor);
63 }
64
65 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 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 if !unsafe_skip_quote_verification {
84 let tcb_level = quote.verify(tcb_info, qe_identity)?;
85
86 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 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 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#[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 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 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 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 pub fn header(&self) -> &Header<'a> {
244 &self.header
245 }
246
247 pub fn report_body(&self) -> &ReportBody {
249 &self.report_body
250 }
251
252 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#[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 pub fn version(&self) -> u16 {
286 match self {
287 Self::V3 { .. } => QUOTE_VERSION_3,
288 Self::V4 { .. } => QUOTE_VERSION_4,
289 }
290 }
291
292 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 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 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 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#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
333#[repr(u32)]
334pub enum TeeType {
335 SGX = 0x00000000,
336 TDX = 0x00000081,
337}
338
339#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, FromPrimitive, ToPrimitive)]
341#[repr(u16)]
342pub enum AttestationKeyType {
343 EcdsaP256 = 2,
344}
345
346#[derive(Debug)]
348pub enum ReportBody {
349 Sgx(SgxReport),
350 Tdx(TdReport),
351}
352
353impl ReportBody {
354 pub fn parse(tee_type: TeeType, raw: &[u8]) -> Result<Self, Error> {
356 match tee_type {
357 TeeType::SGX => {
358 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 let report = TdReport::parse(raw)?;
370
371 Ok(Self::Tdx(report))
372 }
373 }
374 }
375
376 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 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 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 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
422pub trait QuoteSignature<'a>: Sized {
424 fn parse(version: u16, data: Cow<'a, [u8]>) -> Result<Self, Error>;
426}
427
428#[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 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 pub fn signature(&self) -> &[u8] {
486 &self.signature
487 }
488
489 pub fn attestation_public_key(&self) -> &[u8] {
491 &self.attestation_public_key
492 }
493
494 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 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
516fn 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
538fn parse_ecdsa_pk(pk: &[u8]) -> Result<Pk, Error> {
540 let mut pt = vec![0x4]; 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
548pub trait QuoteSignatureEcdsaP256Verifier {
550 fn verify_certification_data(
556 &mut self,
557 signature: &QuoteSignatureEcdsaP256,
558 ) -> Result<Vec<u8>, Error>;
559
560 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#[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
595pub trait CertificationData<'a>: Sized {
597 fn parse(r#type: CertificationDataType, data: Cow<'a, [u8]>) -> Result<Self, Error>;
599}
600
601#[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#[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()); if let Some(start) = data.find("-") {
665 data.take_prefix(start).unwrap(); }
667 }
668
669 Ok(CertificationDataPckCertificateChain { certs })
670 }
671}
672
673#[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 pub fn qe_report(&self) -> &[u8] {
719 &self.qe_report
720 }
721
722 pub fn qe_report_signature(&self) -> &[u8] {
724 &self.qe_report_signature
725 }
726
727 pub fn authentication_data(&self) -> &[u8] {
729 &self.authentication_data
730 }
731
732 pub fn certification_data_type(&self) -> CertificationDataType {
734 self.certification_data_type
735 }
736
737 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 pub fn verify_qe_report_signature(
747 &self,
748 attestation_pk: &[u8],
749 pck_pk: &[u8],
750 ) -> Result<(), Error> {
751 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 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
796pub 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 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 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 let certs = signature
833 .qe
834 .certification_data::<CertificationDataPckCertificateChain>()?
835 .certs;
836 if certs.len() != 3 {
837 return Err(Error::UnexpectedCertificateChain);
838 }
839
840 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 Certificate::verify(&cert_chain, &PCS_TRUST_ROOT, None, None).map_err(|_| {
850 Error::VerificationFailed("PCK certificate chain is invalid".to_string())
851 })?;
852
853 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 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 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_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 = Some(reader.next().read_u32()?);
890 } else if comp_id == 18 {
891 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 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 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}