use pki_types::{CertificateDer, TrustAnchor};
use crate::cert::{Cert, lenient_certificate_serial_number};
use crate::der;
use crate::error::{DerTypeId, Error};
pub fn anchor_from_trusted_cert<'a>(
cert: &'a CertificateDer<'a>,
) -> Result<TrustAnchor<'a>, Error> {
let cert_der = untrusted::Input::from(cert.as_ref());
match Cert::for_trust_anchor(cert_der) {
Ok(cert) => Ok(TrustAnchor::from(cert)),
Err(Error::UnsupportedCertVersion) => {
extract_trust_anchor_from_v1_cert_der(cert_der).or(Err(Error::BadDer))
}
Err(err) => Err(err),
}
}
fn extract_trust_anchor_from_v1_cert_der(
cert_der: untrusted::Input<'_>,
) -> Result<TrustAnchor<'_>, Error> {
cert_der.read_all(Error::BadDer, |cert_der| {
der::nested(
cert_der,
der::Tag::Sequence,
Error::TrailingData(DerTypeId::TrustAnchorV1),
|cert_der| {
let anchor = der::nested(
cert_der,
der::Tag::Sequence,
Error::TrailingData(DerTypeId::TrustAnchorV1TbsCertificate),
|tbs| {
lenient_certificate_serial_number(tbs)?;
skip(tbs, der::Tag::Sequence)?; skip(tbs, der::Tag::Sequence)?; skip(tbs, der::Tag::Sequence)?; let subject = der::expect_tag(tbs, der::Tag::Sequence)?;
let spki = der::expect_tag(tbs, der::Tag::Sequence)?;
Ok(TrustAnchor {
subject: subject.as_slice_less_safe().into(),
subject_public_key_info: spki.as_slice_less_safe().into(),
name_constraints: None,
})
},
);
skip(cert_der, der::Tag::Sequence)?;
skip(cert_der, der::Tag::BitString)?;
anchor
},
)
})
}
impl<'a> From<Cert<'a>> for TrustAnchor<'a> {
fn from(cert: Cert<'a>) -> Self {
Self {
subject: cert.subject.as_slice_less_safe().into(),
subject_public_key_info: cert.spki.as_slice_less_safe().into(),
name_constraints: cert
.name_constraints
.map(|nc| nc.as_slice_less_safe().into()),
}
}
}
fn skip(input: &mut untrusted::Reader<'_>, tag: der::Tag) -> Result<(), Error> {
der::expect_tag(input, tag).map(|_| ())
}
#[cfg(test)]
mod tests {
use rcgen::{CertificateParams, CustomExtension, KeyPair};
use super::*;
#[test]
fn anchor_ignores_critical_extension_with_unknown_oid() {
let der = cert_with_critical_extension(&[1, 2, 3, 4]);
anchor_from_trusted_cert(&der)
.expect("critical extension with unknown OID should be ignored for trust anchors");
}
#[test]
fn anchor_ignores_critical_extension_with_unknown_id_ce_oid() {
let der = cert_with_critical_extension(&[2, 5, 29, 99]);
anchor_from_trusted_cert(&der).expect(
"critical id-ce extension with unknown OID should be ignored for trust anchors",
);
}
fn cert_with_critical_extension(oid: &[u64]) -> CertificateDer<'static> {
let mut params = CertificateParams::new(vec!["example.com".into()]).unwrap();
let mut ext = CustomExtension::from_oid_content(oid, vec![1, 2]);
ext.set_criticality(true);
params.custom_extensions.push(ext);
let key = KeyPair::generate_for(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap();
let cert = params.self_signed(&key).unwrap();
CertificateDer::from(cert.der().to_vec())
}
}