1#![no_std]
20mod error;
21#[cfg(feature = "mock")]
22mod mock;
23mod take_n;
24
25#[cfg(feature = "pck")]
26pub mod pck;
27
28pub use error::{QuoteParseError, QuoteVerificationError, VerifyingKeyError};
29use p256::EncodedPoint;
30use take_n::{take16, take2, take20, take384, take48, take64, take8};
31
32extern crate alloc;
33use alloc::{boxed::Box, vec::Vec};
34
35use nom::{
36 bytes::complete::take,
37 combinator::{map, map_res},
38 number::complete::{le_i16, le_i32, le_u16, le_u32},
39 sequence::tuple,
40 IResult,
41};
42#[cfg(feature = "mock")]
43pub use p256::ecdsa::SigningKey;
44pub use p256::ecdsa::{signature::Verifier, Signature, VerifyingKey};
45use sha2::{Digest, Sha256};
46
47const QUOTE_HEADER_LENGTH: usize = 48;
48const V4_QUOTE_BODY_LENGTH: usize = 584;
49const V5_QUOTE_BODY_LENGTH: usize = V4_QUOTE_BODY_LENGTH + 64;
50
51#[derive(Debug, Eq, PartialEq)]
53pub struct Quote {
54 pub header: QuoteHeader,
55 pub body: QuoteBody,
56 pub signature: Signature,
57 pub attestation_key: VerifyingKey,
58 pub certification_data: CertificationData,
59}
60
61impl Quote {
62 pub fn from_bytes(original_input: &[u8]) -> Result<Self, QuoteParseError> {
64 let (input, header) = quote_header_parser(original_input)?;
66 if header.attestation_key_type != AttestionKeyType::ECDSA256WithP256 {
67 return Err(QuoteParseError::UnsupportedAttestationKeyType);
68 };
69 let body_length = match header.version {
70 4 => V4_QUOTE_BODY_LENGTH,
71 5 => V5_QUOTE_BODY_LENGTH,
72 _ => return Err(QuoteParseError::UnknownQuoteVersion),
73 };
74
75 let signed_data = &original_input[..QUOTE_HEADER_LENGTH + body_length];
77
78 let (input, body) = body_parser(input, header.version)?;
80
81 let (input, _signature_section_length) = le_i32(input)?;
83 let (input, signature) = take(64u8)(input)?;
84 let signature = Signature::from_bytes(signature.into())?;
85
86 let (input, attestation_key) = take(64u8)(input)?;
88 let attestation_key_bytes = attestation_key;
89 let attestation_key = [&[4], attestation_key].concat(); let attestation_key = VerifyingKey::from_sec1_bytes(&attestation_key)?;
91
92 attestation_key.verify(signed_data, &signature)?;
94
95 let (input, certification_data_type) = le_i16(input)?;
97 let (input, certification_dat_len) = le_i32(input)?;
98 let certification_dat_len: usize = certification_dat_len.try_into()?;
99 let (_input, certification_data) = take(certification_dat_len)(input)?;
100 let certification_data = CertificationData::new(
101 certification_data_type,
102 certification_data.to_vec(),
103 attestation_key_bytes.to_vec(),
104 )?;
105
106 Ok(Quote {
107 header,
108 body,
109 signature,
110 attestation_key,
111 certification_data,
112 })
113 }
114
115 pub fn report_input_data(&self) -> [u8; 64] {
117 self.body.reportdata
118 }
119
120 pub fn mrtd(&self) -> [u8; 48] {
122 self.body.mrtd
123 }
124
125 pub fn rtmr0(&self) -> [u8; 48] {
127 self.body.rtmr0
128 }
129
130 pub fn rtmr1(&self) -> [u8; 48] {
132 self.body.rtmr1
133 }
134
135 pub fn rtmr2(&self) -> [u8; 48] {
137 self.body.rtmr2
138 }
139
140 pub fn rtmr3(&self) -> [u8; 48] {
142 self.body.rtmr3
143 }
144
145 pub fn qe_report_certification_data(&self) -> Option<QeReportCertificationData> {
147 if let CertificationData::QeReportCertificationData(qe_report_certification_data) =
148 &self.certification_data
149 {
150 Some(*qe_report_certification_data.clone())
151 } else {
152 None
153 }
154 }
155
156 pub fn verify_with_pck(&self, pck: &VerifyingKey) -> Result<(), QuoteVerificationError> {
158 let qe_report_certification_data = self
159 .qe_report_certification_data()
160 .ok_or(QuoteVerificationError::NoQeReportCertificationData)?;
161 pck.verify(
162 &qe_report_certification_data.qe_report[..],
163 &qe_report_certification_data.signature,
164 )?;
165 Ok(())
166 }
167
168 pub fn pck_cert_chain(&self) -> Result<Vec<u8>, QuoteVerificationError> {
171 match &self.certification_data {
172 CertificationData::PckCertChain(cert_chain) => Ok(cert_chain.clone()),
173 CertificationData::QeReportCertificationData(qe_report_certification_data) => {
174 if let CertificationDataInner::PckCertChain(cert_chain) =
175 &qe_report_certification_data.certification_data
176 {
177 Ok(cert_chain.clone())
178 } else {
179 Err(QuoteVerificationError::NoPckCertChain)
180 }
181 }
182 _ => Err(QuoteVerificationError::NoPckCertChain),
183 }
184 }
185
186 #[cfg(feature = "pck")]
188 pub fn verify(&self) -> Result<VerifyingKey, QuoteVerificationError> {
189 let cert_chain = self.pck_cert_chain()?;
190 let pck = pck::verify_pck_certificate_chain_pem(cert_chain)?;
191
192 self.verify_with_pck(&pck)?;
193 Ok(pck)
194 }
195}
196
197#[derive(Debug, Eq, PartialEq, Clone)]
199pub enum TEEType {
200 SGX = 0x00000000,
201 TDX = 0x00000081,
202}
203
204impl TryFrom<u32> for TEEType {
205 type Error = nom::Err<u32>;
206
207 fn try_from(value: u32) -> Result<Self, Self::Error> {
208 match value {
209 0x00000000 => Ok(TEEType::SGX),
210 0x00000081 => Ok(TEEType::TDX),
211 _ => Err(nom::Err::Failure(value)),
212 }
213 }
214}
215
216#[non_exhaustive]
218#[derive(Debug, Eq, PartialEq, Clone)]
219pub enum AttestionKeyType {
220 ECDSA256WithP256 = 2,
221 ECDSA384WithP384 = 3,
223}
224
225impl TryFrom<u16> for AttestionKeyType {
226 type Error = nom::Err<u32>;
227
228 fn try_from(value: u16) -> Result<Self, Self::Error> {
229 match value {
230 2 => Ok(Self::ECDSA256WithP256),
231 3 => Ok(Self::ECDSA384WithP384),
232 _ => Err(nom::Err::Failure(value as u32)),
233 }
234 }
235}
236
237#[derive(Debug, Eq, PartialEq)]
239pub struct QuoteHeader {
240 pub version: u16,
242 pub attestation_key_type: AttestionKeyType,
244 pub tee_type: TEEType,
246 pub reserved1: [u8; 2],
248 pub reserved2: [u8; 2],
250 pub qe_vendor_id: [u8; 16], pub user_data: [u8; 20],
253}
254
255#[derive(Debug, Eq, PartialEq, Clone)]
257pub enum TDXVersion {
258 One,
260 OnePointFive,
262}
263
264impl TryFrom<u16> for TDXVersion {
265 type Error = QuoteParseError;
266
267 fn try_from(value: u16) -> Result<Self, Self::Error> {
268 match value {
269 2 => Ok(TDXVersion::One),
270 3 => Ok(TDXVersion::OnePointFive),
271 _ => Err(QuoteParseError::Parse),
272 }
273 }
274}
275
276#[derive(Debug, Clone, Eq, PartialEq)]
278pub struct QuoteBody {
279 pub tdx_version: TDXVersion,
280 pub tee_tcb_svn: [u8; 16],
281 pub mrseam: [u8; 48],
282 pub mrsignerseam: [u8; 48],
283 pub seamattributes: [u8; 8],
284 pub tdattributes: [u8; 8],
285 pub xfam: [u8; 8],
286 pub mrtd: [u8; 48],
288 pub mrconfigid: [u8; 48],
289 pub mrowner: [u8; 48],
290 pub mrownerconfig: [u8; 48],
291 pub rtmr0: [u8; 48],
293 pub rtmr1: [u8; 48],
294 pub rtmr2: [u8; 48],
295 pub rtmr3: [u8; 48],
296 pub reportdata: [u8; 64],
298 pub tee_tcb_svn_2: Option<[u8; 16]>,
300 pub mrservicetd: Option<[u8; 48]>,
302}
303
304#[non_exhaustive]
306#[derive(Debug, PartialEq, Eq)]
307#[repr(i16)]
308pub enum CertificationData {
309 PckIdPpidPlainCpusvnPcesvn(Vec<u8>) = 1,
310 PckIdPpidRSA2048CpusvnPcesvn(Vec<u8>) = 2,
311 PckIdPpidRSA3072CpusvnPcesvn(Vec<u8>) = 3,
312 PckLeafCert(Vec<u8>) = 4,
313 PckCertChain(Vec<u8>) = 5,
314 QeReportCertificationData(Box<QeReportCertificationData>) = 6,
315 PlatformManifest(Vec<u8>) = 7,
316}
317
318impl CertificationData {
319 pub fn new(
320 certification_data_type: i16,
321 data: Vec<u8>,
322 attestation_key: Vec<u8>,
323 ) -> Result<Self, QuoteParseError> {
324 match certification_data_type {
325 1 => Ok(Self::PckIdPpidPlainCpusvnPcesvn(data)),
326 2 => Ok(Self::PckIdPpidRSA2048CpusvnPcesvn(data)),
327 3 => Ok(Self::PckIdPpidRSA3072CpusvnPcesvn(data)),
328 4 => Ok(Self::PckLeafCert(data)),
329 5 => Ok(Self::PckCertChain(data)),
330 6 => Ok(Self::QeReportCertificationData(Box::new(
331 QeReportCertificationData::new(data, attestation_key)?,
332 ))),
333 7 => Ok(Self::PlatformManifest(data)),
334 _ => Err(QuoteParseError::UnknownCertificationDataType),
335 }
336 }
337}
338
339#[non_exhaustive]
342#[derive(Debug, PartialEq, Eq, Clone)]
343#[repr(i16)]
344pub enum CertificationDataInner {
345 PckIdPpidPlainCpusvnPcesvn(Vec<u8>) = 1,
346 PckIdPpidRSA2048CpusvnPcesvn(Vec<u8>) = 2,
347 PckIdPpidRSA3072CpusvnPcesvn(Vec<u8>) = 3,
348 PckLeafCert(Vec<u8>) = 4,
349 PckCertChain(Vec<u8>) = 5,
350 PlatformManifest(Vec<u8>) = 7,
351}
352
353impl CertificationDataInner {
354 pub fn new(certification_data_type: i16, data: Vec<u8>) -> Result<Self, QuoteParseError> {
355 match certification_data_type {
356 1 => Ok(Self::PckIdPpidPlainCpusvnPcesvn(data)),
357 2 => Ok(Self::PckIdPpidRSA2048CpusvnPcesvn(data)),
358 3 => Ok(Self::PckIdPpidRSA3072CpusvnPcesvn(data)),
359 4 => Ok(Self::PckLeafCert(data)),
360 5 => Ok(Self::PckCertChain(data)),
361 7 => Ok(Self::PlatformManifest(data)),
363 _ => Err(QuoteParseError::UnknownCertificationDataType),
364 }
365 }
366}
367
368#[derive(Debug, PartialEq, Eq, Clone)]
370pub struct QeReportCertificationData {
371 pub qe_report: [u8; 384],
373 pub signature: Signature,
375 pub qe_authentication_data: Vec<u8>,
377 pub certification_data: CertificationDataInner,
379}
380
381impl QeReportCertificationData {
382 fn new(input: Vec<u8>, attestation_key: Vec<u8>) -> Result<Self, QuoteParseError> {
385 let (input, qe_report) = take384(&input)?;
386 let expected_hash = &qe_report[384 - 64..384 - 32];
389
390 let (input, signature) = take64(input)?;
391 let signature = Signature::from_bytes((&signature).into())?;
392 let (input, qe_authentication_data_size) = le_i16(input)?;
393 let qe_authentication_data_size: usize = qe_authentication_data_size.try_into()?;
394 let (input, qe_authentication_data) = take(qe_authentication_data_size)(input)?;
395
396 let (input, certification_data_type) = le_i16(input)?;
398 let (input, certification_dat_len) = le_i32(input)?;
399 let certification_dat_len: usize = certification_dat_len.try_into()?;
400 let (_input, certification_data) = take(certification_dat_len)(input)?;
401 let certification_data =
402 CertificationDataInner::new(certification_data_type, certification_data.to_vec())?;
403
404 let hash = {
406 let mut hasher = Sha256::new();
407 hasher.update(&attestation_key);
408 hasher.update(qe_authentication_data);
409 hasher.finalize()
410 };
411 if hash[..] != *expected_hash {
412 return Err(QuoteParseError::AttestationKeyDoesNotMatch);
413 }
414
415 Ok(Self {
416 qe_report,
417 signature,
418 qe_authentication_data: qe_authentication_data.to_vec(),
419 certification_data,
420 })
421 }
422}
423
424pub fn encode_verifying_key(input: &VerifyingKey) -> Result<[u8; 33], VerifyingKeyError> {
426 input
427 .to_encoded_point(true)
428 .as_bytes()
429 .try_into()
430 .map_err(|_| VerifyingKeyError::BadSize)
431}
432
433pub fn decode_verifying_key(
435 verifying_key_encoded: &[u8; 33],
436) -> Result<VerifyingKey, VerifyingKeyError> {
437 let point = EncodedPoint::from_bytes(verifying_key_encoded)
438 .map_err(|_| VerifyingKeyError::DecodeEncodedPoint)?;
439 VerifyingKey::from_encoded_point(&point)
440 .map_err(|_| VerifyingKeyError::EncodedPointToVerifyingKey)
441}
442
443fn quote_header_parser(input: &[u8]) -> IResult<&[u8], QuoteHeader> {
445 map_res(
446 tuple((le_u16, le_u16, le_u32, take2, take2, take16, take20)),
447 |(
448 version,
449 attestation_key_type,
450 tee_type,
451 reserved1,
452 reserved2,
453 qe_vendor_id,
454 user_data,
455 )| {
456 Ok::<QuoteHeader, nom::Err<u32>>(QuoteHeader {
457 version,
458 attestation_key_type: attestation_key_type.try_into()?,
459 tee_type: tee_type.try_into()?,
460 reserved1,
461 reserved2,
462 qe_vendor_id,
463 user_data,
464 })
465 },
466 )(input)
467}
468
469fn body_parser(input: &[u8], version: u16) -> IResult<&[u8], QuoteBody> {
471 let (input, tdx_version) = match version {
472 4 => (input, TDXVersion::One),
474 5 => {
476 let (input, body_type) = le_u16(input)?;
477 let (input, _body_size) = le_u32(input)?;
478 (
479 input,
480 body_type.try_into().map_err(|_| {
481 nom::Err::Failure(nom::error::Error::new(input, nom::error::ErrorKind::Fail))
482 })?,
483 )
484 }
485 _ => {
486 return Err(nom::Err::Failure(nom::error::Error::new(
487 input,
488 nom::error::ErrorKind::Fail,
489 )))
490 }
491 };
492 let (input, mut body) = basic_body_parser(input)?;
494 let input = if tdx_version == TDXVersion::OnePointFive {
495 body.tdx_version = TDXVersion::OnePointFive;
496 let (input, tee_tcb_svn_2) = take16(input)?;
497 body.tee_tcb_svn_2 = Some(tee_tcb_svn_2);
498 let (input, mrservicetd) = take48(input)?;
499 body.mrservicetd = Some(mrservicetd);
500 input
501 } else {
502 input
503 };
504 Ok((input, body))
505}
506
507fn basic_body_parser(input: &[u8]) -> IResult<&[u8], QuoteBody> {
509 map(
510 tuple((
511 take16, take48, take48, take8, take8, take8, take48, take48, take48, take48, take48,
512 take48, take48, take48, take64,
513 )),
514 |(
515 tee_tcb_svn,
516 mrseam,
517 mrsignerseam,
518 seamattributes,
519 tdattributes,
520 xfam,
521 mrtd,
522 mrconfigid,
523 mrowner,
524 mrownerconfig,
525 rtmr0,
526 rtmr1,
527 rtmr2,
528 rtmr3,
529 reportdata,
530 )| QuoteBody {
531 tdx_version: TDXVersion::One,
532 tee_tcb_svn,
533 mrseam,
534 mrsignerseam,
535 seamattributes,
536 tdattributes,
537 xfam,
538 mrtd,
539 mrconfigid,
540 mrowner,
541 mrownerconfig,
542 rtmr0,
543 rtmr1,
544 rtmr2,
545 rtmr3,
546 reportdata,
547 tee_tcb_svn_2: None,
548 mrservicetd: None,
549 },
550 )(input)
551}