Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/end_entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,18 +174,16 @@ mod tests {
fn printable_string_common_name() {
const DNS_NAME: &str = "test.example.com";

let issuer = test_utils::make_issuer("Test", None);
let issuer = test_utils::make_issuer("Test");

let ee_cert_der = {
let mut params = rcgen::CertificateParams::new(vec![DNS_NAME.to_string()]);
let mut params = test_utils::end_entity_params(vec![DNS_NAME.to_string()]);
// construct a certificate that uses `PrintableString` as the
// common name value, rather than `UTF8String`.
params.distinguished_name.push(
rcgen::DnType::CommonName,
rcgen::DnValue::PrintableString("example.com".to_string()),
);
params.is_ca = rcgen::IsCa::ExplicitNoCa;
params.alg = test_utils::RCGEN_SIGNATURE_ALG;
let cert = rcgen::Certificate::from_params(params)
.expect("failed to make ee cert (this is a test bug)");
let bytes = cert
Expand Down
39 changes: 21 additions & 18 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
#[cfg(feature = "alloc")]
#![cfg(feature = "alloc")]

use crate::types::CertificateDer;

/// Signature algorithm used by certificates generated using `make_issuer` and
/// `make_end_entity`. This is exported as a constant so that tests can use the
/// same algorithm when generating certificates using `rcgen`.
#[cfg(feature = "alloc")]
pub(crate) static RCGEN_SIGNATURE_ALG: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_P256_SHA256;
/// Signature algorithm used by certificates and parameters generated using the test utils helpers.
static RCGEN_SIGNATURE_ALG: &rcgen::SignatureAlgorithm = &rcgen::PKCS_ECDSA_P256_SHA256;

pub(crate) fn make_issuer(org_name: impl Into<String>) -> rcgen::Certificate {
rcgen::Certificate::from_params(issuer_params(org_name)).unwrap()
}

#[cfg(feature = "alloc")]
pub(crate) fn make_issuer(
org_name: impl Into<String>,
name_constraints: Option<rcgen::NameConstraints>,
) -> rcgen::Certificate {
/// Populate a [CertificateParams] that describes an unconstrained issuer certificate capable
/// of signing other certificates and CRLs, with the given `org_name` as an organization distinguished
/// subject name.
pub(crate) fn issuer_params(org_name: impl Into<String>) -> rcgen::CertificateParams {
let mut ca_params = rcgen::CertificateParams::new(Vec::new());
ca_params
.distinguished_name
Expand All @@ -23,19 +24,21 @@ pub(crate) fn make_issuer(
rcgen::KeyUsagePurpose::CrlSign,
];
ca_params.alg = RCGEN_SIGNATURE_ALG;
ca_params.name_constraints = name_constraints;
rcgen::Certificate::from_params(ca_params).unwrap()
ca_params
}

#[cfg(feature = "alloc")]
pub(crate) fn make_end_entity(issuer: &rcgen::Certificate) -> CertificateDer<'static> {
let mut ee_params = rcgen::CertificateParams::new(vec!["example.com".to_string()]);
ee_params.is_ca = rcgen::IsCa::ExplicitNoCa;
ee_params.alg = RCGEN_SIGNATURE_ALG;
CertificateDer::from(
rcgen::Certificate::from_params(ee_params)
rcgen::Certificate::from_params(end_entity_params(vec!["example.com".into()]))
.unwrap()
.serialize_der_with_signer(issuer)
.unwrap(),
)
}

pub(crate) fn end_entity_params(subject_alt_names: Vec<String>) -> rcgen::CertificateParams {
let mut ee_params = rcgen::CertificateParams::new(subject_alt_names);
ee_params.is_ca = rcgen::IsCa::ExplicitNoCa;
ee_params.alg = RCGEN_SIGNATURE_ALG;
ee_params
}
74 changes: 33 additions & 41 deletions src/verify_cert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ enum Role {
mod tests {
use super::*;
#[cfg(feature = "alloc")]
use crate::test_utils::{make_end_entity, make_issuer};
use crate::test_utils::{issuer_params, make_end_entity, make_issuer};

#[test]
fn eku_key_purpose_id() {
Expand All @@ -508,38 +508,45 @@ mod tests {
}

#[cfg(feature = "alloc")]
enum TrustAnchorIsActualIssuer {
Yes,
No,
enum ChainTrustAnchor {
NotInChain,
InChain,
}

#[cfg(feature = "alloc")]
fn build_degenerate_chain(
intermediate_count: usize,
trust_anchor_is_actual_issuer: TrustAnchorIsActualIssuer,
budget: Option<Budget>,
trust_anchor: ChainTrustAnchor,
) -> ControlFlow<Error, Error> {
let ca_cert = make_issuer("Bogus Subject", None);
let ca_cert = make_issuer("Bogus Subject");
let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());

let mut intermediates = Vec::with_capacity(intermediate_count);
let mut intermediates = Vec::with_capacity(intermediate_count + 1);
if let ChainTrustAnchor::InChain = trust_anchor {
intermediates.push(ca_cert_der.to_vec());
}

let mut issuer = ca_cert;
for _ in 0..intermediate_count {
let intermediate = make_issuer("Bogus Subject", None);
let intermediate = make_issuer("Bogus Subject");
let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap();
intermediates.push(intermediate_der);
issuer = intermediate;
}

if let TrustAnchorIsActualIssuer::No = trust_anchor_is_actual_issuer {
intermediates.pop();
}
let trust_anchor = match trust_anchor {
ChainTrustAnchor::InChain => {
let unused_anchor = make_issuer("Bogus Trust Anchor");
CertificateDer::from(unused_anchor.serialize_der().unwrap())
}
ChainTrustAnchor::NotInChain => ca_cert_der,
};

verify_chain(
&ca_cert_der,
&trust_anchor,
&intermediates,
&make_end_entity(&issuer),
budget,
None,
)
.unwrap_err()
}
Expand All @@ -548,43 +555,29 @@ mod tests {
#[cfg(feature = "alloc")]
fn test_too_many_signatures() {
assert!(matches!(
build_degenerate_chain(5, TrustAnchorIsActualIssuer::Yes, None),
build_degenerate_chain(5, ChainTrustAnchor::NotInChain),
ControlFlow::Break(Error::MaximumSignatureChecksExceeded)
));
}

#[test]
#[cfg(feature = "alloc")]
fn test_too_many_path_calls_mini() {
fn test_too_many_path_calls() {
assert!(matches!(
build_degenerate_chain(
10,
TrustAnchorIsActualIssuer::No,
Some(Budget {
// Crafting a chain that will expend the build chain calls budget without
// first expending the signature checks budget is tricky, so we artificially
// inflate the signature limit to make this test easier to write.
signatures: usize::MAX,
// We don't use the default build chain budget here. Doing so makes this test
// run slowly (due to testing quadratic runtime up to the limit) without adding
// much value from a testing perspective over using a smaller non-default limit.
build_chain_calls: 100,
..Budget::default()
})
),
dbg!(build_degenerate_chain(10, ChainTrustAnchor::InChain)),
ControlFlow::Break(Error::MaximumPathBuildCallsExceeded)
));
}

#[cfg(feature = "alloc")]
fn build_linear_chain(chain_length: usize) -> Result<(), ControlFlow<Error, Error>> {
let ca_cert = make_issuer(format!("Bogus Subject {chain_length}"), None);
let ca_cert = make_issuer(format!("Bogus Subject {chain_length}"));
let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());

let mut intermediates = Vec::with_capacity(chain_length);
let mut issuer = ca_cert;
for i in 0..chain_length {
let intermediate = make_issuer(format!("Bogus Subject {i}"), None);
let intermediate = make_issuer(format!("Bogus Subject {i}"));
let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap();
intermediates.push(intermediate_der);
issuer = intermediate;
Expand Down Expand Up @@ -623,13 +616,12 @@ mod tests {
fn name_constraint_budget() {
// Issue a trust anchor that imposes name constraints. The constraint should match
// the end entity certificate SAN.
let ca_cert = make_issuer(
"Constrained Root",
Some(rcgen::NameConstraints {
permitted_subtrees: vec![rcgen::GeneralSubtree::DnsName(".com".into())],
excluded_subtrees: vec![],
}),
);
let mut ca_cert_params = issuer_params("Constrained Root");
ca_cert_params.name_constraints = Some(rcgen::NameConstraints {
permitted_subtrees: vec![rcgen::GeneralSubtree::DnsName(".com".into())],
excluded_subtrees: vec![],
});
let ca_cert = rcgen::Certificate::from_params(ca_cert_params).unwrap();
let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap());

// Create a series of intermediate issuers. We'll only use one in the actual built path,
Expand All @@ -638,7 +630,7 @@ mod tests {
const NUM_INTERMEDIATES: usize = 5;
let mut intermediates = Vec::with_capacity(NUM_INTERMEDIATES);
for i in 0..NUM_INTERMEDIATES {
intermediates.push(make_issuer(format!("Intermediate {i}"), None));
intermediates.push(make_issuer(format!("Intermediate {i}")));
}

// Each intermediate should be issued by the trust anchor.
Expand Down