diff --git a/src/end_entity.rs b/src/end_entity.rs index 0f4dd777..42613f49 100644 --- a/src/end_entity.rs +++ b/src/end_entity.rs @@ -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 diff --git a/src/test_utils.rs b/src/test_utils.rs index b30c12d7..70b931b9 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -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) -> rcgen::Certificate { + rcgen::Certificate::from_params(issuer_params(org_name)).unwrap() +} -#[cfg(feature = "alloc")] -pub(crate) fn make_issuer( - org_name: impl Into, - name_constraints: Option, -) -> 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) -> rcgen::CertificateParams { let mut ca_params = rcgen::CertificateParams::new(Vec::new()); ca_params .distinguished_name @@ -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) -> 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 +} diff --git a/src/verify_cert.rs b/src/verify_cert.rs index c4240ef0..da563f32 100644 --- a/src/verify_cert.rs +++ b/src/verify_cert.rs @@ -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() { @@ -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, + trust_anchor: ChainTrustAnchor, ) -> ControlFlow { - 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() } @@ -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> { - 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; @@ -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, @@ -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.