From 078fe0f18670225a07a50ee8afb2e5432b8b1df6 Mon Sep 17 00:00:00 2001 From: exh0115 <165619875+exh0115@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:55:03 +0800 Subject: [PATCH] Support QuoteV5 --- Cargo.toml | 1 + tdx/Cargo.toml | 3 +- tdx/README.md | 4 +- tdx/examples/attestation.rs | 12 +++- tdx/examples/fmspc.rs | 11 ++-- tdx/examples/inspect.rs | 40 ++++------- tdx/examples/testdata/tdx_v5_quote.bin | Bin 0 -> 5006 bytes tdx/src/device.rs | 7 +- tdx/src/error.rs | 6 ++ tdx/src/lib.rs | 88 +++++++++---------------- tdx/src/utils.rs | 52 ++++++++++----- 11 files changed, 105 insertions(+), 119 deletions(-) create mode 100644 tdx/examples/testdata/tdx_v5_quote.bin diff --git a/Cargo.toml b/Cargo.toml index c22aabc..8fda620 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,3 +32,4 @@ chrono = "0.4.40" tokio = { version = "1.44.1", features = ["rt-multi-thread"] } x509-parser = "0.15.1" clap = { version = "4.0", features = ["derive", "env"] } +pem = "3.0.5" diff --git a/tdx/Cargo.toml b/tdx/Cargo.toml index bed998e..83cf922 100644 --- a/tdx/Cargo.toml +++ b/tdx/Cargo.toml @@ -21,7 +21,6 @@ cbindgen = "0.29.0" lto = true [dependencies] -dcap-rs.workspace = true rand.workspace = true ureq.workspace = true base64-url.workspace = true @@ -33,6 +32,8 @@ chrono.workspace = true tokio.workspace = true x509-parser.workspace = true clap.workspace = true +pem.workspace = true once_cell = { version = "1.20.2", optional=true } +dcap-rs = { git = "https://github.com/automata-network/dcap-rs", rev="ee2b89d" } coco-provider = { git = "https://github.com/automata-network/coco-provider-sdk", optional = true, default-features = false } diff --git a/tdx/README.md b/tdx/README.md index adc04b7..e2110fc 100644 --- a/tdx/README.md +++ b/tdx/README.md @@ -72,7 +72,7 @@ let tdx = Tdx::new(); ### Generate Attestation To generate an attestation with default options, you can do so like this: ```rust -let (report, _) = tdx.get_attestation_report()?; +let (report, _) = tdx.get_attestation_report_raw()?; ``` If you wish to customise options for the attestation report, you can do something like this: @@ -135,4 +135,4 @@ Please follow Intel official DCAP repo [SGXDataCenterAttestationPrimitives](http cargo build --example inspect sudo ./target/debug/examples/inspect --report tdx/examples/testdata/tdx_v4_quote.bin sudo ./target/debug/examples/inspect --report tdx/examples/testdata/sgx_v3_quote.bin - ``` \ No newline at end of file + ``` diff --git a/tdx/examples/attestation.rs b/tdx/examples/attestation.rs index a8afe74..00d1db3 100644 --- a/tdx/examples/attestation.rs +++ b/tdx/examples/attestation.rs @@ -1,3 +1,4 @@ +use dcap_rs::types::quote::Quote; use tdx::Tdx; fn main() { @@ -5,12 +6,19 @@ fn main() { let tdx = Tdx::new(); // Retrieve an attestation report with default options passed to the hardware device - let (report, _) = tdx.get_attestation_report().unwrap(); + // ================================================================================ + let (report_raw, _) = tdx.get_attestation_report_raw().unwrap(); + let report = Quote::read(&mut report_raw.as_slice()).unwrap(); println!("Attestation Report: {:?}", report); // Verify the attestation report - tdx.verify_attestation_report(&report).unwrap(); + // Use either function, they both do the same thing, except one takes in bytes + // while the other takes in a Quote struct. + // ================================================================================ + // tdx.verify_attestation_report(report).unwrap(); + tdx.verify_attestation_report_raw(&mut report_raw.as_slice()) + .unwrap(); println!("Verification successful!"); } diff --git a/tdx/examples/fmspc.rs b/tdx/examples/fmspc.rs index d640e18..28cb07c 100644 --- a/tdx/examples/fmspc.rs +++ b/tdx/examples/fmspc.rs @@ -1,15 +1,16 @@ -use tdx::Tdx; +use dcap_rs::types::quote::Quote; use tdx::utils::get_pck_fmspc_and_issuer; +use tdx::Tdx; fn main() { // Initialise a TDX object let tdx = Tdx::new(); // Retrieve an attestation report with default options passed to the hardware device - let (report, _) = tdx.get_attestation_report().unwrap(); - // println!("Attestation Report: {:?}", report); + let (report_raw, _) = tdx.get_attestation_report_raw().unwrap(); + let report = Quote::read(&mut report_raw.as_slice()).unwrap(); - let (fmspc, _) = get_pck_fmspc_and_issuer(&report); + let (fmspc, _) = get_pck_fmspc_and_issuer(&report).unwrap(); println!("FMSPC: {:?}", fmspc.to_uppercase()); if report.header.tee_type == 0 { println!("Platform: SGX"); @@ -17,4 +18,4 @@ fn main() { println!("Platform: TDX"); } println!("Version: {}", report.header.version); -} \ No newline at end of file +} diff --git a/tdx/examples/inspect.rs b/tdx/examples/inspect.rs index 9ef3c4d..232955c 100644 --- a/tdx/examples/inspect.rs +++ b/tdx/examples/inspect.rs @@ -1,9 +1,8 @@ +use dcap_rs::types::quote::Quote; use std::path::PathBuf; use clap::Parser; -use dcap_rs::types::quotes::{QuoteHeader, version_3::QuoteV3, version_4::QuoteV4}; -use dcap_rs::utils::cert::{parse_certchain, parse_pem}; -use tdx::utils::{extract_fmspc_from_extension, get_pck_fmspc_and_issuer}; +use tdx::utils::get_pck_fmspc_and_issuer; #[derive(Parser)] struct Opt { @@ -15,34 +14,17 @@ fn main() -> anyhow::Result<()> { let opt = Opt::parse(); let report_path = opt.report; let report = std::fs::read(&report_path)?; - let header = QuoteHeader::from_bytes(&report[0..48]); - if header.version == 3 { - let quote_v3 = QuoteV3::from_bytes(&report); - let fmspc = get_pck_fmspc_from_v3_quote("e_v3); - println!("FMSPC: {:?}", fmspc.to_uppercase()); + let report = Quote::read(&mut report.as_slice())?; + let report_version = u32::from(report.header.version); + + let (fmspc, _) = get_pck_fmspc_and_issuer(&report).unwrap(); + println!("FMSPC: {:?}", fmspc.to_uppercase()); + if report.header.tee_type == 0 { println!("Platform: SGX"); - println!("Version: V3"); - } else if header.version == 4 { - let quote_v4 = QuoteV4::from_bytes(&report); - let (fmspc, _) = get_pck_fmspc_and_issuer("e_v4); - println!("FMSPC: {:?}", fmspc.to_uppercase()); - if quote_v4.header.tee_type == 0 { - println!("Platform: SGX"); - } else { - println!("Platform: TDX"); - } - println!("Version: V4"); } else { - eprintln!("Unsupported quote version: {}", header.version); + println!("Platform: TDX"); } - Ok(()) -} + println!("Report Version: V{}", report_version); -fn get_pck_fmspc_from_v3_quote(quote: &QuoteV3) -> String { - let pem = parse_pem("e.signature.qe_cert_data.cert_data).expect("Failed to parse cert data"); - let cert_chain = parse_certchain(&pem); - let pck = &cert_chain[0]; - let fmspc_slice = extract_fmspc_from_extension(pck); - let fmspc = hex::encode(fmspc_slice); - fmspc + Ok(()) } diff --git a/tdx/examples/testdata/tdx_v5_quote.bin b/tdx/examples/testdata/tdx_v5_quote.bin new file mode 100644 index 0000000000000000000000000000000000000000..10758d58903669dad29b4a118c346d4801a18540 GIT binary patch literal 5006 zcmcIn3zQSp8D7D~brcX`iy{cpdK4DPb02dQ>%B9R$z(Ih&Lfjd)V9eaJ9$iAn@uJe zJ&N^F6sd5Ow^$S^R*J`>)>aOB#41Qde6~ofkE4LvN~;#3YPENgU5LwC0Z-?gY;yj4 z|M?&H`|kH|bJK{X>zeA%?JN2m2k-R$>JDu5LyNB+b;|Bp*Vr{_$-EV*)We7F?Vf1v z8rd{&L{n4qN$5|*Gx?djjYpTifA8&|Z57gcC!e|h+_CF7+;`FG%kOG=ZvV{tGW)J- z$sY(ko!|G>o+~r-XD2tf9`DaHOox4NRL!qdPpq4ivK^4luKZ!g-uT>A)0busys=>h zfBnj2fv@xW(K&VTgc;|=!z-oN9M&gVaQb>Z@Dr*0qp@~u~` z8$BX@)7-u+4ig)eKKrL>n|3unG-3WnP0qD1HGlG-DeCtpEM4;YWMakk={*~_+WIV? zPj5bX=V@L0cQ@}X-T&5`k92%~XYm>D%bS;Nm%=@e*^v=<<_~pZ7%SmcP%Oe&g(=vxKX!J>Gh+e`D9`{r!O@e|aqu zW!L>==j8cg+#QUJYgd%NTJpGe(w#qeZ2q#b=M~?({?+!G9nG#M$N&7`75l8)h{YpFS{a=lskr_m>Ok=v^%jefq~& zR*!zg2#=ie974m*t1PqMjjvq$yC-0;)_MLdmsPr29!oD;)%wxmmn_$`j`{c7cduB< zZ(d0Is9QBW4KM$2`pPT%KOMg(>AZCPfkRXNx@ex^8kc`?=hVI9rfs=l;W-aJwRvRd z-Akve8XLT)ICbTpH}2guan16}Z~g6|(NpP`C5yh8KlRtIzI`Q!zwivXcGL@_np`N0 z5hKo;H1eyaIxLT&jjaQF_6>aRS+nkuUDhp&FJ2~iHF?b%|Le~89S0BGd>eM-&Xya} z+rRCVuirKE>f9Z-O{9I!bzcx4jvRY+tMbufo8MF}+ctZQc;D0C=Ko#SM;v?U!-nd9 z4XpRSpRlcdXU(IVR&UvR3v=yL&5QoF_TdtFM(d6dbBA?mF#dDTm@zND$BySAotzpfVC*G@7QYq^GUhWe;@3vW_&K25efF z5`GrsXo4uv0_(&fU?GU^IRC0PR0 z-UxI(ETCxNpsoh2F`02eQm8^Q9P*&d!g<;5vW7(}!a{W*16Ah-9XLB)RcO2_hxmNd z?yR^-Ak?!`SiVgTDkR(;_Qb?pz z{vM8jP*n$hvk>Y&0syWTi?|(CDd(!#qrf7#nJ%Y=iAXU;Dn->eF6F(Q)oj0`)KT@= zEeR!64a7Xq?efvpSUVK`9WaI!EAG5b;#H%I2VPHBbX6l=IVVGsB!T*?>TVsK1aD2D z@VYAp0$LD{8BuH08P|ZIWCIvZ_V|IMcpVz2N9vn3aU9*hZ%mV=oB3H;`9xw$R~db=nSB>Vf$Ln+p3#UMPiosiYANwr5qe-(_~GfF;sNx{b0z zlA{R3Oc*c#2h}1v?<01AFaT1+*i{B%!!DS#9>xKcag-f1BSSO7Fp(jO08j^pW(3Uu zWZnL?5(pFl4&y+W0R|izOCxF}47bn_q0xafOBk}hRs#7A5lFLyAr5BAWJ7?jl|T&> z!E~Td08#|KLwiA~9EYJxlmIFV0SJJ@Pc3S$@hC}j0sv(bFwefBEDD1l!s&FE>8q2v z+})iEGl_D-2RJK)T+Td&_z))=LRiQa{7!pZv0{Scpa=(P(|WZ8OL0SxsOY|<@!&=X zg_eP1Z|s2^s7DqW(sTp8X74_7BXJ~9eGDL>3j;|=%f58FzfUk2R|p!twra1}u!z+Z zsSufbN65l@JH_rC�=-BmheiqT;fO_F`{O(57P+kH534OJfx|UUcyBG>HqIe3vNt zAyS>1cu_v`XhEy3e6!dQTFNA@{fO9kq;g2H<8+%l&rw;At%oxV)fFP}_Q26%N2?9u z#j=OBt1fmz)Q(%xB6sZMf$_08WP3ay9-41b=Zmsx9U}IBMg(U+zE5Zn}8LaVj z+n`KibuBu6ogOR|L-n1IP7lg7c9c%L(mJKfZWH82%1QuEW(IyE&L||O0?H>qOJ~2` zriBDQW=EHHzM^+J;~v;)r>Z2a2HX2WM1RHJFNxklN1{93VYCI3e3-}<_|AOMUsemQ zxKPnDqUO=CxR^=WgRt=43(pX~QYLfHnQ zenFx8NGQ~r=;pg9!GIo?imQACl8Lk{7jyHvV&uwRyH8WHd_J7fQfLs+lxn+G$w~$q zjM56yGAxD`!XdU4sgD^Y*^c*nt##ouM~*-SFUJs&ycy)20-G=saN5P*F-Jt$JIs zLz3yBBjB>w?WIU4Zk0V`$`*)YSk~oQShkR-|+v8K|%> zb2#PNS&=c!k55e7{8COI%p5bv-4jWDFmvp9=IX}O>z9TsVzs&Y82CzbWGS5`89D4v zS&}`$B4I?R?qnxs^C$g+mv<$CbSj#KlBZY7re$Zz!KBipEPJhN#qZ1%id8wK!H6bG za>dnSFM42iyP4_kcTrc7I8(ssv1)zp p99wJ)<=S(WVyMH9bHR$62jy^p_Q!axQz;ktP^7@j`5&BZ`Y&;D-`D^E literal 0 HcmV?d00001 diff --git a/tdx/src/device.rs b/tdx/src/device.rs index f7f1232..5e617cf 100644 --- a/tdx/src/device.rs +++ b/tdx/src/device.rs @@ -4,7 +4,7 @@ use coco_provider::{ coco::{CocoDeviceType, ReportRequest}, get_coco_provider, CocoProvider, }; -use dcap_rs::types::quotes::version_4::QuoteV4; + use serde::Deserialize; const IMDS_QUOTE_URL: &str = "http://169.254.169.254/acc/tdquote"; @@ -44,11 +44,6 @@ impl Device { Ok(Device { options, provider }) } - pub fn get_attestation_report(&self) -> Result<(QuoteV4, Option>)> { - let (raw_report, var_data) = self.get_attestation_report_raw()?; - Ok((QuoteV4::from_bytes(&raw_report), var_data)) - } - pub fn get_attestation_report_raw(&self) -> Result<(Vec, Option>)> { let report_data = match self.provider.device_type { CocoDeviceType::Tpm => { diff --git a/tdx/src/error.rs b/tdx/src/error.rs index 15088b4..323d83b 100644 --- a/tdx/src/error.rs +++ b/tdx/src/error.rs @@ -74,3 +74,9 @@ impl From for TdxError { TdxError::Anyhow(err.to_string()) } } + +impl From for TdxError { + fn from(err: std::str::Utf8Error) -> Self { + TdxError::IO(format!("{:?}", err)) + } +} diff --git a/tdx/src/lib.rs b/tdx/src/lib.rs index c5fb9fb..38421bc 100644 --- a/tdx/src/lib.rs +++ b/tdx/src/lib.rs @@ -3,17 +3,19 @@ pub mod error; pub mod pccs; pub mod utils; -use dcap_rs::types::collaterals::IntelCollateral; -use dcap_rs::types::quotes::version_4::QuoteV4; -use dcap_rs::utils::quotes::version_4::verify_quote_dcapv4; +use dcap_rs::types::collateral::Collateral; +use dcap_rs::types::quote::Quote; +use dcap_rs::verify_dcap_quote; use error::{Result, TdxError}; use pccs::enclave_id::get_enclave_identity; use pccs::fmspc_tcb::get_tcb_info; use pccs::pcs::{get_certificate_by_id, IPCSDao::CA}; -use std::panic; +use std::time::SystemTime; use tokio::runtime::Runtime; use utils::get_pck_fmspc_and_issuer; +use crate::utils::der_to_pem_bytes; + pub struct Tdx; impl Tdx { @@ -21,30 +23,6 @@ impl Tdx { Tdx } - /// Retrieve an Attestation Report. - /// - /// Returns: - /// - A tuple containing the attestation report and the optional var data. - /// - The attestation report is a `QuoteV4` struct. - /// - The var data is an optional `Vec` containing the var data. - /// Var data is only available if the device resides on an Azure Confidential VM. - /// Var data provided by Azure can be used to verify the contents of the attestation report's report_data - pub fn get_attestation_report(&self) -> Result<(QuoteV4, Option>)> { - let device = device::Device::default()?; - device.get_attestation_report() - } - - /// Retrieve an Attestation Report with options. - /// When available, users can pass in a 64 byte report data when requesting an attestation report. - /// This cannot be used on Azure Confidential VM. - pub fn get_attestation_report_with_options( - &self, - options: device::DeviceOptions, - ) -> Result<(QuoteV4, Option>)> { - let device = device::Device::new(options)?; - device.get_attestation_report() - } - /// Retrieve an Attestation Report in raw bytes. /// /// Returns: @@ -69,8 +47,14 @@ impl Tdx { device.get_attestation_report_raw() } + pub fn verify_attestation_report_raw(&self, report: &mut &[u8]) -> Result<()> { + let quote = Quote::read(report)?; + self.verify_attestation_report(quote)?; + Ok(()) + } + /// This function verifies the chain of trust for the attestation report. - pub fn verify_attestation_report(&self, report: &QuoteV4) -> Result<()> { + pub fn verify_attestation_report(&self, report: Quote) -> Result<()> { // First retrieve all the required collaterals. let rt = Runtime::new().unwrap(); let (root_ca, root_ca_crl) = rt.block_on(get_certificate_by_id(CA::ROOT))?; @@ -78,49 +62,41 @@ impl Tdx { return Err(TdxError::Http("Root CA or CRL is empty".to_string())); } - let (fmspc, pck_type) = get_pck_fmspc_and_issuer(report); + let (fmspc, pck_type) = get_pck_fmspc_and_issuer(&report)?; // tcb_type: 0: SGX, 1: TDX // version: TDX uses TcbInfoV3 let tcb_info = rt.block_on(get_tcb_info(1, &fmspc, 3))?; let quote_version = report.header.version; - let qe_identity = rt.block_on(get_enclave_identity(quote_version as u32))?; + let qe_identity = rt.block_on(get_enclave_identity(quote_version.get() as u32))?; let (signing_ca, _) = rt.block_on(get_certificate_by_id(CA::SIGNING))?; if signing_ca.is_empty() { - return Err(TdxError::Http("Signing CA is empty".to_string())); + return Err(TdxError::Http("TCB Signing CA is empty".to_string())); } + let signing_ca_pem = der_to_pem_bytes(&signing_ca); + let root_ca_pem = der_to_pem_bytes(&root_ca); + let combined_pem = [signing_ca_pem, root_ca_pem].concat(); let (_, pck_crl) = rt.block_on(get_certificate_by_id(pck_type))?; if pck_crl.is_empty() { return Err(TdxError::Http("PCK CRL is empty".to_string())); } - // Pass all the collaterals into a struct for verifying the quote. - let current_time = chrono::Utc::now().timestamp() as u64; - let mut collaterals = IntelCollateral::new(); + let tcb_info_json = std::str::from_utf8(&tcb_info)?; + let qe_identity_json = std::str::from_utf8(&qe_identity)?; - collaterals.set_tcbinfo_bytes(&tcb_info); - collaterals.set_qeidentity_bytes(&qe_identity); - collaterals.set_intel_root_ca_der(&root_ca); - collaterals.set_sgx_tcb_signing_der(&signing_ca); - collaterals.set_sgx_intel_root_ca_crl_der(&root_ca_crl); - match pck_type { - CA::PLATFORM => { - collaterals.set_sgx_platform_crl_der(&pck_crl); - } - CA::PROCESSOR => { - collaterals.set_sgx_processor_crl_der(&pck_crl); - } - _ => { - return Err(TdxError::Http("Unknown PCK Type".to_string())); - } - } - - match panic::catch_unwind(|| verify_quote_dcapv4(report, &collaterals, current_time)) { - Ok(_) => Ok(()), - Err(e) => Err(TdxError::Dcap(format!("DCAP Error: {:?}", e))), - } + // Pass all the collaterals into a struct for verifying the quote. + let collaterals = Collateral::new( + &root_ca_crl, + &pck_crl, + &combined_pem, + &tcb_info_json, + &qe_identity_json, + )?; + + verify_dcap_quote(SystemTime::now(), collaterals, report)?; + Ok(()) } } diff --git a/tdx/src/utils.rs b/tdx/src/utils.rs index 19ec346..1a9bcab 100644 --- a/tdx/src/utils.rs +++ b/tdx/src/utils.rs @@ -1,11 +1,9 @@ +use crate::error::{Result, TdxError}; use crate::CA; -use dcap_rs::types::quotes::version_4::QuoteV4; -use dcap_rs::types::quotes::QeReportCertData; -use dcap_rs::utils::cert::{get_x509_issuer_cn, parse_certchain, parse_pem}; +use dcap_rs::{types::quote::Quote, utils::cert_chain_processor}; use rand::RngCore; use x509_parser::oid_registry::asn1_rs::{oid, FromDer, OctetString, Oid, Sequence}; -use x509_parser::prelude::*; - +use x509_parser::prelude::{parse_x509_pem, X509Certificate}; /// Generates 64 bytes of random data /// Always guaranted to return something (ie, unwrap() can be safely called) pub fn generate_random_data() -> Option<[u8; 64]> { @@ -14,18 +12,30 @@ pub fn generate_random_data() -> Option<[u8; 64]> { Some(data) } -pub fn get_pck_fmspc_and_issuer(quote: &QuoteV4) -> (String, CA) { - let raw_cert_data = QeReportCertData::from_bytes("e.signature.qe_cert_data.cert_data); +pub fn der_to_pem_bytes(der_bytes: &[u8]) -> Vec { + let pem_struct = pem::Pem::new("CERTIFICATE".to_string(), der_bytes.to_vec()); + pem::encode(&pem_struct).into_bytes() +} + +pub fn get_pck_fmspc_and_issuer(quote: &Quote) -> Result<(String, CA)> { + let raw_cert_data = "e.signature.cert_data.cert_data; - let pem = parse_pem(&raw_cert_data.qe_cert_data.cert_data).expect("Failed to parse cert data"); - // Cert Chain: - // [0]: pck -> - // [1]: pck ca -> - // [2]: root ca - let cert_chain = parse_certchain(&pem); - let pck = &cert_chain[0]; + // // Cert Chain: + // // [0]: pck -> + // // [1]: pck ca -> + // // [2]: root ca + let ranges = cert_chain_processor::find_certificate_ranges(raw_cert_data); + if ranges.is_empty() { + return Err(TdxError::Dcap("No certificates found".to_string())); + } + let (pck_start, pck_end) = ranges[0]; + let (_, pem_struct) = parse_x509_pem(&raw_cert_data[pck_start..pck_end]) + .map_err(|e| TdxError::X509(format!("x509_parser error: {e}")))?; + let pck = pem_struct + .parse_x509() + .map_err(|e| TdxError::X509(format!("x509 error: {e}")))?; - let pck_issuer = get_x509_issuer_cn(pck); + let pck_issuer = get_x509_issuer_cn(&pck); let pck_ca = match pck_issuer.as_str() { "Intel SGX PCK Platform CA" => CA::PLATFORM, @@ -33,13 +43,19 @@ pub fn get_pck_fmspc_and_issuer(quote: &QuoteV4) -> (String, CA) { _ => panic!("Unknown PCK Issuer"), }; - let fmspc_slice = extract_fmspc_from_extension(pck); + let fmspc_slice = extract_fmspc_from_extension(&pck); let fmspc = hex::encode(fmspc_slice); - (fmspc, pck_ca) + Ok((fmspc, pck_ca)) +} + +fn get_x509_issuer_cn<'a>(cert: &'a X509Certificate<'a>) -> String { + let issuer = cert.issuer(); + let cn = issuer.iter_common_name().next().unwrap(); + cn.as_str().unwrap().to_string() } -pub fn extract_fmspc_from_extension<'a>(cert: &'a X509Certificate<'a>) -> [u8; 6] { +fn extract_fmspc_from_extension<'a>(cert: &'a X509Certificate<'a>) -> [u8; 6] { let sgx_extensions_bytes = cert .get_extension_unique(&oid!(1.2.840 .113741 .1 .13 .1)) .unwrap()