Skip to content

Commit

Permalink
[ot_certs] Add support for custom key usage
Browse files Browse the repository at this point in the history
Signed-off-by: Amaury Pouly <[email protected]>
  • Loading branch information
pamaury committed Aug 30, 2024
1 parent cf32d0f commit 764117f
Show file tree
Hide file tree
Showing 12 changed files with 131 additions and 16 deletions.
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/cert/cdi_0.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,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",
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/cert/cdi_1.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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",
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/cert/tpm_ek.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@
authority_key_identifier: { var: "auth_key_key_id" },
subject_key_identifier: { var: "tpm_ek_pub_key_id" },
basic_constraints: { ca: false },
key_usage: { cert_sign: true },
subject_alt_name: [
{ tpm_vendor: { var: "tpm_vendor" } },
{ tpm_model: { var: "tpm_model" } },
Expand Down
1 change: 1 addition & 0 deletions sw/device/silicon_creator/lib/cert/uds.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
24 changes: 14 additions & 10 deletions sw/host/ot_certs/src/asn1/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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),
Expand All @@ -284,21 +286,23 @@ impl X509 {
// cRLSign (6),
// encipherOnly (7),
// decipherOnly (8) }
//
// From the Open Profile for DICE specification:
// https://pigweed.googlesource.com/open-dice/+/refs/heads/main/docs/specification.md#certificate-details
// The standard extensions are fixed by the specification.
Self::push_extension(builder, &Oid::KeyUsage, true, |builder| {
builder.push_bitstring(
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),
Expand Down
14 changes: 14 additions & 0 deletions sw/host/ot_certs/src/template/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -562,6 +571,11 @@ 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")),
Expand Down
27 changes: 25 additions & 2 deletions sw/host/ot_certs/src/template/subst.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down
10 changes: 8 additions & 2 deletions sw/host/ot_certs/src/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -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.
Expand All @@ -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 => (),
Expand Down Expand Up @@ -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)?,
Expand Down
44 changes: 43 additions & 1 deletion sw/host/ot_certs/src/x509/extension.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -228,6 +228,44 @@ impl<'a> asn1::SimpleAsn1Readable<'a> for DiceTcbInfoFlags {
}
}

// From https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3
// KeyUsage ::= BIT STRING {
// digitalSignature (0),
// nonRepudiation (1), -- recent editions of X.509 have
// -- renamed this bit to contentCommitment
// keyEncipherment (2),
// dataEncipherment (3),
// keyAgreement (4),
// keyCertSign (5),
// cRLSign (6),
// encipherOnly (7),
// decipherOnly (8) }
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 but the asn1
// does not support arbitrary string errors.
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)
Expand All @@ -252,6 +290,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")?
Expand Down
5 changes: 5 additions & 0 deletions sw/host/ot_certs/tests/example.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 4 additions & 1 deletion sw/host/ot_certs/tests/example_data.json
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
14 changes: 14 additions & 0 deletions sw/host/ot_certs/tests/generic.hjson
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit 764117f

Please sign in to comment.