1use anyhow::{anyhow, bail, Context, Result};
2use asn1_der::{
3 typed::{DerDecodable, Sequence},
4 DerObject,
5};
6
7#[cfg(feature = "default-x509")]
8use crate::configs::DefaultConfig;
9use crate::{
10 config::{Config, ParsedCert, PckCa, X509Codec},
11 constants::{self, CpuSvn, Fmspc, Svn},
12 oids,
13 quote::{AuthData, Quote},
14 utils,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct PckExtension {
20 pub ppid: Vec<u8>,
21 pub cpu_svn: CpuSvn,
22 pub pce_svn: Svn,
23 pub pce_id: Vec<u8>,
24 pub fmspc: Fmspc,
25 pub sgx_type: u64,
26 pub platform_instance_id: Option<Vec<u8>>,
27 pub raw_extension: Vec<u8>,
28 pub dynamic_platform: Option<bool>,
31 pub cached_keys: Option<bool>,
34 pub smt_enabled: Option<bool>,
37}
38
39impl PckExtension {
40 pub fn get_value(&self, oid: &const_oid::ObjectIdentifier) -> Result<Option<Vec<u8>>> {
45 let obj = DerObject::decode(&self.raw_extension).context("Failed to decode DER object")?;
46 find_recursive(oid, obj, 0)
47 }
48}
49
50const MAX_DER_RECURSION_DEPTH: usize = 10;
51
52pub fn extract_cert_chain(quote: &Quote) -> Result<Vec<Vec<u8>>> {
57 if let Ok(chain_bytes) = quote.raw_cert_chain() {
58 let certs = utils::extract_certs(chain_bytes)?;
59 return Ok(certs
60 .into_iter()
61 .map(|cert| cert.as_ref().to_vec())
62 .collect());
63 }
64
65 let cert_data = match "e.auth_data {
66 AuthData::V3(data) => &data.certification_data,
67 AuthData::V4(data) => &data.qe_report_data.certification_data,
68 };
69 if cert_data.cert_type == constants::PCK_ID_PCK_CERTIFICATE {
70 return Ok(vec![cert_data.body.data.clone()]);
71 }
72
73 bail!(
74 "Certification data type {} is not supported (expecting 4 or 5)",
75 cert_data.cert_type
76 );
77}
78
79pub fn parse_pck_extension_with<C: Config>(cert_der: &[u8]) -> Result<PckExtension> {
81 let extension = utils::get_intel_extension_with::<C>(cert_der)?;
82
83 let ppid = find_extension_required(&[oids::PPID], &extension)?;
84 let cpu_svn = utils::get_cpu_svn(&extension)?;
85 let pce_svn = utils::get_pce_svn(&extension)?;
86 let pce_id = find_extension_required(&[oids::PCEID], &extension)?;
87 let fmspc = utils::get_fmspc(&extension)?;
88 let sgx_type = decode_enumerated(&find_extension_required(&[oids::SGX_TYPE], &extension)?)?;
89 let platform_instance_id = find_extension_optional(&[oids::PLATFORM_INSTANCE_ID], &extension)?;
90
91 let dynamic_platform =
93 find_extension_optional(&[oids::CONFIGURATION, oids::DYNAMIC_PLATFORM], &extension)?
94 .map(|v| decode_boolean(&v))
95 .transpose()?;
96 let cached_keys =
97 find_extension_optional(&[oids::CONFIGURATION, oids::CACHED_KEYS], &extension)?
98 .map(|v| decode_boolean(&v))
99 .transpose()?;
100 let smt_enabled =
101 find_extension_optional(&[oids::CONFIGURATION, oids::SMT_ENABLED], &extension)?
102 .map(|v| decode_boolean(&v))
103 .transpose()?;
104
105 Ok(PckExtension {
106 ppid,
107 cpu_svn,
108 pce_svn,
109 pce_id,
110 fmspc,
111 sgx_type,
112 platform_instance_id,
113 raw_extension: extension,
114 dynamic_platform,
115 cached_keys,
116 smt_enabled,
117 })
118}
119
120#[cfg(feature = "default-x509")]
125pub fn parse_pck_extension(cert_der: &[u8]) -> Result<PckExtension> {
126 parse_pck_extension_with::<DefaultConfig>(cert_der)
127}
128
129pub fn parse_pck_extension_from_pem_with<C: Config>(pem_data: &[u8]) -> Result<PckExtension> {
131 let certs = utils::extract_certs(pem_data)?;
132 let leaf = certs
133 .first()
134 .ok_or_else(|| anyhow!("No certificates found in PEM chain"))?;
135 parse_pck_extension_with::<C>(leaf)
136}
137
138#[cfg(feature = "default-x509")]
144pub fn parse_pck_extension_from_pem(pem_data: &[u8]) -> Result<PckExtension> {
145 parse_pck_extension_from_pem_with::<DefaultConfig>(pem_data)
146}
147
148pub fn pck_ca_with<C: Config>(cert_der: &[u8]) -> Result<PckCa> {
155 let parsed = C::X509::from_der(cert_der).context("Failed to decode certificate")?;
156 Ok(parsed.pck_ca().unwrap_or(PckCa::Processor))
160}
161
162#[cfg(feature = "default-x509")]
164pub fn pck_ca(cert_der: &[u8]) -> Result<PckCa> {
165 pck_ca_with::<DefaultConfig>(cert_der)
166}
167
168pub fn quote_fmspc_with<C: Config>(quote: &Quote) -> Result<Fmspc> {
173 let chain = extract_cert_chain(quote)?;
174 let leaf = chain.first().context("Empty PCK certificate chain")?;
175 Ok(parse_pck_extension_with::<C>(leaf)?.fmspc)
176}
177
178#[cfg(feature = "default-x509")]
180pub fn quote_fmspc(quote: &Quote) -> Result<Fmspc> {
181 quote_fmspc_with::<DefaultConfig>(quote)
182}
183
184pub fn quote_ca_with<C: Config>(quote: &Quote) -> Result<PckCa> {
188 let chain = extract_cert_chain(quote)?;
189 let leaf = chain.first().context("Empty PCK certificate chain")?;
190 pck_ca_with::<C>(leaf)
191}
192
193#[cfg(feature = "default-x509")]
195pub fn quote_ca(quote: &Quote) -> Result<PckCa> {
196 quote_ca_with::<DefaultConfig>(quote)
197}
198
199fn find_extension_required(
200 path: &[const_oid::ObjectIdentifier],
201 extension: &[u8],
202) -> Result<Vec<u8>> {
203 find_extension_optional(path, extension)?
204 .ok_or_else(|| anyhow!("Intel extension path {path:?} is missing"))
205}
206
207fn find_extension_optional(
208 path: &[const_oid::ObjectIdentifier],
209 extension: &[u8],
210) -> Result<Option<Vec<u8>>> {
211 let mut obj = DerObject::decode(extension).context("Failed to decode DER object")?;
212 for oid in path {
213 let seq = Sequence::load(obj).context("Failed to load sequence")?;
214 match sub_object_opt(oid, seq)? {
215 Some(value) => obj = value,
216 None => return Ok(None),
217 }
218 }
219 Ok(Some(obj.value().to_vec()))
220}
221
222fn sub_object_opt<'a>(
223 oid: &const_oid::ObjectIdentifier,
224 seq: Sequence<'a>,
225) -> Result<Option<DerObject<'a>>> {
226 for idx in 0..seq.len() {
227 let entry = seq
228 .get(idx)
229 .context("Failed to read entry inside Intel extension")?;
230 let entry_seq = Sequence::load(entry).context("Failed to load nested sequence")?;
231 let name = entry_seq.get(0).context("Failed to read OID")?;
232 let value = entry_seq.get(1).context("Failed to read value")?;
233 if name.value() == oid.as_bytes() {
234 return Ok(Some(value));
235 }
236 }
237 Ok(None)
238}
239
240fn find_recursive<'a>(
241 oid: &const_oid::ObjectIdentifier,
242 obj: DerObject<'a>,
243 depth: usize,
244) -> Result<Option<Vec<u8>>> {
245 if depth > MAX_DER_RECURSION_DEPTH {
246 bail!("DER recursion depth exceeded");
247 }
248 let seq = match Sequence::load(obj) {
249 Ok(s) => s,
250 Err(_) => return Ok(None),
251 };
252 for idx in 0..seq.len() {
253 let entry = match seq.get(idx) {
254 Ok(e) => e,
255 Err(_) => continue,
256 };
257 let entry_seq = match Sequence::load(entry) {
258 Ok(s) => s,
259 Err(_) => continue,
260 };
261 let name = match entry_seq.get(0) {
262 Ok(n) => n,
263 Err(_) => continue,
264 };
265 let value = match entry_seq.get(1) {
266 Ok(v) => v,
267 Err(_) => continue,
268 };
269 if name.value() == oid.as_bytes() {
270 return Ok(Some(value.value().to_vec()));
271 }
272 if value.tag() == 0x30 {
274 let next_depth = depth
275 .checked_add(1)
276 .context("DER recursion depth overflow")?;
277 if let Some(found) = find_recursive(oid, value, next_depth)? {
278 return Ok(Some(found));
279 }
280 }
281 }
282 Ok(None)
283}
284
285fn decode_boolean(bytes: &[u8]) -> Result<bool> {
286 match bytes[..] {
287 [0x00] => Ok(false),
288 [_] => Ok(true),
289 _ => bail!("Unexpected BOOLEAN length: {}", bytes.len()),
290 }
291}
292
293fn decode_enumerated(bytes: &[u8]) -> Result<u64> {
294 match bytes[..] {
295 [byte0] => Ok(u64::from(byte0)),
296 [byte0, byte1] => Ok(u16::from_be_bytes([byte0, byte1]) as u64),
297 _ => bail!("Unexpected ENUMERATED length"),
298 }
299}