ot_certs/x509/
extension.rs1use anyhow::{Context, Result, bail, ensure};
6use num_bigint_dig::BigUint;
7
8use foreign_types::{ForeignType, ForeignTypeRef};
9use openssl::asn1::{Asn1Object, Asn1ObjectRef, Asn1OctetStringRef};
10use openssl::x509::X509;
11
12use crate::asn1::Oid;
13use crate::template::{
14 BasicConstraints, CertificateExtension, DiceTcbInfoExtension, DiceTcbInfoFlags, FirmwareId,
15 HashAlgorithm, KeyUsage, Value,
16};
17
18pub struct X509ExtensionRef<'a> {
20 pub object: &'a Asn1ObjectRef,
22 pub critical: bool,
24 pub data: &'a Asn1OctetStringRef,
26}
27
28pub fn x509_get_extensions(x509: &X509) -> Result<Vec<X509ExtensionRef<'_>>> {
30 let mut exts = Vec::new();
31 let ext_count = unsafe { openssl_sys::X509_get_ext_count(x509.as_ptr()) };
33
34 for index in 0..ext_count {
35 let ext = unsafe { openssl_sys::X509_get_ext(x509.as_ptr(), index) };
42
43 let critical = unsafe { openssl_sys::X509_EXTENSION_get_critical(ext) };
45 let critical = match critical {
48 0 => false,
49 1 => true,
50 _ => bail!("openssl returned non-boolean critical marker for extension {index}"),
51 };
52
53 let object = unsafe {
56 let data = openssl_sys::X509_EXTENSION_get_object(ext);
59 Asn1ObjectRef::from_ptr(data)
60 };
61
62 let data = unsafe {
65 let data = openssl_sys::X509_EXTENSION_get_data(ext);
68 Asn1OctetStringRef::from_ptr(data)
69 };
70
71 exts.push(X509ExtensionRef {
72 object,
73 critical,
74 data,
75 })
76 }
77
78 Ok(exts)
79}
80
81#[derive(asn1::Asn1Read)]
113struct Fwid<'a> {
114 pub hash_alg: asn1::ObjectIdentifier,
115 pub digest: &'a [u8],
116}
117
118#[derive(asn1::Asn1Read)]
123struct DiceTcbInfo<'a> {
124 #[implicit(0)]
125 pub vendor: Option<asn1::Utf8String<'a>>,
126 #[implicit(1)]
127 pub model: Option<asn1::Utf8String<'a>>,
128 #[implicit(2)]
129 pub version: Option<asn1::Utf8String<'a>>,
130 #[implicit(3)]
131 pub svn: Option<asn1::BigInt<'a>>,
132 #[implicit(4)]
133 pub layer: Option<asn1::BigInt<'a>>,
134 #[implicit(5)]
135 pub index: Option<asn1::BigInt<'a>>,
136 #[implicit(6)]
137 pub fwids: Option<asn1::SequenceOf<'a, Fwid<'a>>>,
138 #[implicit(7)]
139 pub flags: Option<DiceTcbInfoFlags>,
140 #[implicit(8)]
141 pub vendor_info: Option<&'a [u8]>,
142 #[implicit(9)]
143 pub tcb_type: Option<&'a [u8]>,
144}
145
146fn convert_hash_algorithm(objid: &asn1::ObjectIdentifier) -> Result<HashAlgorithm> {
147 for (oid, hashalg) in [(Oid::Sha256, HashAlgorithm::Sha256)] {
148 if *objid
149 == asn1::ObjectIdentifier::from_string(oid.oid())
150 .expect("Cannot convert Oid to asn1::ObjectIdentifier")
151 {
152 return Ok(hashalg);
153 }
154 }
155 bail!("unsupported hash algorithm {}", objid);
156}
157
158fn asn1utf8_to_str(s: &asn1::Utf8String) -> Value<String> {
159 Value::literal(s.as_str().to_string())
160}
161
162fn asn1bigint_to_bn(bn: &asn1::BigInt) -> Value<BigUint> {
163 Value::literal(BigUint::from_bytes_be(bn.as_bytes()))
164}
165
166impl DiceTcbInfo<'_> {
167 fn to_dice_extension(&self) -> Result<DiceTcbInfoExtension> {
168 let fw_ids = self
169 .fwids
170 .as_ref()
171 .map(|fwids| {
172 fwids
173 .clone()
174 .map(|fwid| {
175 Ok(FirmwareId {
176 hash_algorithm: convert_hash_algorithm(&fwid.hash_alg)
177 .context("unknown hash algorithm")?,
178 digest: Value::literal(fwid.digest.to_vec()),
179 })
180 })
181 .collect::<Result<Vec<_>>>()
182 })
183 .transpose()
184 .context("cannot parse DICE TCB firmware IDs")?;
185
186 ensure!(
188 self.index.is_none(),
189 "the parser does not support DICE TCB index"
190 );
191 ensure!(
192 self.vendor_info.is_none(),
193 "the parser does not support DICE TCB vendor info"
194 );
195 ensure!(
196 self.tcb_type.is_none(),
197 "the parser does not support DICE TCB type"
198 );
199
200 Ok(DiceTcbInfoExtension {
201 model: self.model.as_ref().map(asn1utf8_to_str),
202 vendor: self.vendor.as_ref().map(asn1utf8_to_str),
203 version: self.version.as_ref().map(asn1utf8_to_str),
204 svn: self.svn.as_ref().map(asn1bigint_to_bn),
205 layer: self.layer.as_ref().map(asn1bigint_to_bn),
206 fw_ids,
207 flags: self.flags.clone(),
208 })
209 }
210}
211
212impl<'a> asn1::SimpleAsn1Readable<'a> for DiceTcbInfoFlags {
213 const TAG: asn1::Tag = asn1::OwnedBitString::TAG;
214 fn parse_data(_data: &'a [u8]) -> asn1::ParseResult<Self> {
215 let result = asn1::OwnedBitString::parse_data(_data)?;
216 let bs = result.as_bitstring();
217 if bs.as_bytes().len() != 1 || bs.padding_bits() != 4 {
218 asn1::ParseResult::Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidLength))
220 } else {
221 Ok(DiceTcbInfoFlags {
222 not_configured: Value::Literal(bs.has_bit_set(0)),
223 not_secure: Value::Literal(bs.has_bit_set(1)),
224 recovery: Value::Literal(bs.has_bit_set(2)),
225 debug: Value::Literal(bs.has_bit_set(3)),
226 })
227 }
228 }
229}
230
231impl<'a> asn1::SimpleAsn1Readable<'a> for KeyUsage {
244 const TAG: asn1::Tag = asn1::OwnedBitString::TAG;
245 fn parse_data(_data: &'a [u8]) -> asn1::ParseResult<Self> {
246 let result = asn1::OwnedBitString::parse_data(_data)?;
247 let bs = result.as_bitstring();
248 const PARSED_BITS: &[usize] = &[0, 4, 5];
251 let len = bs.as_bytes().len() * 8 - bs.padding_bits() as usize;
252 for i in 0..len {
253 if bs.has_bit_set(i) && !PARSED_BITS.contains(&i) {
254 return asn1::ParseResult::Err(asn1::ParseError::new(
257 asn1::ParseErrorKind::ExtraData,
258 ));
259 }
260 }
261 Ok(KeyUsage {
262 digital_signature: Some(Value::Literal(bs.has_bit_set(0))),
263 key_agreement: Some(Value::Literal(bs.has_bit_set(4))),
264 cert_sign: Some(Value::Literal(bs.has_bit_set(5))),
265 })
266 }
267}
268
269#[derive(asn1::Asn1Read)]
277struct AuthorityKeyIdentifier<'a> {
278 #[implicit(0)]
279 pub key_id: Option<&'a [u8]>,
280}
281
282pub fn parse_dice_tcb_info_extension(ext: &[u8]) -> Result<DiceTcbInfoExtension> {
284 asn1::parse_single::<DiceTcbInfo>(ext)
285 .context("cannot parse DICE extension")?
286 .to_dice_extension()
287}
288
289#[derive(asn1::Asn1Read)]
294struct BasicConstraintsInternal {
295 ca: bool,
296}
297
298impl BasicConstraintsInternal {
299 fn to_basic_constraints(&self) -> Result<BasicConstraints> {
300 Ok(BasicConstraints {
301 ca: Value::Literal(self.ca),
302 })
303 }
304}
305
306pub fn parse_key_usage(ext: &X509ExtensionRef) -> Result<KeyUsage> {
307 Ok(asn1::parse_single::<KeyUsage>(ext.data.as_slice())?)
308}
309
310pub fn parse_basic_constraints(ext: &X509ExtensionRef) -> Result<BasicConstraints> {
311 asn1::parse_single::<BasicConstraintsInternal>(ext.data.as_slice())
312 .context("cannot parse DICE extension")?
313 .to_basic_constraints()
314}
315
316pub fn parse_authority_key_id(ext: &X509ExtensionRef) -> Result<Vec<u8>> {
317 let auth = asn1::parse_single::<AuthorityKeyIdentifier>(ext.data.as_slice())?;
318 Ok(auth
319 .key_id
320 .context("authority key identifier extension is empty")?
321 .to_vec())
322}
323
324pub fn parse_subject_key_id(ext: &X509ExtensionRef) -> Result<Vec<u8>> {
325 let subj = asn1::parse_single::<&[u8]>(ext.data.as_slice())?;
330 Ok(subj.to_vec())
331}
332
333pub fn parse_extension(ext: &X509ExtensionRef) -> Result<CertificateExtension> {
335 let dice_oid =
336 Asn1Object::from_str(Oid::DiceTcbInfo.oid()).expect("cannot create object ID from string");
337 Ok(match ext.object.to_owned().as_slice() {
339 obj if obj == dice_oid.as_slice() => {
340 CertificateExtension::DiceTcbInfo(parse_dice_tcb_info_extension(ext.data.as_slice())?)
341 }
342 _ => bail!("unknown extension type {}", ext.object,),
343 })
344}