Files
cli/vendor/webpki-roots/tests/verify.rs

203 lines
7.6 KiB
Rust

use core::time::Duration;
use std::convert::TryFrom;
use pki_types::{CertificateDer, ServerName, SignatureVerificationAlgorithm, UnixTime};
use rcgen::{
BasicConstraints, CertificateParams, CertifiedIssuer, DnType, IsCa, Issuer, KeyPair,
KeyUsagePurpose,
};
use webpki::{anchor_from_trusted_cert, EndEntityCert, Error, KeyUsage};
use x509_parser::extensions::{GeneralName, NameConstraints as X509ParserNameConstraints};
use x509_parser::prelude::FromDer;
use webpki_roots::TLS_SERVER_ROOTS;
#[test]
fn name_constraints() {
for name_constraints in TLS_SERVER_ROOTS
.iter()
.filter_map(|ta| ta.name_constraints.as_ref())
{
let time = UnixTime::since_unix_epoch(Duration::from_secs(0x40000000)); // Time matching rcgen default.
let test_case = ConstraintTest::new(name_constraints.as_ref());
let trust_anchors = &[anchor_from_trusted_cert(&test_case.trust_anchor).unwrap()];
// Each permitted EE should verify without error.
for permitted_ee in test_case.permitted_certs {
webpki::EndEntityCert::try_from(&permitted_ee)
.unwrap()
.verify_for_usage(
ALL_ALGORITHMS,
trust_anchors,
&[],
time,
KeyUsage::server_auth(),
None,
None,
)
.unwrap();
}
// Each forbidden EE should fail to verify with the expected name constraint error.
for forbidden_ee in test_case.forbidden_certs {
let ee = EndEntityCert::try_from(&forbidden_ee).unwrap();
let result = ee.verify_for_usage(
ALL_ALGORITHMS,
trust_anchors,
&[],
time,
KeyUsage::server_auth(),
None,
None,
);
assert!(matches!(result, Err(Error::NameConstraintViolation)));
}
}
}
struct ConstraintTest {
trust_anchor: CertificateDer<'static>,
permitted_certs: Vec<CertificateDer<'static>>,
forbidden_certs: Vec<CertificateDer<'static>>,
}
impl ConstraintTest {
fn new(webpki_name_constraints: &[u8]) -> Self {
let name_constraints = rcgen_name_constraints(webpki_name_constraints);
// Create a trust anchor CA certificate that has the name constraints we want to test.
let trust_anchor = {
let mut params = CertificateParams::new([]).unwrap();
params
.distinguished_name
.push(DnType::CommonName, "Name Constraint Test CA");
params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained);
params.key_usages = vec![
KeyUsagePurpose::KeyCertSign,
KeyUsagePurpose::DigitalSignature,
];
params.name_constraints = Some(name_constraints.clone());
let key = KeyPair::generate().unwrap();
CertifiedIssuer::self_signed(params, key).unwrap()
};
let certs_for_subtrees = |suffix| {
name_constraints
.permitted_subtrees
.iter()
.filter_map(|subtree| match subtree {
rcgen::GeneralSubtree::DnsName(dns_name) => Some(rcgen_ee_for_name(
format!("valid{dns_name}{suffix}"),
&trust_anchor,
)),
_ => None,
})
.collect()
};
Self {
// For each permitted subtree in the name constraints, issue an end entity certificate
// that contains a DNS name matching the permitted subtree base.
permitted_certs: certs_for_subtrees(""),
// For each permitted subtree in the name constraints, issue an end entity certificate
// that contains a DNS name that will **not** match the permitted subtree base.
forbidden_certs: certs_for_subtrees(".invalid"),
trust_anchor: trust_anchor.der().to_owned(),
}
}
}
fn rcgen_ee_for_name(name: String, issuer: &Issuer<'_, KeyPair>) -> CertificateDer<'static> {
let mut ee = CertificateParams::new(vec![name.clone()]).unwrap();
ee.distinguished_name.push(DnType::CommonName, name);
ee.is_ca = IsCa::NoCa;
let key_pair = KeyPair::generate().unwrap();
ee.signed_by(&key_pair, issuer).unwrap().into()
}
/// Convert the webpki trust anchor DER encoding of name constraints to rcgen NameConstraints.
fn rcgen_name_constraints(der: &[u8]) -> rcgen::NameConstraints {
// x509 parser expects the outer SEQUENCE that the webpki trust anchor representation elides
// so wrap the DER up.
let wrapped_der = yasna::construct_der(|writer| {
writer.write_sequence(|writer| {
writer.next().write_der(der);
})
});
// Constraints should parse with no trailing data.
let (trailing, constraints) = X509ParserNameConstraints::from_der(&wrapped_der).unwrap();
assert!(
trailing.is_empty(),
"unexpected trailing DER in name constraint"
);
// There should be at least one permitted subtree.
assert!(
constraints.permitted_subtrees.is_some(),
"empty permitted subtrees in constraints"
);
// We don't expect any excluded subtrees as this time.
assert!(constraints.excluded_subtrees.is_none());
// Collect all of the DNS names from the x509-parser representation, mapping to the rcgen
// representation usable in cert parameters. We don't expect to find any other types of general
// name and x509-parser doesn't parse the subtree minimum and maximum (which we would assert to
// be missing for proper encoding anyway).
let permitted_subtrees = match constraints.permitted_subtrees {
None => Vec::default(),
Some(subtrees) => subtrees
.iter()
.map(|subtree| match &subtree.base {
GeneralName::DNSName(base) => rcgen::GeneralSubtree::DnsName(base.to_string()),
name => panic!("unexpected subtree base general name type: {name}"),
})
.collect(),
};
rcgen::NameConstraints {
permitted_subtrees,
excluded_subtrees: Vec::default(),
}
}
#[test]
fn tubitak_name_constraint_works() {
let root = CertificateDer::from(&include_bytes!("data/tubitak/root.der")[..]);
let inter = CertificateDer::from(&include_bytes!("data/tubitak/inter.der")[..]);
let subj = CertificateDer::from(&include_bytes!("data/tubitak/subj.der")[..]);
let roots = [anchor_from_trusted_cert(&root).unwrap().to_owned()];
let now = UnixTime::since_unix_epoch(Duration::from_secs(1493668479));
let cert = EndEntityCert::try_from(&subj).unwrap();
cert.verify_for_usage(
ALL_ALGORITHMS,
&roots,
&[inter, root],
now,
KeyUsage::server_auth(),
None,
None,
)
.unwrap();
let subject = ServerName::try_from("testssl.kamusm.gov.tr").unwrap();
cert.verify_is_valid_for_subject_name(&subject).unwrap();
}
static ALL_ALGORITHMS: &[&dyn SignatureVerificationAlgorithm] = &[
webpki::aws_lc_rs::ECDSA_P256_SHA256,
webpki::aws_lc_rs::ECDSA_P256_SHA384,
webpki::aws_lc_rs::ECDSA_P384_SHA256,
webpki::aws_lc_rs::ECDSA_P384_SHA384,
webpki::aws_lc_rs::RSA_PKCS1_2048_8192_SHA256,
webpki::aws_lc_rs::RSA_PKCS1_2048_8192_SHA384,
webpki::aws_lc_rs::RSA_PKCS1_2048_8192_SHA512,
webpki::aws_lc_rs::RSA_PKCS1_3072_8192_SHA384,
webpki::aws_lc_rs::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
webpki::aws_lc_rs::RSA_PSS_2048_8192_SHA384_LEGACY_KEY,
webpki::aws_lc_rs::RSA_PSS_2048_8192_SHA512_LEGACY_KEY,
];