Skip to content

Commit

Permalink
Encode SubjectAlternativeName extension
Browse files Browse the repository at this point in the history
Currently, only the otherName choice is supported.
  • Loading branch information
sree-revoori1 authored and jhand2 committed Mar 26, 2024
1 parent 8690f1f commit 98c844b
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 6 deletions.
9 changes: 8 additions & 1 deletion dpe/src/commands/certify_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use caliptra_cfi_lib_git::cfi_launder;
use caliptra_cfi_lib_git::{cfi_assert, cfi_assert_eq};
use cfg_if::cfg_if;
use crypto::{Crypto, Hasher};
use platform::{Platform, MAX_ISSUER_NAME_SIZE, MAX_KEY_IDENTIFIER_SIZE};
use platform::{Platform, PlatformError, MAX_ISSUER_NAME_SIZE, MAX_KEY_IDENTIFIER_SIZE};

#[repr(C)]
#[derive(Debug, PartialEq, Eq, zerocopy::FromBytes, zerocopy::AsBytes)]
Expand Down Expand Up @@ -142,13 +142,20 @@ impl CommandExecution for CertifyKeyCmd {
env.platform
.get_issuer_key_identifier(&mut authority_key_identifier)?;

let subject_alt_name = match env.platform.get_subject_alternative_name() {
Ok(subject_alt_name) => Some(subject_alt_name),
Err(PlatformError::NotImplemented) => None,
Err(e) => Err(DpeErrorCode::Platform(e))?,
};

let measurements = MeasurementData {
label: &self.label,
tci_nodes: &nodes[..tcb_count],
is_ca: self.uses_is_ca(),
supports_recursive: dpe.support.recursive(),
subject_key_identifier,
authority_key_identifier,
subject_alt_name,
};

let mut issuer_name = [0u8; MAX_ISSUER_NAME_SIZE];
Expand Down
187 changes: 184 additions & 3 deletions dpe/src/x509.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ use crate::{
};
use bitflags::bitflags;
use crypto::{EcdsaPub, EcdsaSig};
use platform::{CertValidity, SignerIdentifier, MAX_KEY_IDENTIFIER_SIZE};
use platform::{
CertValidity, OtherName, SignerIdentifier, SubjectAltName, MAX_KEY_IDENTIFIER_SIZE,
};

pub enum DirectoryString<'a> {
PrintableString(&'a [u8]),
Expand Down Expand Up @@ -51,6 +53,7 @@ pub struct MeasurementData<'a> {
pub supports_recursive: bool,
pub subject_key_identifier: [u8; MAX_KEY_IDENTIFIER_SIZE],
pub authority_key_identifier: [u8; MAX_KEY_IDENTIFIER_SIZE],
pub subject_alt_name: Option<SubjectAltName>,
}

pub struct CertWriter<'a> {
Expand Down Expand Up @@ -145,6 +148,9 @@ impl CertWriter<'_> {
// RFC 5280 2.5.29.35
const AUTHORITY_KEY_IDENTIFIER_OID: &'static [u8] = &[0x55, 0x1D, 0x23];

// RFC 5280 2.5.29.17
const SUBJECT_ALTERNATIVE_NAME_OID: &'static [u8] = &[0x55, 0x1D, 0x11];

// RFC 5652 1.2.840.113549.1.7.2
const ID_SIGNED_DATA_OID: &'static [u8] =
&[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02];
Expand Down Expand Up @@ -502,6 +508,49 @@ impl CertWriter<'_> {
Self::get_structure_size(size, tagged)
}

fn get_subject_alt_name_extension_size(
measurements: &MeasurementData,
tagged: bool,
) -> Result<usize, DpeErrorCode> {
match &measurements.subject_alt_name {
None => Ok(0),
Some(SubjectAltName::OtherName(other_name)) => {
let san_size = Self::get_other_name_size(other_name, /*tagged=*/ true)?;

// Extension data is sequence -> octet string. To compute size, wrap
// in tagging twice.
let ext_size = Self::get_structure_size(san_size, /*tagged=*/ true)?;
let size = Self::get_structure_size(Self::SUBJECT_ALTERNATIVE_NAME_OID.len(), /*tagged=*/true)? // Extension OID
+ Self::get_structure_size(Self::BOOL_SIZE, /*tagged=*/true)? // Critical bool
+ Self::get_structure_size(ext_size, /*tagged=*/true)?; // OCTET STRING

Self::get_structure_size(size, tagged)
}
}
}

fn get_other_name_size(other_name: &OtherName, tagged: bool) -> Result<usize, DpeErrorCode> {
let size = Self::get_structure_size(other_name.oid.len(), /*tagged=*/ true)?
+ Self::get_other_name_value_size(
other_name.other_name.as_slice(),
/*tagged=*/ true,
/*explicit=*/ true,
)?;

Self::get_structure_size(size, tagged)
}

fn get_other_name_value_size(
other_name_value: &[u8],
tagged: bool,
explicit: bool,
) -> Result<usize, DpeErrorCode> {
// Determine whether to include the explicit tag wrapping in the size calculation
let size = Self::get_structure_size(other_name_value.len(), explicit)?;

Self::get_structure_size(size, tagged)
}

/// Get the size of the TBS Extensions field.
fn get_extensions_size(
measurements: &MeasurementData,
Expand All @@ -523,7 +572,8 @@ impl CertWriter<'_> {
measurements,
/*tagged=*/ true,
is_x509,
)?;
)?
+ Self::get_subject_alt_name_extension_size(measurements, /*tagged=*/ true)?;

// Determine whether to include the explicit tag wrapping in the size calculation
size = Self::get_structure_size(size, /*tagged=*/ explicit)?;
Expand Down Expand Up @@ -1402,6 +1452,104 @@ impl CertWriter<'_> {
Ok(bytes_written)
}

#[allow(clippy::identity_op)]
fn encode_other_name_value(&mut self, other_name_value: &[u8]) -> Result<usize, DpeErrorCode> {
// value is EXPLICIT field number 0
let mut bytes_written =
self.encode_byte(Self::CONTEXT_SPECIFIC | Self::CONSTRUCTED | 0x0)?;
bytes_written += self.encode_size_field(Self::get_other_name_value_size(
other_name_value,
/*tagged=*/ true,
/*explicit=*/ false,
)?)?;

// value := UTF8STRING
bytes_written += self.encode_tag_field(Self::UTF8_STRING_TAG)?;
bytes_written += self.encode_size_field(Self::get_other_name_value_size(
other_name_value,
/*tagged=*/ false,
/*explicit=*/ false,
)?)?;
bytes_written += self.encode_bytes(other_name_value)?;

Ok(bytes_written)
}

/// OtherName ::= SEQUENCE {
/// type-id OBJECT IDENTIFIER,
/// value [0] EXPLICIT ANY DEFINED BY type-id
/// }
#[allow(clippy::identity_op)]
fn encode_other_name(&mut self, other_name: &OtherName) -> Result<usize, DpeErrorCode> {
// otherName is EXPLICIT field number 0
let mut bytes_written =
self.encode_byte(Self::CONTEXT_SPECIFIC | Self::CONSTRUCTED | 0x0)?;

bytes_written += self.encode_size_field(Self::get_other_name_size(
other_name, /*tagged=*/ false,
)?)?;
bytes_written += self.encode_oid(other_name.oid)?;
bytes_written += self.encode_other_name_value(other_name.other_name.as_slice())?;

Ok(bytes_written)
}

/// SubjectAltName ::= GeneralNames
///
/// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
///
/// GeneralName ::= CHOICE {
/// otherName [0] OtherName,
/// rfc822Name [1] IA5String,
/// dNSName [2] IA5String,
/// x400Address [3] ORAddress,
/// directoryName [4] Name,
/// ediPartyName [5] EDIPartyName,
/// uniformResourceIdentifier [6] IA5String,
/// iPAddress [7] OCTET STRING,
/// registeredID [8] OBJECT IDENTIFIER
/// }
///
/// Currently, only otherName is supported.
fn encode_subject_alt_name_extension(
&mut self,
measurements: &MeasurementData,
) -> Result<usize, DpeErrorCode> {
match &measurements.subject_alt_name {
None => Ok(0),
Some(SubjectAltName::OtherName(other_name)) => {
// Encode Extension
let san_extension_size = Self::get_subject_alt_name_extension_size(
measurements,
/*tagged=*/ false,
)?;
let mut bytes_written = self.encode_byte(Self::SEQUENCE_TAG)?;
bytes_written += self.encode_size_field(san_extension_size)?;
bytes_written += self.encode_oid(Self::SUBJECT_ALTERNATIVE_NAME_OID)?;

bytes_written += self.encode_byte(Self::BOOL_TAG)?;
bytes_written += self.encode_size_field(Self::BOOL_SIZE)?;
// authority key identifier extension must NOT be marked critical
bytes_written += self.encode_byte(0x00)?;

// Extension data is sequence -> octet string. To compute size, wrap
// in tagging once.
let other_name_size = Self::get_other_name_size(other_name, /*tagged=*/ true)?;
bytes_written += self.encode_byte(Self::OCTET_STRING_TAG)?;
bytes_written += self.encode_size_field(Self::get_structure_size(
other_name_size,
/*tagged=*/ true,
)?)?;

bytes_written += self.encode_byte(Self::SEQUENCE_TAG)?;
bytes_written += self.encode_size_field(other_name_size)?;
bytes_written += self.encode_other_name(other_name)?;

Ok(bytes_written)
}
}
}

