Created
August 29, 2024 09:59
-
-
Save pamaury/9f7fd597f69a3c626ae86f58d7537b7b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/sw/device/silicon_creator/lib/cert/uds.hjson b/sw/device/silicon_creator/lib/cert/uds.hjson | |
index 6a9ec3621b..238a672f1b 100644 | |
--- a/sw/device/silicon_creator/lib/cert/uds.hjson | |
+++ b/sw/device/silicon_creator/lib/cert/uds.hjson | |
@@ -98,6 +98,7 @@ | |
// https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#certificate-details | |
// The standard extensions are fixed by the specification. | |
basic_constraints: { ca: true }, | |
+ key_usage: { cert_sign: true }, | |
private_extensions: [ | |
{ | |
type: "dice_tcb_info", | |
diff --git a/sw/host/ot_certs/src/asn1/x509.rs b/sw/host/ot_certs/src/asn1/x509.rs | |
index 72298b5567..103a920a16 100644 | |
--- a/sw/host/ot_certs/src/asn1/x509.rs | |
+++ b/sw/host/ot_certs/src/asn1/x509.rs | |
@@ -10,7 +10,7 @@ use crate::asn1::builder::{concat_suffix, Builder}; | |
use crate::asn1::{Oid, Tag}; | |
use crate::template::{ | |
AttributeType, BasicConstraints, Certificate, CertificateExtension, EcCurve, EcPublicKeyInfo, | |
- EcdsaSignature, HashAlgorithm, Name, Signature, SubjectPublicKeyInfo, Value, | |
+ EcdsaSignature, HashAlgorithm, KeyUsage, Name, Signature, SubjectPublicKeyInfo, Value, | |
}; | |
impl HashAlgorithm { | |
@@ -150,7 +150,9 @@ impl X509 { | |
Self::push_basic_constraints_ext(builder, constraints)?; | |
} | |
Self::push_subject_alt_name_ext(builder, &cert.subject_alt_name)?; | |
- Self::push_key_usage_ext(builder)?; | |
+ if let Some(key_usage) = &cert.key_usage { | |
+ Self::push_key_usage_ext(builder, key_usage)?; | |
+ } | |
Self::push_auth_key_id_ext(builder, &cert.authority_key_identifier)?; | |
Self::push_subject_key_id_ext(builder, &cert.subject_key_identifier)?; | |
for ext in &cert.private_extensions { | |
@@ -271,7 +273,7 @@ impl X509 { | |
}) | |
} | |
- pub fn push_key_usage_ext<B: Builder>(builder: &mut B) -> Result<()> { | |
+ pub fn push_key_usage_ext<B: Builder>(builder: &mut B, key_usage: &KeyUsage) -> Result<()> { | |
// From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3 | |
// KeyUsage ::= BIT STRING { | |
// digitalSignature (0), | |
@@ -293,12 +295,12 @@ impl X509 { | |
Some("key_usage".into()), | |
&Tag::BitString, | |
&[ | |
- /* digitalSignature */ Value::Literal(false), | |
+ key_usage.digital_signature.clone().unwrap_or(Value::literal(false)), | |
/* nonRepudiation */ Value::Literal(false), | |
/* keyEncipherment */ Value::Literal(false), | |
/* dataEncipherment */ Value::Literal(false), | |
- /* keyAgreement */ Value::Literal(false), | |
- /* keyCertSign */ Value::Literal(true), | |
+ key_usage.key_agreement.clone().unwrap_or(Value::literal(false)), | |
+ key_usage.cert_sign.clone().unwrap_or(Value::literal(false)), | |
/* cRLSign */ Value::Literal(false), | |
/* encipherOnly */ Value::Literal(false), | |
/* decipherOnly */ Value::Literal(false), | |
diff --git a/sw/host/ot_certs/src/template/mod.rs b/sw/host/ot_certs/src/template/mod.rs | |
index adc992235e..b6ac3393a8 100644 | |
--- a/sw/host/ot_certs/src/template/mod.rs | |
+++ b/sw/host/ot_certs/src/template/mod.rs | |
@@ -75,6 +75,7 @@ pub struct Certificate { | |
pub subject_key_identifier: Value<Vec<u8>>, | |
// X509 basic constraints extension, optional. | |
pub basic_constraints: Option<BasicConstraints>, | |
+ pub key_usage: Option<KeyUsage>, | |
/// X509 Subject Alternative Name extension, optional. | |
#[serde(default)] | |
pub subject_alt_name: Name, | |
@@ -99,6 +100,13 @@ pub struct BasicConstraints { | |
pub ca: Value<bool>, | |
} | |
+#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] | |
+pub struct KeyUsage { | |
+ pub digital_signature: Option<Value<bool>>, | |
+ pub key_agreement: Option<Value<bool>>, | |
+ pub cert_sign: Option<Value<bool>>, | |
+} | |
+ | |
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] | |
#[serde(tag = "type", rename_all = "snake_case")] | |
pub enum CertificateExtension { | |
@@ -466,6 +474,7 @@ mod tests { | |
}, | |
authority_key_identifier: { var: "signing_pub_key_id" }, | |
subject_key_identifier: { var: "owner_pub_key_id" }, | |
+ key_usage: { key_agreement: true }, | |
private_extensions: [ | |
{ | |
type: "dice_tcb_info", | |
@@ -562,6 +571,7 @@ mod tests { | |
authority_key_identifier: Value::variable("signing_pub_key_id"), | |
subject_key_identifier: Value::variable("owner_pub_key_id"), | |
basic_constraints: None, | |
+ key_usage: Some(KeyUsage { digital_signature: None, key_agreement: Some(Value::literal(true)), cert_sign: None }), | |
subject_alt_name: vec![], | |
private_extensions: vec![CertificateExtension::DiceTcbInfo(DiceTcbInfoExtension { | |
vendor: Some(Value::literal("OpenTitan")), | |
diff --git a/sw/host/ot_certs/src/template/subst.rs b/sw/host/ot_certs/src/template/subst.rs | |
index 42c3012dd1..69b84db140 100644 | |
--- a/sw/host/ot_certs/src/template/subst.rs | |
+++ b/sw/host/ot_certs/src/template/subst.rs | |
@@ -14,8 +14,8 @@ use serde::{Deserialize, Serialize}; | |
use crate::template::{ | |
BasicConstraints, Certificate, CertificateExtension, Conversion, DiceTcbInfoExtension, | |
- DiceTcbInfoFlags, EcPublicKey, EcPublicKeyInfo, EcdsaSignature, FirmwareId, Signature, | |
- SubjectPublicKeyInfo, Template, Value, Variable, VariableType, | |
+ DiceTcbInfoFlags, EcPublicKey, EcPublicKeyInfo, EcdsaSignature, FirmwareId, KeyUsage, | |
+ Signature, SubjectPublicKeyInfo, Template, Value, Variable, VariableType, | |
}; | |
/// Substitution value: this is the raw value loaded from a hjson/json file | |
@@ -362,6 +362,10 @@ impl Subst for Certificate { | |
.basic_constraints | |
.subst(data) | |
.context("cannot substitute basic constraints")?, | |
+ key_usage: self | |
+ .key_usage | |
+ .subst(data) | |
+ .context("cannot substitute key usage")?, | |
private_extensions: self | |
.private_extensions | |
.iter() | |
@@ -464,6 +468,25 @@ impl Subst for DiceTcbInfoFlags { | |
} | |
} | |
+impl Subst for KeyUsage { | |
+ fn subst(&self, data: &SubstData) -> Result<KeyUsage> { | |
+ Ok(KeyUsage { | |
+ digital_signature: self | |
+ .digital_signature | |
+ .subst(data) | |
+ .context("cannot substitute digital signature key usage")?, | |
+ key_agreement: self | |
+ .key_agreement | |
+ .subst(data) | |
+ .context("cannot substitute key agreement")?, | |
+ cert_sign: self | |
+ .cert_sign | |
+ .subst(data) | |
+ .context("cannot substitute cert sign")?, | |
+ }) | |
+ } | |
+} | |
+ | |
impl Subst for SubjectPublicKeyInfo { | |
fn subst(&self, data: &SubstData) -> Result<SubjectPublicKeyInfo> { | |
match self { | |
diff --git a/sw/host/ot_certs/src/x509.rs b/sw/host/ot_certs/src/x509.rs | |
index be131c4f75..a3d4859313 100644 | |
--- a/sw/host/ot_certs/src/x509.rs | |
+++ b/sw/host/ot_certs/src/x509.rs | |
@@ -22,7 +22,7 @@ use crate::asn1::der; | |
use crate::asn1::x509; | |
use crate::template::{ | |
- self, AttributeType, EcCurve, EcPublicKeyInfo, EcdsaSignature, Name, Signature, | |
+ self, AttributeType, EcCurve, EcPublicKeyInfo, EcdsaSignature, KeyUsage, Name, Signature, | |
SubjectPublicKeyInfo, Value, | |
}; | |
@@ -221,6 +221,7 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> { | |
extension::x509_get_extensions(&x509).context("could not parse X509 extensions")?; | |
let mut private_extensions = Vec::new(); | |
let mut basic_constraints = None; | |
+ let mut key_usage: Option<KeyUsage> = None; | |
for ext in raw_extensions { | |
match ext.object.nid() { | |
// Ignore extensions that are standard and handled by openssl. | |
@@ -234,7 +235,11 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> { | |
.context("could not parse X509 basic constraints")?, | |
); | |
} | |
- Nid::KEY_USAGE => (), | |
+ Nid::KEY_USAGE => { | |
+ key_usage = Some( | |
+ extension::parse_key_usage(&ext).context("could not parse X509 key usage")?, | |
+ ); | |
+ } | |
Nid::AUTHORITY_KEY_IDENTIFIER => (), | |
Nid::SUBJECT_ALT_NAME => (), | |
Nid::SUBJECT_KEY_IDENTIFIER => (), | |
@@ -266,6 +271,7 @@ pub fn parse_certificate(cert: &[u8]) -> Result<template::Certificate> { | |
.context("the certificate has not subject key id")?, | |
), | |
basic_constraints, | |
+ key_usage, | |
subject_alt_name: get_subject_alt_name(&x509)?, | |
private_extensions, | |
signature: extract_signature(&x509)?, | |
diff --git a/sw/host/ot_certs/src/x509/extension.rs b/sw/host/ot_certs/src/x509/extension.rs | |
index 37d6533881..0cf4dd773f 100644 | |
--- a/sw/host/ot_certs/src/x509/extension.rs | |
+++ b/sw/host/ot_certs/src/x509/extension.rs | |
@@ -12,7 +12,7 @@ use openssl::x509::X509; | |
use crate::asn1::Oid; | |
use crate::template::{ | |
BasicConstraints, CertificateExtension, DiceTcbInfoExtension, DiceTcbInfoFlags, FirmwareId, | |
- HashAlgorithm, Value, | |
+ HashAlgorithm, KeyUsage, Value, | |
}; | |
/// X509 extension reference. | |
@@ -228,6 +228,29 @@ impl<'a> asn1::SimpleAsn1Readable<'a> for DiceTcbInfoFlags { | |
} | |
} | |
+impl<'a> asn1::SimpleAsn1Readable<'a> for KeyUsage { | |
+ const TAG: asn1::Tag = asn1::OwnedBitString::TAG; | |
+ fn parse_data(_data: &'a [u8]) -> asn1::ParseResult<Self> { | |
+ let result = asn1::OwnedBitString::parse_data(_data)?; | |
+ let bs = result.as_bitstring(); | |
+ // List of bits that this parser supports. Any bit set outside of | |
+ // these will be an error because we cannot record it. | |
+ const PARSED_BITS: &[usize] = &[0, 4, 5]; | |
+ let len = bs.as_bytes().len() * 8 - bs.padding_bits() as usize; | |
+ for i in 0..len { | |
+ if bs.has_bit_set(i) && !PARSED_BITS.contains(&i) { | |
+ // FIXME This will not return a very readable error message, should improve this | |
+ return asn1::ParseResult::Err(asn1::ParseError::new(asn1::ParseErrorKind::ExtraData)) | |
+ } | |
+ } | |
+ Ok(KeyUsage { | |
+ digital_signature: Some(Value::Literal(bs.has_bit_set(0))), | |
+ key_agreement: Some(Value::Literal(bs.has_bit_set(4))), | |
+ cert_sign: Some(Value::Literal(bs.has_bit_set(5))), | |
+ }) | |
+ } | |
+} | |
+ | |
/// Try to parse an X509 extension as a DICE TCB info extension. | |
pub fn parse_dice_tcb_info_extension(ext: &[u8]) -> Result<DiceTcbInfoExtension> { | |
asn1::parse_single::<DiceTcbInfo>(ext) | |
@@ -252,6 +275,10 @@ impl BasicConstraintsInternal { | |
} | |
} | |
+pub fn parse_key_usage(ext: &X509ExtensionRef) -> Result<KeyUsage> { | |
+ Ok(asn1::parse_single::<KeyUsage>(ext.data.as_slice())?) | |
+} | |
+ | |
pub fn parse_basic_constraints(ext: &X509ExtensionRef) -> Result<BasicConstraints> { | |
asn1::parse_single::<BasicConstraintsInternal>(ext.data.as_slice()) | |
.context("cannot parse DICE extension")? | |
diff --git a/sw/host/ot_certs/tests/example.hjson b/sw/host/ot_certs/tests/example.hjson | |
index f63b4fb44c..47061ff97d 100644 | |
--- a/sw/host/ot_certs/tests/example.hjson | |
+++ b/sw/host/ot_certs/tests/example.hjson | |
@@ -40,6 +40,11 @@ | |
{ tpm_model: "TPM Model" }, | |
{ tpm_version: "TPM Version" }, | |
], | |
+ key_usage: { | |
+ digital_signature: true, | |
+ key_agreement: false, | |
+ cert_sign: true, | |
+ } | |
private_extensions: [ | |
{ | |
type: "dice_tcb_info", | |
diff --git a/sw/host/ot_certs/tests/example_data.json b/sw/host/ot_certs/tests/example_data.json | |
index a9e2fc31cd..9c97329dcb 100644 | |
--- a/sw/host/ot_certs/tests/example_data.json | |
+++ b/sw/host/ot_certs/tests/example_data.json | |
@@ -24,5 +24,8 @@ | |
"basic_constraints_ca": true, | |
"tpm_vendor": "TPM Vendor", | |
"tpm_model": "TPM Model", | |
- "tpm_version": "TPM Version" | |
+ "tpm_version": "TPM Version", | |
+ "key_usage_digital_signature": true, | |
+ "key_usage_key_agreement": false, | |
+ "key_usage_cert_sign": true | |
} | |
diff --git a/sw/host/ot_certs/tests/generic.hjson b/sw/host/ot_certs/tests/generic.hjson | |
index aea5c16132..87da5c2792 100644 | |
--- a/sw/host/ot_certs/tests/generic.hjson | |
+++ b/sw/host/ot_certs/tests/generic.hjson | |
@@ -104,6 +104,15 @@ | |
type: "string", | |
size: 100, | |
} | |
+ key_usage_cert_sign: { | |
+ type: "boolean" | |
+ } | |
+ key_usage_key_agreement: { | |
+ type: "boolean" | |
+ } | |
+ key_usage_digital_signature: { | |
+ type: "boolean" | |
+ } | |
}, | |
certificate: { | |
@@ -135,6 +144,11 @@ | |
{ tpm_model: { var: "tpm_model" } }, | |
{ tpm_version: { var: "tpm_version" } }, | |
], | |
+ key_usage: { | |
+ digital_signature: {var: "key_usage_digital_signature" }, | |
+ key_agreement: {var: "key_usage_key_agreement" }, | |
+ cert_sign: {var: "key_usage_cert_sign" }, | |
+ } | |
private_extensions: [ | |
{ | |
type: "dice_tcb_info", |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment