use crate::cert::x509::{
key_usage_der, parse_hex_u16, time_to_unix_secs, AlgorithmIdentifier, AttributeTypeAndValue,
AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectPublicKeyInfo, Validity,
OID_AUTHORITY_KEY_ID, OID_BASIC_CONSTRAINTS, OID_ECDSA_WITH_SHA256, OID_EC_PUBLIC_KEY,
OID_KEY_USAGE, OID_PRIME256V1, OID_SUBJECT_KEY_ID, P256_PUBLIC_KEY_LEN,
};
use crate::error::{Error, ErrorCode};
use der::asn1::{AnyRef, BitStringRef, ObjectIdentifier, OctetStringRef, UintRef};
use der::{
Decode, DecodeValue, EncodeValue, FixedTag, Header, Reader, Sequence, SliceReader, Tag,
TagNumber, Tagged,
};
pub type DacCert<'a> = X509Cert<'a, DacExtensions<'a>>;
pub type PaiCert<'a> = X509Cert<'a, PaiExtensions<'a>>;
pub type PaaCert<'a> = X509Cert<'a, PaaExtensions<'a>>;
const OID_MATTER_VENDOR_ID: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.2.1");
const OID_MATTER_PRODUCT_ID: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.3.6.1.4.1.37244.2.2");
struct Name<'a> {
raw_bytes: &'a [u8],
attrs: MatterDnAttrs,
}
impl<'a> der::FixedTag for Name<'a> {
const TAG: Tag = Tag::Sequence;
}
impl<'a> DecodeValue<'a> for Name<'a> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
let raw_bytes = reader.read_slice(header.length)?;
let attrs = MatterDnAttrs::parse(raw_bytes)?;
Ok(Self { raw_bytes, attrs })
}
}
#[derive(Debug, Clone, Default)]
pub struct MatterDnAttrs {
vendor_id: Option<u16>,
product_id: Option<u16>,
}
impl MatterDnAttrs {
fn parse(rdn_bytes: &[u8]) -> der::Result<Self> {
let mut vendor_id = None;
let mut product_id = None;
let mut outer = SliceReader::new(rdn_bytes)?;
while !outer.is_finished() {
let rdn_set = AnyRef::decode(&mut outer)?;
let mut set_reader = SliceReader::new(rdn_set.value())?;
while !set_reader.is_finished() {
let atv = AttributeTypeAndValue::decode(&mut set_reader)?;
if atv.oid == OID_MATTER_VENDOR_ID {
let value = atv.value;
vendor_id = Some(
parse_hex_u16(value.value())
.map_err(|_| der::ErrorKind::Value { tag: value.tag() })?,
);
} else if atv.oid == OID_MATTER_PRODUCT_ID {
let value = atv.value;
product_id = Some(
parse_hex_u16(value.value())
.map_err(|_| der::ErrorKind::Value { tag: value.tag() })?,
);
}
}
}
Ok(Self {
vendor_id,
product_id,
})
}
}
struct ParsedExtension<T> {
critical: bool,
value: T,
}
struct ParsedExtensionFields<'a> {
basic_constraints: Option<ParsedExtension<BasicConstraints>>,
key_usage: Option<ParsedExtension<KeyUsage>>,
subject_key_id: Option<ParsedExtension<OctetStringRef<'a>>>,
authority_key_id: Option<ParsedExtension<AuthorityKeyIdentifier<'a>>>,
}
impl<'a> ParsedExtensionFields<'a> {
fn parse<R: Reader<'a>>(reader: &mut R) -> der::Result<Self> {
let mut basic_constraints: Option<ParsedExtension<BasicConstraints>> = None;
let mut key_usage: Option<ParsedExtension<KeyUsage>> = None;
let mut subject_key_id: Option<ParsedExtension<OctetStringRef<'a>>> = None;
let mut authority_key_id: Option<ParsedExtension<AuthorityKeyIdentifier<'a>>> = None;
while !reader.is_finished() {
let ext_any = AnyRef::decode(reader)?;
let mut ext_reader = SliceReader::new(ext_any.value())?;
let extn_id = ObjectIdentifier::decode(&mut ext_reader)?;
let critical = if !ext_reader.is_finished() && ext_reader.peek_tag()? == Tag::Boolean {
bool::decode(&mut ext_reader)?
} else {
false
};
let extn_value = OctetStringRef::decode(&mut ext_reader)?;
let value_bytes = extn_value.as_bytes();
if extn_id == OID_BASIC_CONSTRAINTS {
let bc = BasicConstraints::from_der(value_bytes)?;
basic_constraints = Some(ParsedExtension {
critical,
value: bc,
});
} else if extn_id == OID_KEY_USAGE {
let bs = BitStringRef::from_der(value_bytes)?;
key_usage = Some(ParsedExtension {
critical,
value: bs.into(),
});
} else if extn_id == OID_SUBJECT_KEY_ID {
let skid = OctetStringRef::from_der(value_bytes)?;
subject_key_id = Some(ParsedExtension {
critical,
value: skid,
});
} else if extn_id == OID_AUTHORITY_KEY_ID {
let akid = AuthorityKeyIdentifier::from_der(value_bytes)?;
authority_key_id = Some(ParsedExtension {
critical,
value: akid,
});
} else {
if critical {
return Err(der::ErrorKind::Failed.into());
}
}
}
Ok(Self {
basic_constraints,
key_usage,
subject_key_id,
authority_key_id,
})
}
}
pub trait CertType<'a>: DecodeValue<'a> + der::FixedTag + der::EncodeValue {
fn subject_key_id(&self) -> Option<&'a [u8]>;
fn authority_key_id(&self) -> Option<&'a [u8]>;
fn validate_issuer_subject(
issuer_attrs: &MatterDnAttrs,
subject_attrs: &MatterDnAttrs,
issuer_raw: &[u8],
subject_raw: &[u8],
) -> der::Result<()>;
}
#[allow(unused)]
pub struct DacExtensions<'a> {
basic_constraints: ParsedExtension<BasicConstraints>,
key_usage: ParsedExtension<KeyUsage>,
subject_key_id: ParsedExtension<OctetStringRef<'a>>,
authority_key_id: ParsedExtension<AuthorityKeyIdentifier<'a>>,
}
impl<'a> DecodeValue<'a> for DacExtensions<'a> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
let fields = ParsedExtensionFields::parse(reader)?;
let basic_constraints = fields.basic_constraints.ok_or(der::ErrorKind::Failed)?;
let key_usage = fields.key_usage.ok_or(der::ErrorKind::Failed)?;
let subject_key_id = fields.subject_key_id.ok_or(der::ErrorKind::Failed)?;
let authority_key_id = fields.authority_key_id.ok_or(der::ErrorKind::Failed)?;
if !basic_constraints.critical {
return Err(der::ErrorKind::Failed.into());
}
if basic_constraints.value.ca {
return Err(der::ErrorKind::Failed.into());
}
if !key_usage.critical {
return Err(der::ErrorKind::Failed.into());
}
if !key_usage.value.digital_signature() {
return Err(der::ErrorKind::Failed.into());
}
if !key_usage
.value
.has_only_bits(key_usage_der::DIGITAL_SIGNATURE)
{
return Err(der::ErrorKind::Failed.into());
}
Ok(Self {
basic_constraints,
key_usage,
subject_key_id,
authority_key_id,
})
})
}
}
impl<'a> der::FixedTag for DacExtensions<'a> {
const TAG: Tag = Tag::Sequence;
}
impl<'a> der::EncodeValue for DacExtensions<'a> {
fn value_len(&self) -> der::Result<der::Length> {
unimplemented!("DacExtensions encoding is not supported")
}
fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
unimplemented!("DacExtensions encoding is not supported")
}
}
impl<'a> CertType<'a> for DacExtensions<'a> {
fn subject_key_id(&self) -> Option<&'a [u8]> {
Some(self.subject_key_id.value.as_bytes())
}
fn authority_key_id(&self) -> Option<&'a [u8]> {
Some(self.authority_key_id.value.key_identifier.as_bytes())
}
fn validate_issuer_subject(
issuer_attrs: &MatterDnAttrs,
subject_attrs: &MatterDnAttrs,
_issuer_raw: &[u8],
_subject_raw: &[u8],
) -> der::Result<()> {
let issuer_vid = issuer_attrs.vendor_id.ok_or(der::ErrorKind::Failed)?;
let subject_vid = subject_attrs.vendor_id.ok_or(der::ErrorKind::Failed)?;
let _subject_pid = subject_attrs.product_id.ok_or(der::ErrorKind::Failed)?;
if issuer_vid != subject_vid {
return Err(der::ErrorKind::Failed.into());
}
if let Some(issuer_pid) = issuer_attrs.product_id {
if let Some(subject_pid) = subject_attrs.product_id {
if issuer_pid != subject_pid {
return Err(der::ErrorKind::Failed.into());
}
}
}
Ok(())
}
}
#[allow(unused)]
pub struct PaiExtensions<'a> {
basic_constraints: ParsedExtension<BasicConstraints>,
key_usage: ParsedExtension<KeyUsage>,
subject_key_id: ParsedExtension<OctetStringRef<'a>>,
authority_key_id: ParsedExtension<AuthorityKeyIdentifier<'a>>,
}
impl<'a> DecodeValue<'a> for PaiExtensions<'a> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
let fields = ParsedExtensionFields::parse(reader)?;
let basic_constraints = fields.basic_constraints.ok_or(der::ErrorKind::Failed)?;
let key_usage = fields.key_usage.ok_or(der::ErrorKind::Failed)?;
let subject_key_id = fields.subject_key_id.ok_or(der::ErrorKind::Failed)?;
let authority_key_id = fields.authority_key_id.ok_or(der::ErrorKind::Failed)?;
if !basic_constraints.critical {
return Err(der::ErrorKind::Failed.into());
}
if !basic_constraints.value.ca {
return Err(der::ErrorKind::Failed.into());
}
if basic_constraints.value.path_len_constraint != Some(0) {
return Err(der::ErrorKind::Failed.into());
}
if !key_usage.critical {
return Err(der::ErrorKind::Failed.into());
}
if !key_usage.value.key_cert_sign() || !key_usage.value.crl_sign() {
return Err(der::ErrorKind::Failed.into());
}
let allowed_bits = key_usage_der::KEY_CERT_SIGN
| key_usage_der::CRL_SIGN
| key_usage_der::DIGITAL_SIGNATURE;
if (key_usage.value.bits & !allowed_bits) != 0 {
return Err(der::ErrorKind::Failed.into());
}
Ok(Self {
basic_constraints,
key_usage,
subject_key_id,
authority_key_id,
})
})
}
}
impl<'a> der::FixedTag for PaiExtensions<'a> {
const TAG: Tag = Tag::Sequence;
}
impl<'a> der::EncodeValue for PaiExtensions<'a> {
fn value_len(&self) -> der::Result<der::Length> {
unimplemented!("PaiExtensions encoding is not supported")
}
fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
unimplemented!("PaiExtensions encoding is not supported")
}
}
impl<'a> CertType<'a> for PaiExtensions<'a> {
fn subject_key_id(&self) -> Option<&'a [u8]> {
Some(self.subject_key_id.value.as_bytes())
}
fn authority_key_id(&self) -> Option<&'a [u8]> {
Some(self.authority_key_id.value.key_identifier.as_bytes())
}
fn validate_issuer_subject(
issuer_attrs: &MatterDnAttrs,
subject_attrs: &MatterDnAttrs,
_issuer_raw: &[u8],
_subject_raw: &[u8],
) -> der::Result<()> {
let subject_vid = subject_attrs.vendor_id.ok_or(der::ErrorKind::Failed)?;
if let Some(issuer_vid) = issuer_attrs.vendor_id {
if issuer_vid != subject_vid {
return Err(der::ErrorKind::Failed.into());
}
}
Ok(())
}
}
#[allow(unused)]
pub struct PaaExtensions<'a> {
basic_constraints: ParsedExtension<BasicConstraints>,
key_usage: ParsedExtension<KeyUsage>,
subject_key_id: ParsedExtension<OctetStringRef<'a>>,
authority_key_id: Option<ParsedExtension<AuthorityKeyIdentifier<'a>>>,
}
impl<'a> DecodeValue<'a> for PaaExtensions<'a> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
let fields = ParsedExtensionFields::parse(reader)?;
let basic_constraints = fields.basic_constraints.ok_or(der::ErrorKind::Failed)?;
let key_usage = fields.key_usage.ok_or(der::ErrorKind::Failed)?;
let subject_key_id = fields.subject_key_id.ok_or(der::ErrorKind::Failed)?;
let authority_key_id = fields.authority_key_id;
if !basic_constraints.critical || !basic_constraints.value.ca {
return Err(der::ErrorKind::Failed.into());
}
if let Some(path_len) = basic_constraints.value.path_len_constraint {
if path_len != 1 {
return Err(der::ErrorKind::Failed.into());
}
}
if !key_usage.critical {
return Err(der::ErrorKind::Failed.into());
}
if !key_usage.value.key_cert_sign() || !key_usage.value.crl_sign() {
return Err(der::ErrorKind::Failed.into());
}
let allowed_bits = key_usage_der::KEY_CERT_SIGN
| key_usage_der::CRL_SIGN
| key_usage_der::DIGITAL_SIGNATURE;
if (key_usage.value.bits & !allowed_bits) != 0 {
return Err(der::ErrorKind::Failed.into());
}
Ok(Self {
basic_constraints,
key_usage,
subject_key_id,
authority_key_id,
})
})
}
}
impl<'a> FixedTag for PaaExtensions<'a> {
const TAG: Tag = Tag::Sequence;
}
impl<'a> EncodeValue for PaaExtensions<'a> {
fn value_len(&self) -> der::Result<der::Length> {
unimplemented!("PaaExtensions encoding is not supported")
}
fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
unimplemented!("PaaExtensions encoding is not supported")
}
}
impl<'a> CertType<'a> for PaaExtensions<'a> {
fn subject_key_id(&self) -> Option<&'a [u8]> {
Some(self.subject_key_id.value.as_bytes())
}
fn authority_key_id(&self) -> Option<&'a [u8]> {
self.authority_key_id
.as_ref()
.map(|ext| ext.value.key_identifier.as_bytes())
}
fn validate_issuer_subject(
issuer_attrs: &MatterDnAttrs,
subject_attrs: &MatterDnAttrs,
issuer_raw: &[u8],
subject_raw: &[u8],
) -> der::Result<()> {
if issuer_attrs.product_id.is_some() || subject_attrs.product_id.is_some() {
return Err(der::ErrorKind::Failed.into());
}
if issuer_raw != subject_raw {
return Err(der::ErrorKind::Failed.into());
}
Ok(())
}
}
#[allow(unused)]
struct TbsCertificate<'a, E: CertType<'a>> {
version: UintRef<'a>,
serial_number: AnyRef<'a>,
signature: AlgorithmIdentifier<'a>,
issuer: Name<'a>,
validity: Validity,
subject: Name<'a>,
subject_public_key_info: SubjectPublicKeyInfo<'a>,
extensions: E,
}
impl<'a, E: CertType<'a>> DecodeValue<'a> for TbsCertificate<'a, E> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
let version = reader
.context_specific::<UintRef<'a>>(TagNumber::new(0), der::TagMode::Explicit)?
.ok_or(der::ErrorKind::Failed)?;
let version_value = version.as_bytes();
if version_value.len() != 1 || version_value[0] != 2 {
return Err(der::ErrorKind::Failed.into());
}
let serial_number = AnyRef::decode(reader)?;
let signature = AlgorithmIdentifier::decode(reader)?;
if signature.algorithm != OID_ECDSA_WITH_SHA256 {
return Err(der::ErrorKind::Failed.into());
}
let issuer = Name::decode(reader)?;
let validity = Validity::decode(reader)?;
let subject = Name::decode(reader)?;
let subject_public_key_info = SubjectPublicKeyInfo::decode(reader)?;
let spki_algo = &subject_public_key_info.algorithm;
if spki_algo.algorithm != OID_EC_PUBLIC_KEY {
return Err(der::ErrorKind::Failed.into());
}
if subject_public_key_info.subject_public_key.raw_bytes().len() != P256_PUBLIC_KEY_LEN {
return Err(der::ErrorKind::Failed.into());
}
let params = spki_algo.parameters.ok_or(der::ErrorKind::Failed)?;
let curve_oid: ObjectIdentifier = params.try_into()?;
if curve_oid != OID_PRIME256V1 {
return Err(der::ErrorKind::Failed.into());
}
let extensions = reader
.context_specific::<E>(TagNumber::new(3), der::TagMode::Explicit)?
.ok_or(der::ErrorKind::Failed)?;
E::validate_issuer_subject(
&issuer.attrs,
&subject.attrs,
issuer.raw_bytes,
subject.raw_bytes,
)?;
Ok(Self {
version,
serial_number,
signature,
issuer,
validity,
subject,
subject_public_key_info,
extensions,
})
})
}
}
impl<'a, E: CertType<'a>> der::FixedTag for TbsCertificate<'a, E> {
const TAG: Tag = Tag::Sequence;
}
impl<'a, E: CertType<'a>> EncodeValue for TbsCertificate<'a, E> {
fn value_len(&self) -> der::Result<der::Length> {
unimplemented!("PaaExtensions encoding is not supported")
}
fn encode_value(&self, _writer: &mut impl der::Writer) -> der::Result<()> {
unimplemented!("PaaExtensions encoding is not supported")
}
}
#[allow(unused)]
#[derive(Sequence)]
struct Certificate<'a, E: CertType<'a>> {
tbs_certificate: TbsCertificate<'a, E>,
signature_algorithm: AlgorithmIdentifier<'a>,
signature_value: BitStringRef<'a>,
}
pub struct X509Cert<'a, E: CertType<'a>> {
cert: Certificate<'a, E>,
}
impl<'a, E: CertType<'a>> X509Cert<'a, E> {
pub fn new(data: &'a [u8]) -> Result<Self, Error> {
let cert = Certificate::from_der(data).map_err(|_| ErrorCode::InvalidData)?;
Ok(Self { cert })
}
pub fn subject_key_id(&self) -> Result<&'a [u8], Error> {
self.cert
.tbs_certificate
.extensions
.subject_key_id()
.ok_or(Error::from(ErrorCode::NotFound))
}
pub fn authority_key_id(&self) -> Result<&'a [u8], Error> {
self.cert
.tbs_certificate
.extensions
.authority_key_id()
.ok_or(Error::from(ErrorCode::NotFound))
}
pub fn public_key(&self) -> Result<&'a [u8], Error> {
let spki_bytes = &self
.cert
.tbs_certificate
.subject_public_key_info
.subject_public_key
.raw_bytes();
Ok(spki_bytes)
}
pub fn vendor_id(&self) -> Result<u16, Error> {
self.cert
.tbs_certificate
.subject
.attrs
.vendor_id
.ok_or(Error::from(ErrorCode::NotFound))
}
pub fn product_id(&self) -> Result<u16, Error> {
self.cert
.tbs_certificate
.subject
.attrs
.product_id
.ok_or(Error::from(ErrorCode::NotFound))
}
pub fn not_before_unix(&self) -> Result<u64, Error> {
time_to_unix_secs(&self.cert.tbs_certificate.validity.not_before)
}
pub fn not_after_unix(&self) -> Result<u64, Error> {
time_to_unix_secs(&self.cert.tbs_certificate.validity.not_after)
}
pub fn is_valid_at(&self, now_unix_secs: u64) -> Result<bool, Error> {
let not_before = self.not_before_unix()?;
let not_after = self.not_after_unix()?;
Ok(now_unix_secs >= not_before && now_unix_secs <= not_after)
}
}
#[cfg(test)]
mod tests {
use super::*;
const PAA_DER: &[u8] = &[
0x30, 0x82, 0x01, 0xa0, 0x30, 0x82, 0x01, 0x46, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08,
0x57, 0xd3, 0xa2, 0xd0, 0x1e, 0x31, 0x81, 0x90, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x21, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04,
0x03, 0x0c, 0x16, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c,
0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x50, 0x41, 0x41, 0x30, 0x20, 0x17, 0x0d, 0x32,
0x31, 0x30, 0x36, 0x32, 0x38, 0x31, 0x34, 0x32, 0x33, 0x34, 0x33, 0x5a, 0x18, 0x0f, 0x39,
0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30,
0x21, 0x31, 0x1f, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x16, 0x4d, 0x61, 0x74,
0x74, 0x65, 0x72, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74,
0x20, 0x50, 0x41, 0x41, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00,
0x04, 0x1b, 0x0f, 0x25, 0x94, 0x2e, 0x3d, 0x92, 0xab, 0xd6, 0x70, 0x4c, 0x1a, 0x27, 0x81,
0xa0, 0x38, 0xec, 0x53, 0x21, 0x2c, 0x4d, 0xab, 0x58, 0xb0, 0xbe, 0x3c, 0x40, 0xbd, 0xfb,
0x49, 0x23, 0x23, 0x42, 0x1c, 0x79, 0xdc, 0xc7, 0xad, 0x70, 0x18, 0x10, 0x07, 0x12, 0x0d,
0xc8, 0x6f, 0x0a, 0x89, 0x25, 0x3d, 0x89, 0x93, 0xeb, 0x37, 0xab, 0x65, 0x2e, 0xf8, 0xdb,
0x13, 0x75, 0xe5, 0xb1, 0x45, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d,
0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x01, 0x30,
0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06,
0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xfa, 0x92, 0xcf, 0x09,
0x5e, 0xfa, 0x42, 0xe1, 0x14, 0x30, 0x65, 0x16, 0x32, 0xfe, 0xfe, 0x1b, 0x2c, 0x77, 0xa7,
0xc8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfa,
0x92, 0xcf, 0x09, 0x5e, 0xfa, 0x42, 0xe1, 0x14, 0x30, 0x65, 0x16, 0x32, 0xfe, 0xfe, 0x1b,
0x2c, 0x77, 0xa7, 0xc8, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03,
0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x20, 0x50, 0xa7, 0x90, 0x33, 0x64, 0xb6, 0x53,
0xff, 0x0e, 0xa4, 0x63, 0xdc, 0x68, 0x4a, 0x86, 0xdd, 0x25, 0xc7, 0x31, 0xa3, 0x9e, 0xfe,
0xb3, 0xc2, 0x0c, 0xd2, 0xde, 0xd1, 0xb6, 0x60, 0x7e, 0x2f, 0x02, 0x21, 0x00, 0xaf, 0xd4,
0xed, 0x4b, 0x6a, 0x99, 0xe5, 0xf8, 0xc5, 0x52, 0x1d, 0x70, 0x1e, 0xbc, 0xf9, 0xfd, 0x53,
0xb9, 0x39, 0x4f, 0xd8, 0x0f, 0xc5, 0x99, 0x92, 0xff, 0x3e, 0x5b, 0xbb, 0xb6, 0x0a, 0x35,
];
const PAI_DER: &[u8] = &[
0x30, 0x82, 0x01, 0xcb, 0x30, 0x82, 0x01, 0x71, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08,
0x56, 0xad, 0x82, 0x22, 0xad, 0x94, 0x5b, 0x64, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x30, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04,
0x03, 0x0c, 0x0f, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20,
0x50, 0x41, 0x41, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82,
0xa2, 0x7c, 0x02, 0x01, 0x0c, 0x04, 0x46, 0x46, 0x46, 0x31, 0x30, 0x20, 0x17, 0x0d, 0x32,
0x32, 0x30, 0x32, 0x30, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x39,
0x39, 0x39, 0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30,
0x3d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1c, 0x4d, 0x61, 0x74,
0x74, 0x65, 0x72, 0x20, 0x44, 0x65, 0x76, 0x20, 0x50, 0x41, 0x49, 0x20, 0x30, 0x78, 0x46,
0x46, 0x46, 0x31, 0x20, 0x6e, 0x6f, 0x20, 0x50, 0x49, 0x44, 0x31, 0x14, 0x30, 0x12, 0x06,
0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x02, 0x01, 0x0c, 0x04, 0x46, 0x46,
0x46, 0x31, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01,
0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x41,
0x9a, 0x93, 0x15, 0xc2, 0x17, 0x3e, 0x0c, 0x8c, 0x87, 0x6d, 0x03, 0xcc, 0xfc, 0x94, 0x48,
0x52, 0x64, 0x7f, 0x7f, 0xec, 0x5e, 0x50, 0x82, 0xf4, 0x05, 0x99, 0x28, 0xec, 0xa8, 0x94,
0xc5, 0x94, 0x15, 0x13, 0x09, 0xac, 0x63, 0x1e, 0x4c, 0xb0, 0x33, 0x92, 0xaf, 0x68, 0x4b,
0x0b, 0xaf, 0xb7, 0xe6, 0x5b, 0x3b, 0x81, 0x62, 0xc2, 0xf5, 0x2b, 0xf9, 0x31, 0xb8, 0xe7,
0x7a, 0xaa, 0x82, 0xa3, 0x66, 0x30, 0x64, 0x30, 0x12, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x0e, 0x06,
0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 0x1d,
0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x63, 0x54, 0x0e, 0x47, 0xf6, 0x4b,
0x1c, 0x38, 0xd1, 0x38, 0x84, 0xa4, 0x62, 0xd1, 0x6c, 0x19, 0x5d, 0x8f, 0xfb, 0x3c, 0x30,
0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x6a, 0xfd, 0x22,
0x77, 0x1f, 0x51, 0x1f, 0xec, 0xbf, 0x16, 0x41, 0x97, 0x67, 0x10, 0xdc, 0xdc, 0x31, 0xa1,
0x71, 0x7e, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03,
0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00, 0xb2, 0xef, 0x27, 0xf4, 0x9a, 0xe9, 0xb5, 0x0f,
0xb9, 0x1e, 0xea, 0xc9, 0x4c, 0x4d, 0x0b, 0xdb, 0xb8, 0xd7, 0x92, 0x9c, 0x6c, 0xb8, 0x8f,
0xac, 0xe5, 0x29, 0x36, 0x8d, 0x12, 0x05, 0x4c, 0x0c, 0x02, 0x20, 0x65, 0x5d, 0xc9, 0x2b,
0x86, 0xbd, 0x90, 0x98, 0x82, 0xa6, 0xc6, 0x21, 0x77, 0xb8, 0x25, 0xd7, 0xd0, 0x5e, 0xdb,
0xe7, 0xc2, 0x2f, 0x9f, 0xea, 0x71, 0x22, 0x0e, 0x7e, 0xa7, 0x03, 0xf8, 0x91,
];
const DAC_DER: &[u8] = &[
0x30, 0x82, 0x01, 0xe9, 0x30, 0x82, 0x01, 0x8e, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08,
0x23, 0x8a, 0x64, 0x7b, 0xbc, 0x4c, 0x30, 0xdd, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x3d, 0x31, 0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04,
0x03, 0x0c, 0x1c, 0x4d, 0x61, 0x74, 0x74, 0x65, 0x72, 0x20, 0x44, 0x65, 0x76, 0x20, 0x50,
0x41, 0x49, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46, 0x31, 0x20, 0x6e, 0x6f, 0x20, 0x50, 0x49,
0x44, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c,
0x02, 0x01, 0x0c, 0x04, 0x46, 0x46, 0x46, 0x31, 0x30, 0x20, 0x17, 0x0d, 0x32, 0x32, 0x30,
0x32, 0x30, 0x35, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x18, 0x0f, 0x39, 0x39, 0x39,
0x39, 0x31, 0x32, 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x53, 0x31,
0x25, 0x30, 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x1c, 0x4d, 0x61, 0x74, 0x74, 0x65,
0x72, 0x20, 0x44, 0x65, 0x76, 0x20, 0x44, 0x41, 0x43, 0x20, 0x30, 0x78, 0x46, 0x46, 0x46,
0x31, 0x2f, 0x30, 0x78, 0x38, 0x30, 0x30, 0x30, 0x31, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b,
0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x02, 0x01, 0x0c, 0x04, 0x46, 0x46, 0x46, 0x31,
0x31, 0x14, 0x30, 0x12, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0xa2, 0x7c, 0x02,
0x02, 0x0c, 0x04, 0x38, 0x30, 0x30, 0x30, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
0x03, 0x42, 0x00, 0x04, 0x62, 0xdb, 0x16, 0xba, 0xde, 0xa3, 0x26, 0xa6, 0xdb, 0x84, 0x81,
0x4a, 0x06, 0x3f, 0xc6, 0xc7, 0xe9, 0xe2, 0xb1, 0x01, 0xb7, 0x21, 0x64, 0x8e, 0xba, 0x4e,
0x5a, 0xc8, 0x40, 0xf5, 0xda, 0x30, 0x1e, 0xe6, 0x18, 0x12, 0x4e, 0xb4, 0x18, 0x0e, 0x2f,
0xc3, 0xa2, 0x04, 0x7a, 0x56, 0x4b, 0xa9, 0xbc, 0xfa, 0x0b, 0xf7, 0x1f, 0x60, 0xce, 0x89,
0x30, 0xf1, 0xe7, 0xf6, 0x6e, 0xc8, 0xd7, 0x28, 0xa3, 0x60, 0x30, 0x5e, 0x30, 0x0c, 0x06,
0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0e, 0x06, 0x03,
0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x07, 0x80, 0x30, 0x1d, 0x06,
0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xbc, 0xf7, 0xb0, 0x07, 0x49, 0x70, 0x63,
0x60, 0x6a, 0x26, 0xbe, 0x4e, 0x08, 0x7c, 0x59, 0x56, 0x87, 0x74, 0x5a, 0x5a, 0x30, 0x1f,
0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x63, 0x54, 0x0e, 0x47,
0xf6, 0x4b, 0x1c, 0x38, 0xd1, 0x38, 0x84, 0xa4, 0x62, 0xd1, 0x6c, 0x19, 0x5d, 0x8f, 0xfb,
0x3c, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x49,
0x00, 0x30, 0x46, 0x02, 0x21, 0x00, 0x97, 0x97, 0x11, 0xec, 0x9e, 0x76, 0x18, 0xce, 0x41,
0x80, 0x11, 0x32, 0xc2, 0x50, 0xdb, 0x70, 0x76, 0x74, 0x63, 0x0c, 0xd5, 0x8c, 0x12, 0xc6,
0xe2, 0x31, 0x5f, 0x08, 0xd0, 0x1e, 0xe1, 0x78, 0x02, 0x21, 0x00, 0xec, 0xfc, 0x13, 0x06,
0xbd, 0x2a, 0x13, 0x3d, 0x12, 0x2a, 0x27, 0x86, 0x10, 0xea, 0x3d, 0xca, 0x47, 0xf0, 0x5c,
0x7a, 0x8b, 0x80, 0x5f, 0xa7, 0x1c, 0x6f, 0xf4, 0x15, 0x38, 0xa8, 0x64, 0xc8,
];
#[test]
fn test_paa_skid() {
let cert = PaaCert::new(PAA_DER).unwrap();
let skid = cert.subject_key_id().unwrap();
assert_eq!(
skid,
&[
0xFA, 0x92, 0xCF, 0x09, 0x5E, 0xFA, 0x42, 0xE1, 0x14, 0x30, 0x65, 0x16, 0x32, 0xFE,
0xFE, 0x1B, 0x2C, 0x77, 0xA7, 0xC8
]
);
}
#[test]
fn test_paa_self_signed_akid_equals_skid() {
let cert = PaaCert::new(PAA_DER).unwrap();
let skid = cert.subject_key_id().unwrap();
let akid = cert.authority_key_id().unwrap();
assert_eq!(skid, akid);
}
#[test]
fn test_pai_skid() {
let cert = PaiCert::new(PAI_DER).unwrap();
let skid = cert.subject_key_id().unwrap();
assert_eq!(
skid,
&[
0x63, 0x54, 0x0E, 0x47, 0xF6, 0x4B, 0x1C, 0x38, 0xD1, 0x38, 0x84, 0xA4, 0x62, 0xD1,
0x6C, 0x19, 0x5D, 0x8F, 0xFB, 0x3C
]
);
}
#[test]
fn test_dac_skid() {
let cert = DacCert::new(DAC_DER).unwrap();
let skid = cert.subject_key_id().unwrap();
assert_eq!(
skid,
&[
0xBC, 0xF7, 0xB0, 0x07, 0x49, 0x70, 0x63, 0x60, 0x6A, 0x26, 0xBE, 0x4E, 0x08, 0x7C,
0x59, 0x56, 0x87, 0x74, 0x5A, 0x5A
]
);
}
#[test]
fn test_dac_akid_matches_pai_skid() {
let dac = DacCert::new(DAC_DER).unwrap();
let pai = PaiCert::new(PAI_DER).unwrap();
assert_eq!(
dac.authority_key_id().unwrap(),
pai.subject_key_id().unwrap()
);
}
#[test]
fn test_paa_public_key() {
let cert = PaaCert::new(PAA_DER).unwrap();
let pk = cert.public_key().unwrap();
assert_eq!(pk.len(), 65);
assert_eq!(pk[0], 0x04); assert_eq!(
pk,
&[
0x04, 0x1b, 0x0f, 0x25, 0x94, 0x2e, 0x3d, 0x92, 0xab, 0xd6, 0x70, 0x4c, 0x1a, 0x27,
0x81, 0xa0, 0x38, 0xec, 0x53, 0x21, 0x2c, 0x4d, 0xab, 0x58, 0xb0, 0xbe, 0x3c, 0x40,
0xbd, 0xfb, 0x49, 0x23, 0x23, 0x42, 0x1c, 0x79, 0xdc, 0xc7, 0xad, 0x70, 0x18, 0x10,
0x07, 0x12, 0x0d, 0xc8, 0x6f, 0x0a, 0x89, 0x25, 0x3d, 0x89, 0x93, 0xeb, 0x37, 0xab,
0x65, 0x2e, 0xf8, 0xdb, 0x13, 0x75, 0xe5, 0xb1, 0x45,
]
);
}
#[test]
fn test_dac_public_key() {
let cert = DacCert::new(DAC_DER).unwrap();
let pk = cert.public_key().unwrap();
assert_eq!(pk.len(), 65);
assert_eq!(pk[0], 0x04);
assert_eq!(
pk,
&[
0x04, 0x62, 0xdb, 0x16, 0xba, 0xde, 0xa3, 0x26, 0xa6, 0xdb, 0x84, 0x81, 0x4a, 0x06,
0x3f, 0xc6, 0xc7, 0xe9, 0xe2, 0xb1, 0x01, 0xb7, 0x21, 0x64, 0x8e, 0xba, 0x4e, 0x5a,
0xc8, 0x40, 0xf5, 0xda, 0x30, 0x1e, 0xe6, 0x18, 0x12, 0x4e, 0xb4, 0x18, 0x0e, 0x2f,
0xc3, 0xa2, 0x04, 0x7a, 0x56, 0x4b, 0xa9, 0xbc, 0xfa, 0x0b, 0xf7, 0x1f, 0x60, 0xce,
0x89, 0x30, 0xf1, 0xe7, 0xf6, 0x6e, 0xc8, 0xd7, 0x28,
]
);
}
#[test]
fn test_dac_vendor_id() {
let cert = DacCert::new(DAC_DER).unwrap();
assert_eq!(cert.vendor_id().unwrap(), 0xFFF1);
}
#[test]
fn test_dac_product_id() {
let cert = DacCert::new(DAC_DER).unwrap();
assert_eq!(cert.product_id().unwrap(), 0x8000);
}
#[test]
fn test_pai_vendor_id() {
let cert = PaiCert::new(PAI_DER).unwrap();
assert_eq!(cert.vendor_id().unwrap(), 0xFFF1);
}
#[test]
fn test_pai_no_product_id() {
let cert = PaiCert::new(PAI_DER).unwrap();
assert_eq!(
cert.product_id().map_err(|e| e.code()),
Err(ErrorCode::NotFound)
);
}
#[test]
fn test_paa_no_vendor_id() {
let cert = PaaCert::new(PAA_DER).unwrap();
assert_eq!(
cert.vendor_id().map_err(|e| e.code()),
Err(ErrorCode::NotFound)
);
}
#[test]
fn test_paa_no_product_id() {
let cert = PaaCert::new(PAA_DER).unwrap();
assert_eq!(
cert.product_id().map_err(|e| e.code()),
Err(ErrorCode::NotFound)
);
}
#[test]
fn test_dac_not_before() {
let cert = DacCert::new(DAC_DER).unwrap();
let nb = cert.not_before_unix().unwrap();
assert_eq!(nb, 1644019200);
}
#[test]
fn test_dac_not_after_no_expiry() {
let cert = DacCert::new(DAC_DER).unwrap();
let na = cert.not_after_unix().unwrap();
assert_eq!(na, u64::MAX);
}
#[test]
fn test_paa_not_before() {
let cert = PaaCert::new(PAA_DER).unwrap();
let nb = cert.not_before_unix().unwrap();
assert_eq!(nb, 1624890223);
}
#[test]
fn test_dac_is_valid_at() {
let cert = DacCert::new(DAC_DER).unwrap();
assert!(cert.is_valid_at(1700000000).unwrap());
assert!(!cert.is_valid_at(1600000000).unwrap());
}
#[test]
fn test_invalid_empty_input() {
assert!(DacCert::new(&[]).is_err());
}
#[test]
fn test_invalid_not_sequence() {
assert!(DacCert::new(&[0x01, 0x02, 0x03]).is_err());
}
#[test]
fn test_invalid_empty_sequence() {
assert!(DacCert::new(&[0x30, 0x00]).is_err());
}
#[test]
fn test_tbs_field_version_error_when_absent() {
let mut der = PAA_DER.to_vec();
let version_field_len: usize = 5;
let version_start: usize = 8;
der[3] -= version_field_len as u8;
der[7] -= version_field_len as u8;
der.drain(version_start..version_start + version_field_len);
assert!(PaaCert::new(&der).is_err());
}
#[test]
fn test_version_must_be_v3() {
let mut der = PAA_DER.to_vec();
der[12] = 1;
assert!(PaaCert::new(&der).is_err());
}
#[test]
fn test_parse_hex_u16() {
assert_eq!(parse_hex_u16(b"FFF1").unwrap(), 0xFFF1);
assert_eq!(parse_hex_u16(b"8000").unwrap(), 0x8000);
assert_eq!(parse_hex_u16(b"0000").unwrap(), 0x0000);
assert_eq!(parse_hex_u16(b"FFFF").unwrap(), 0xFFFF);
assert_eq!(parse_hex_u16(b"abcd").unwrap(), 0xABCD);
assert!(parse_hex_u16(b"FFF").is_err()); assert!(parse_hex_u16(b"FFFFF").is_err()); assert!(parse_hex_u16(b"GHIJ").is_err()); }
#[test]
fn test_keyusage_from_bitstring_1_byte_with_unused() {
let bs_bytes = &[0x81u8];
let bs = BitStringRef::new(7, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(
ku.bits, 0x8000,
"Expected only bit 0 (digitalSignature) set"
);
assert!(ku.digital_signature());
assert!(ku.has_only_bits(key_usage_der::DIGITAL_SIGNATURE));
}
#[test]
fn test_keyusage_from_bitstring_1_byte_no_unused() {
let bs_bytes = &[0x80u8];
let bs = BitStringRef::new(0, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(ku.bits, 0x8000);
assert!(ku.digital_signature());
assert!(ku.has_only_bits(key_usage_der::DIGITAL_SIGNATURE));
}
#[test]
fn test_keyusage_from_bitstring_2_bytes_with_unused() {
let bs_bytes = &[0x80u8, 0x80u8];
let bs = BitStringRef::new(7, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(ku.bits, 0x8080);
assert!(ku.digital_signature());
}
#[test]
fn test_keyusage_from_bitstring_2_bytes_no_unused() {
let bs_bytes = &[0x04u8, 0x00u8];
let bs = BitStringRef::new(0, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(ku.bits, 0x0400);
assert!(ku.key_cert_sign());
assert!(ku.has_only_bits(key_usage_der::KEY_CERT_SIGN));
}
#[test]
fn test_keyusage_rejects_oversized_bitstring() {
let bs_bytes = &[0x80u8, 0x00u8, 0xFFu8];
let bs = BitStringRef::new(0, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(ku.bits, 0, "Oversized bitstring should be rejected");
assert!(!ku.digital_signature());
}
#[test]
fn test_keyusage_1_byte_with_padding() {
let bs_bytes = &[0xFFu8];
let bs = BitStringRef::new(7, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(ku.bits, 0x8000, "Should mask off unused padding bits");
assert!(ku.has_only_bits(key_usage_der::DIGITAL_SIGNATURE));
}
#[test]
fn test_keyusage_2_bytes_with_padding() {
let bs_bytes = &[0x80u8, 0xFFu8];
let bs = BitStringRef::new(7, bs_bytes).unwrap();
let ku = KeyUsage::from(bs);
assert_eq!(
ku.bits, 0x8080,
"Should mask off unused padding bits in last byte"
);
}
}