/// AuthorityKeyIdentifier ::= SEQUENCE {
/// keyIdentifier [0] KeyIdentifier OPTIONAL,
/// authorityCertIssuer [1] GeneralNames OPTIONAL,
Expand Down Expand Up @@ -1526,6 +1674,7 @@ impl CertWriter<'_> {
bytes_written += self.encode_extended_key_usage(measurements)?;
bytes_written += self.encode_subject_key_identifier_extension(measurements, is_x509)?;
bytes_written += self.encode_authority_key_identifier_extension(measurements, is_x509)?;
bytes_written += self.encode_subject_alt_name_extension(measurements)?;

Ok(bytes_written)
}
Expand Down Expand Up @@ -2049,7 +2198,7 @@ pub(crate) mod tests {
use crate::{DpeProfile, DPE_PROFILE};
use crypto::{CryptoBuf, EcdsaPub, EcdsaSig};
use openssl::hash::{Hasher, MessageDigest};
use platform::{ArrayVec, CertValidity, MAX_KEY_IDENTIFIER_SIZE};
use platform::{ArrayVec, CertValidity, OtherName, SubjectAltName, MAX_KEY_IDENTIFIER_SIZE};
use std::str;
use x509_parser::certificate::X509CertificateParser;
use x509_parser::nom::Parser;
Expand Down Expand Up @@ -2295,6 +2444,7 @@ pub(crate) mod tests {
supports_recursive: true,
subject_key_identifier: [0u8; MAX_KEY_IDENTIFIER_SIZE],
authority_key_identifier: [0u8; MAX_KEY_IDENTIFIER_SIZE],
subject_alt_name: None,
};

let mut not_before = ArrayVec::new();
Expand Down Expand Up @@ -2352,6 +2502,9 @@ pub(crate) mod tests {

const ECC_INT_SIZE: usize = DPE_PROFILE.get_ecc_int_size();

const DEFAULT_OTHER_NAME_OID: &'static [u8] = &[0, 0, 0];
const DEFAULT_OTHER_NAME_VALUE: &str = "default-other-name";

fn build_test_tbs<'a>(is_ca: bool, cert_buf: &'a mut [u8]) -> (usize, TbsCertificate<'a>) {
let mut issuer_der = [0u8; 1024];
let mut issuer_writer = CertWriter::new(&mut issuer_der, true);
Expand All @@ -2374,13 +2527,22 @@ pub(crate) mod tests {
let mut subject_key_identifier = [0u8; MAX_KEY_IDENTIFIER_SIZE];
let digest = &hasher.finish().unwrap();
subject_key_identifier.copy_from_slice(&digest[..MAX_KEY_IDENTIFIER_SIZE]);
let mut other_name = ArrayVec::new();
other_name
.try_extend_from_slice(DEFAULT_OTHER_NAME_VALUE.as_bytes())
.unwrap();
let subject_alt_name = SubjectAltName::OtherName(OtherName {
oid: DEFAULT_OTHER_NAME_OID,
other_name,
});
let measurements = MeasurementData {
label: &[0; DPE_PROFILE.get_hash_size()],
tci_nodes: &[node],
is_ca,
supports_recursive: true,
subject_key_identifier,
authority_key_identifier: subject_key_identifier,
subject_alt_name: Some(subject_alt_name),
};

let mut not_before = ArrayVec::new();
Expand Down Expand Up @@ -2488,6 +2650,25 @@ pub(crate) mod tests {
Err(_) => panic!("multiple authority key identifier extensions found"),
_ => (),
}

match cert.subject_alternative_name() {
Ok(Some(ext)) => {
assert!(!ext.critical);
let san = ext.value;
assert_eq!(san.general_names.len(), 1);
let general_name = san.general_names.get(0).unwrap();
match general_name {
GeneralName::OtherName(oid, other_name_value) => {
assert_eq!(oid.as_bytes(), DEFAULT_OTHER_NAME_OID);
// skip first 4 der encoding bytes
assert_eq!(&other_name_value[4..], DEFAULT_OTHER_NAME_VALUE.as_bytes());
}
_ => panic!("Wrong SubjectAlternativeName"),
};
}
Ok(None) => panic!("No SubjectAltName extension found!"),
Err(e) => panic!("Error {} parsing SubjectAltName extension", e.to_string()),
}
}

#[test]
Expand Down
8 changes: 6 additions & 2 deletions platform/src/default.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Licensed under the Apache-2.0 license

use crate::{
CertValidity, Platform, PlatformError, SignerIdentifier, MAX_CHUNK_SIZE, MAX_ISSUER_NAME_SIZE,
MAX_KEY_IDENTIFIER_SIZE,
CertValidity, Platform, PlatformError, SignerIdentifier, SubjectAltName, MAX_CHUNK_SIZE,
MAX_ISSUER_NAME_SIZE, MAX_KEY_IDENTIFIER_SIZE,
};
use arrayvec::ArrayVec;
use cfg_if::cfg_if;
Expand Down Expand Up @@ -197,4 +197,8 @@ impl Platform for DefaultPlatform {
not_after: not_after_vec,
})
}

fn get_subject_alternative_name(&mut self) -> Result<SubjectAltName, PlatformError> {
Err(PlatformError::NotImplemented)
}
}
21 changes: 21 additions & 0 deletions platform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub const MAX_ISSUER_NAME_SIZE: usize = 128;
pub const MAX_SN_SIZE: usize = 20;
pub const MAX_KEY_IDENTIFIER_SIZE: usize = 20;
pub const MAX_VALIDITY_SIZE: usize = 24;
pub const MAX_OTHER_NAME_SIZE: usize = 128;

#[derive(Debug, PartialEq, Eq)]
pub enum SignerIdentifier {
Expand All @@ -30,6 +31,17 @@ pub enum SignerIdentifier {
SubjectKeyIdentifier(ArrayVec<u8, { MAX_KEY_IDENTIFIER_SIZE }>),
}

#[derive(Debug, PartialEq, Eq)]
pub enum SubjectAltName {
OtherName(OtherName),
}

#[derive(Debug, PartialEq, Eq)]
pub struct OtherName {
pub oid: &'static [u8],
pub other_name: ArrayVec<u8, { MAX_OTHER_NAME_SIZE }>,
}

#[derive(Debug, PartialEq, Eq)]
pub struct CertValidity {
pub not_before: ArrayVec<u8, { MAX_VALIDITY_SIZE }>,
Expand All @@ -47,6 +59,7 @@ pub enum PlatformError {
SubjectKeyIdentifierError(u32) = 0x6,
CertValidityError(u32) = 0x7,
IssuerKeyIdentifierError(u32) = 0x8,
SubjectAlternativeNameError(u32) = 0x9,
}

impl PlatformError {
Expand All @@ -67,6 +80,7 @@ impl PlatformError {
PlatformError::SubjectKeyIdentifierError(code) => Some(*code),
PlatformError::CertValidityError(code) => Some(*code),
PlatformError::IssuerKeyIdentifierError(code) => Some(*code),
PlatformError::SubjectAlternativeNameError(code) => Some(*code),
}
}
}
Expand Down Expand Up @@ -125,4 +139,11 @@ pub trait Platform {
///
/// Example: 99991231235959Z is December 31st, 9999 23:59:59 UTC
fn get_cert_validity(&mut self) -> Result<CertValidity, PlatformError>;

/// Retrieves the SubjectAlternativeName extension
///
/// Currently, only the otherName choice is supported. This function
/// can be left unimplemented if the SubjectAlternativeName extension is
/// not needed in the DPE leaf cert or CSR.
fn get_subject_alternative_name(&mut self) -> Result<SubjectAltName, PlatformError>;
}

0 comments on commit 98c844b

Please sign in to comment.