From 3f3610d83879fe5ec3c7b2b056ffae3c05574800 Mon Sep 17 00:00:00 2001 From: Daniel McCarney Date: Thu, 28 Mar 2024 15:37:20 -0400 Subject: [PATCH] deps: rcgen 0.12 -> 0.13 * updates rcgen dev dependency to 0.13 * fixes breaking upstream changes * reworks tests to remove some duplication/improve member ordering --- Cargo.lock | 41 ++--- Cargo.toml | 2 +- benches/benchmark.rs | 22 +-- src/end_entity.rs | 17 +- src/test_utils.rs | 36 +++-- src/verify_cert.rs | 366 +++++++++++++++++++++++-------------------- 6 files changed, 254 insertions(+), 230 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4de93144..23e49940 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,7 @@ dependencies = [ "aws-lc-sys", "mirai-annotations", "paste", + "untrusted 0.7.1", "zeroize", ] @@ -37,12 +38,6 @@ dependencies = [ "paste", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.0" @@ -310,16 +305,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "pem" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" -dependencies = [ - "base64 0.21.7", - "serde", -] - [[package]] name = "pkg-config" version = "0.3.30" @@ -362,12 +347,12 @@ dependencies = [ [[package]] name = "rcgen" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48406db8ac1f3cbc7dcdb56ec355343817958a356ff430259bb07baf7607e1e1" +checksum = "aa96feb4d337a43eae1b39b6d4cafc2860a46cf9cec6f1e65294244ece65e348" dependencies = [ - "pem", - "ring", + "aws-lc-rs", + "rustls-pki-types", "time", "yasna", ] @@ -412,7 +397,7 @@ dependencies = [ "getrandom", "libc", "spin", - "untrusted", + "untrusted 0.9.0", "windows-sys 0.52.0", ] @@ -437,16 +422,16 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "048a63e5b3ac996d78d402940b5fa47973d2d080c6c6fffa1d0f19c4445310b7" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" [[package]] name = "rustls-webpki" version = "0.102.2" dependencies = [ "aws-lc-rs", - "base64 0.22.0", + "base64", "bencher", "bzip2", "once_cell", @@ -455,7 +440,7 @@ dependencies = [ "rustls-pki-types", "serde", "serde_json", - "untrusted", + "untrusted 0.9.0", ] [[package]] @@ -543,6 +528,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "untrusted" version = "0.9.0" diff --git a/Cargo.toml b/Cargo.toml index a3a2b6ce..51618e6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ base64 = "0.22" bencher = "0.1.5" bzip2 = "0.4.4" once_cell = "1.17.2" -rcgen = { version = "0.12.0" } +rcgen = { version = "0.13", default-features = false, features = ["aws_lc_rs"] } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 32dde67e..cc6e89a2 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,9 +1,9 @@ use bencher::{benchmark_group, benchmark_main, Bencher}; use once_cell::sync::Lazy; use rcgen::{ - date_time_ymd, BasicConstraints, Certificate, CertificateParams, CertificateRevocationList, - CertificateRevocationListParams, IsCa, KeyIdMethod, KeyUsagePurpose, RevocationReason, - RevokedCertParams, SerialNumber, PKCS_ECDSA_P256_SHA256, + date_time_ymd, BasicConstraints, CertificateParams, CertificateRevocationListParams, + CertifiedKey, IsCa, KeyIdMethod, KeyPair, KeyUsagePurpose, RevocationReason, RevokedCertParams, + SerialNumber, PKCS_ECDSA_P256_SHA256, }; use std::fs::File; @@ -16,15 +16,18 @@ use webpki::{BorrowedCertRevocationList, CertRevocationList, OwnedCertRevocation /// Lazy initialized CRL issuer to be used when generating CRL data. Includes /// `KeyUsagePurpose::CrlSign` key usage bit. -static CRL_ISSUER: Lazy> = Lazy::new(|| { - let mut issuer_params = CertificateParams::new(vec!["crl.issuer.example.com".to_string()]); +static CRL_ISSUER: Lazy> = Lazy::new(|| { + let mut issuer_params = + CertificateParams::new(vec!["crl.issuer.example.com".to_string()]).unwrap(); issuer_params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); issuer_params.key_usages = vec![ KeyUsagePurpose::KeyCertSign, KeyUsagePurpose::DigitalSignature, KeyUsagePurpose::CrlSign, ]; - Mutex::new(Certificate::from_params(issuer_params).unwrap()) + let key_pair = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256).unwrap(); + let cert = issuer_params.self_signed(&key_pair).unwrap(); + Mutex::new(CertifiedKey { cert, key_pair }) }); /// Number of revoked certificates to include in the small benchmark CRL. Produces a CRL roughly @@ -83,14 +86,15 @@ fn generate_crl(revoked_count: usize) -> Vec { this_update: date_time_ymd(2023, 6, 17), next_update: date_time_ymd(2024, 6, 17), crl_number: SerialNumber::from(1234), - alg: &PKCS_ECDSA_P256_SHA256, key_identifier_method: KeyIdMethod::Sha256, issuing_distribution_point: None, revoked_certs, }; - let crl = CertificateRevocationList::from_params(crl).unwrap(); let issuer = CRL_ISSUER.lock().unwrap(); - crl.serialize_der_with_signer(&issuer).unwrap() + crl.signed_by(&issuer.cert, &issuer.key_pair) + .unwrap() + .der() + .to_vec() } /// Benchmark parsing a small CRL file into a borrowed representation. diff --git a/src/end_entity.rs b/src/end_entity.rs index 605f0227..6ae4d81a 100644 --- a/src/end_entity.rs +++ b/src/end_entity.rs @@ -197,23 +197,22 @@ mod tests { let issuer = test_utils::make_issuer("Test"); - let ee_cert_der = { + let ee_cert = { 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()), + rcgen::DnValue::PrintableString( + rcgen::PrintableString::try_from("example.com").unwrap(), + ), ); - let cert = rcgen::Certificate::from_params(params) - .expect("failed to make ee cert (this is a test bug)"); - let bytes = cert - .serialize_der_with_signer(&issuer) - .expect("failed to serialize signed ee cert (this is a test bug)"); - CertificateDer::from(bytes) + params + .signed_by(&test_utils::make_keypair(), &issuer.cert, &issuer.key_pair) + .expect("failed to make ee cert (this is a test bug)") }; - expect_dns_name(&ee_cert_der, DNS_NAME); + expect_dns_name(ee_cert.der(), DNS_NAME); } // This test reproduces https://github.com/rustls/webpki/issues/167 --- an diff --git a/src/test_utils.rs b/src/test_utils.rs index 0b5dd7ef..ffaff592 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -1,20 +1,26 @@ #![cfg(feature = "alloc")] use std::prelude::v1::*; -use crate::types::CertificateDer; - /// 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() +pub(crate) fn make_issuer(org_name: impl Into) -> rcgen::CertifiedKey { + let key_pair = make_keypair(); + rcgen::CertifiedKey { + cert: issuer_params(org_name).self_signed(&key_pair).unwrap(), + key_pair, + } +} + +pub(crate) fn make_keypair() -> rcgen::KeyPair { + rcgen::KeyPair::generate_for(RCGEN_SIGNATURE_ALG).unwrap() } /// 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()); + let mut ca_params = rcgen::CertificateParams::new(Vec::new()).unwrap(); ca_params .distinguished_name .push(rcgen::DnType::OrganizationName, org_name); @@ -24,23 +30,25 @@ pub(crate) fn issuer_params(org_name: impl Into) -> rcgen::CertificatePa rcgen::KeyUsagePurpose::DigitalSignature, rcgen::KeyUsagePurpose::CrlSign, ]; - ca_params.alg = RCGEN_SIGNATURE_ALG; ca_params } #[cfg_attr(not(feature = "ring"), allow(dead_code))] -pub(crate) fn make_end_entity(issuer: &rcgen::Certificate) -> CertificateDer<'static> { - CertificateDer::from( - rcgen::Certificate::from_params(end_entity_params(vec!["example.com".into()])) - .unwrap() - .serialize_der_with_signer(issuer) +pub(crate) fn make_end_entity( + issuer: &rcgen::Certificate, + issuer_key: &rcgen::KeyPair, +) -> rcgen::CertifiedKey { + let key_pair = make_keypair(); + rcgen::CertifiedKey { + cert: end_entity_params(vec!["example.com".into()]) + .signed_by(&key_pair, issuer, issuer_key) .unwrap(), - ) + key_pair, + } } pub(crate) fn end_entity_params(subject_alt_names: Vec) -> rcgen::CertificateParams { - let mut ee_params = rcgen::CertificateParams::new(subject_alt_names); + let mut ee_params = rcgen::CertificateParams::new(subject_alt_names).unwrap(); 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 49a36ecc..706015dc 100644 --- a/src/verify_cert.rs +++ b/src/verify_cert.rs @@ -699,8 +699,9 @@ pub(crate) enum Role { #[cfg(all(test, feature = "alloc", any(feature = "ring", feature = "aws_lc_rs")))] mod tests { use super::*; - use crate::test_utils::{issuer_params, make_end_entity, make_issuer}; + use crate::test_utils::{issuer_params, make_end_entity, make_issuer, make_keypair}; use crate::trust_anchor::anchor_from_trusted_cert; + use rcgen::{CertifiedKey, KeyPair}; use std::dbg; use std::prelude::v1::*; @@ -712,57 +713,10 @@ mod tests { ) } - #[cfg(feature = "alloc")] - enum ChainTrustAnchor { - NotInChain, - InChain, - } - - fn build_degenerate_chain( - intermediate_count: usize, - trust_anchor: ChainTrustAnchor, - ) -> ControlFlow { - 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 + 1); - if let ChainTrustAnchor::InChain = trust_anchor { - intermediates.push(CertificateDer::from(ca_cert_der.to_vec())); - } - - let mut issuer = ca_cert; - for _ in 0..intermediate_count { - let intermediate = make_issuer("Bogus Subject"); - let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap(); - intermediates.push(CertificateDer::from(intermediate_der)); - issuer = intermediate; - } - - 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, - }; - - let ee_der = make_end_entity(&issuer); - let ee_cert = EndEntityCert::try_from(&ee_der).unwrap(); - verify_chain( - &[anchor_from_trusted_cert(&trust_anchor).unwrap()], - &intermediates, - &ee_cert, - None, - None, - ) - .map(|_| ()) - .unwrap_err() - } - #[test] fn test_too_many_signatures() { assert!(matches!( - build_degenerate_chain(5, ChainTrustAnchor::NotInChain), + verify_degenerate_chain(5, ChainTrustAnchor::NotInChain), ControlFlow::Break(Error::MaximumSignatureChecksExceeded) )); } @@ -770,83 +724,25 @@ mod tests { #[test] fn test_too_many_path_calls() { assert!(matches!( - dbg!(build_degenerate_chain(10, ChainTrustAnchor::InChain)), + dbg!(verify_degenerate_chain(10, ChainTrustAnchor::InChain)), ControlFlow::Break(Error::MaximumPathBuildCallsExceeded) )); } - fn build_linear_chain(chain_length: usize) -> Result<(), ControlFlow> { - let ca_cert = make_issuer(format!("Bogus Subject {chain_length}")); - let ca_cert_der = CertificateDer::from(ca_cert.serialize_der().unwrap()); - let anchor = anchor_from_trusted_cert(&ca_cert_der).unwrap(); - let anchors = &[anchor.clone()]; - - 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}")); - let intermediate_der = intermediate.serialize_der_with_signer(&issuer).unwrap(); - intermediates.push(CertificateDer::from(intermediate_der)); - issuer = intermediate; - } - - let ee_der = make_end_entity(&issuer); - let ee_cert = EndEntityCert::try_from(&ee_der).unwrap(); - - let expected_chain = |path: &VerifiedPath<'_>| { - assert_eq!(path.anchor().subject, anchor.subject); - assert!(public_values_eq(path.end_entity().subject, ee_cert.subject)); - assert_eq!(path.intermediate_certificates().count(), chain_length); - - let intermediate_certs = intermediates - .iter() - .map(|der| Cert::from_der(untrusted::Input::from(der.as_ref())).unwrap()) - .collect::>(); - - for (cert, expected) in path - .intermediate_certificates() - .rev() - .zip(intermediate_certs.iter()) - { - assert!(public_values_eq(cert.subject, expected.subject)); - assert_eq!(cert.der(), expected.der()); - } - - for (cert, expected) in path - .intermediate_certificates() - .zip(intermediate_certs.iter().rev()) - { - assert!(public_values_eq(cert.subject, expected.subject)); - assert_eq!(cert.der(), expected.der()); - } - - Ok(()) - }; - - verify_chain( - anchors, - &intermediates, - &ee_cert, - Some(&expected_chain), - None, - ) - .map(|_| ()) - } - #[test] fn longest_allowed_path() { - assert!(build_linear_chain(1).is_ok()); - assert!(build_linear_chain(2).is_ok()); - assert!(build_linear_chain(3).is_ok()); - assert!(build_linear_chain(4).is_ok()); - assert!(build_linear_chain(5).is_ok()); - assert!(build_linear_chain(6).is_ok()); + assert!(verify_linear_chain(1).is_ok()); + assert!(verify_linear_chain(2).is_ok()); + assert!(verify_linear_chain(3).is_ok()); + assert!(verify_linear_chain(4).is_ok()); + assert!(verify_linear_chain(5).is_ok()); + assert!(verify_linear_chain(6).is_ok()); } #[test] fn path_too_long() { assert!(matches!( - build_linear_chain(7), + verify_linear_chain(7), Err(ControlFlow::Continue(Error::MaximumPathDepthExceeded)) )); } @@ -860,30 +756,27 @@ mod tests { 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()); - let anchors = &[anchor_from_trusted_cert(&ca_cert_der).unwrap()]; + let ca_key_pair = KeyPair::generate().unwrap(); + let ca_cert = ca_cert_params.self_signed(&ca_key_pair).unwrap(); // Create a series of intermediate issuers. We'll only use one in the actual built path, // helping demonstrate that the name constraint budget is not expended checking certificates // that are not part of the path we compute. - 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}"))); - } - - // Each intermediate should be issued by the trust anchor. - let mut intermediates_der = Vec::with_capacity(NUM_INTERMEDIATES); - for intermediate in &intermediates { - intermediates_der.push(CertificateDer::from( - intermediate.serialize_der_with_signer(&ca_cert).unwrap(), - )); + let mut intermediates = Vec::with_capacity(5); + for i in 0..5 { + let intermediate = issuer_params(format!("Intermediate {i}")); + let intermediate_key_pair = KeyPair::generate().unwrap(); + // Each intermediate should be issued by the trust anchor. + let intermediate = intermediate + .signed_by(&intermediate_key_pair, &ca_cert, &ca_key_pair) + .unwrap(); + intermediates.push((intermediate, intermediate_key_pair)); } // Create an end-entity cert that is issued by the last of the intermediates. - let ee_der = make_end_entity(intermediates.last().unwrap()); - let ee_cert = EndEntityCert::try_from(&ee_der).unwrap(); + let last_issuer = intermediates.last().unwrap(); + let ee_cert = make_end_entity(&last_issuer.0, &last_issuer.1); + let ee_cert = EndEntityCert::try_from(ee_cert.cert.der()).unwrap(); // We use a custom budget to make it easier to write a test, otherwise it is tricky to // stuff enough names/constraints into the potential chains while staying within the path @@ -897,6 +790,13 @@ mod tests { ..Budget::default() }; + let ca_cert_der = ca_cert.into(); + let anchors = &[anchor_from_trusted_cert(&ca_cert_der).unwrap()]; + let intermediates_der = intermediates + .iter() + .map(|(cert, _)| cert.der().clone()) + .collect::>(); + // Validation should succeed with the name constraint comparison budget allocated above. // This shows that we're not consuming budget on unused intermediates: we didn't budget // enough comparisons for that to pass the overall chain building. @@ -973,57 +873,56 @@ mod tests { // Create a trust anchor, and use it to issue two distinct intermediate certificates, each // with a unique subject and keypair. let trust_anchor = make_issuer("Trust Anchor"); - let trust_anchor_der = CertificateDer::from(trust_anchor.serialize_der().unwrap()); let trust_anchor_cert = - Cert::from_der(untrusted::Input::from(trust_anchor_der.as_ref())).unwrap(); - let trust_anchors = &[anchor_from_trusted_cert(&trust_anchor_der).unwrap()]; - - let intermediate_a = make_issuer("Intermediate A"); - let intermediate_a_der = CertificateDer::from( - intermediate_a - .serialize_der_with_signer(&trust_anchor) - .unwrap(), - ); + Cert::from_der(untrusted::Input::from(trust_anchor.cert.der())).unwrap(); + let trust_anchors = &[anchor_from_trusted_cert(trust_anchor.cert.der()).unwrap()]; + + let intermediate_a = issuer_params("Intermediate A"); + let intermediate_a_kp = KeyPair::generate().unwrap(); + let intermediate_a = intermediate_a + .signed_by( + &intermediate_a_kp, + &trust_anchor.cert, + &trust_anchor.key_pair, + ) + .unwrap(); let intermediate_a_cert = - Cert::from_der(untrusted::Input::from(intermediate_a_der.as_ref())).unwrap(); - - let intermediate_c = make_issuer("Intermediate C"); - let intermediate_c_der = CertificateDer::from( - intermediate_c - .serialize_der_with_signer(&trust_anchor) - .unwrap(), - ); + Cert::from_der(untrusted::Input::from(intermediate_a.der())).unwrap(); + + let intermediate_c = issuer_params("Intermediate C"); + let intermediate_c_kp = KeyPair::generate().unwrap(); + let intermediate_c = intermediate_c + .signed_by( + &intermediate_c_kp, + &trust_anchor.cert, + &trust_anchor.key_pair, + ) + .unwrap(); let intermediate_c_cert = - Cert::from_der(untrusted::Input::from(intermediate_c_der.as_ref())).unwrap(); + Cert::from_der(untrusted::Input::from(intermediate_c.der())).unwrap(); // Next, create an intermediate that is issued by both of the intermediates above. // Both should share the same subject, and key pair, but will differ in the issuer. - let intermediate_b_key = rcgen::KeyPair::generate(&rcgen::PKCS_ECDSA_P256_SHA256).unwrap(); - let mut intermediate_b_params = issuer_params("Intermediate"); - intermediate_b_params.key_pair = Some(intermediate_b_key); - let intermediate_b = rcgen::Certificate::from_params(intermediate_b_params).unwrap(); - - let intermediate_b_a_der = CertificateDer::from( - intermediate_b - .serialize_der_with_signer(&intermediate_a) - .unwrap(), - ); - let intermediate_b_c_der = CertificateDer::from( - intermediate_b - .serialize_der_with_signer(&intermediate_c) - .unwrap(), - ); + let intermediate_b_key = make_keypair(); + let intermediate_b_params = issuer_params("Intermediate"); + let intermediate_b_a = intermediate_b_params + .clone() + .signed_by(&intermediate_b_key, &intermediate_a, &intermediate_a_kp) + .unwrap(); + let intermediate_b_c = intermediate_b_params + .signed_by(&intermediate_b_key, &intermediate_c, &intermediate_c_kp) + .unwrap(); let intermediates = &[ - intermediate_a_der.clone(), - intermediate_c_der.clone(), - intermediate_b_a_der.clone(), - intermediate_b_c_der.clone(), + intermediate_a.der().clone(), + intermediate_c.der().clone(), + intermediate_b_a.der().clone(), + intermediate_b_c.der().clone(), ]; // Create an end entity certificate signed by the keypair of the intermediates created above. - let ee = make_end_entity(&intermediate_b); - let ee_cert = &EndEntityCert::try_from(&ee).unwrap(); + let ee = make_end_entity(&intermediate_b_a, &intermediate_b_key); + let ee_cert = &EndEntityCert::try_from(ee.cert.der()).unwrap(); // We should be able to create a valid path from EE to trust anchor. let path = verify_chain(trust_anchors, intermediates, ee_cert, None, None).unwrap(); @@ -1071,6 +970,129 @@ mod tests { assert_eq!(path_intermediates[1].issuer(), trust_anchor_cert.subject()); } + fn verify_degenerate_chain( + intermediate_count: usize, + chain_trust_anchor: ChainTrustAnchor, + ) -> ControlFlow { + let ca_cert = make_issuer("Bogus Subject"); + let intermediates = build_linear_chain(&ca_cert, intermediate_count, true); + + let trust_anchor = match chain_trust_anchor { + ChainTrustAnchor::InChain => make_issuer("Bogus Trust Anchor"), + ChainTrustAnchor::NotInChain => ca_cert, + }; + + let last_intermediate = intermediates.last().unwrap(); + let ee_cert = make_end_entity(&last_intermediate.cert, &last_intermediate.key_pair); + let ee_cert = EndEntityCert::try_from(ee_cert.cert.der()).unwrap(); + let intermediates = &mut intermediates + .into_iter() + .map(|cert_and_key| cert_and_key.cert.into()) + .collect::>(); + let trust_anchor_der: CertificateDer<'_> = trust_anchor.cert.into(); + let webpki_ta = anchor_from_trusted_cert(&trust_anchor_der).unwrap(); + if matches!(chain_trust_anchor, ChainTrustAnchor::InChain) { + // Note: we clone the trust anchor DER here because we can't move it into the chain + // as it's loaned to webpki_ta above. + intermediates.insert(0, trust_anchor_der.clone()) + } + + verify_chain(&[webpki_ta], intermediates, &ee_cert, None, None) + .map(|_| ()) + .unwrap_err() + } + + #[cfg(feature = "alloc")] + enum ChainTrustAnchor { + NotInChain, + InChain, + } + + fn verify_linear_chain(chain_length: usize) -> Result<(), ControlFlow> { + let ca_cert = make_issuer(format!("Bogus Subject {chain_length}")); + let intermediates = build_linear_chain(&ca_cert, chain_length, false); + + let ca_cert_der: CertificateDer<'_> = ca_cert.cert.into(); + let anchor = anchor_from_trusted_cert(&ca_cert_der).unwrap(); + let anchors = &[anchor.clone()]; + + let last_intermediate = intermediates.last().unwrap(); + let ee_cert = make_end_entity(&last_intermediate.cert, &last_intermediate.key_pair); + let ee_cert = EndEntityCert::try_from(ee_cert.cert.der()).unwrap(); + + let intermediates_der = intermediates + .into_iter() + .map(|cert_and_key| cert_and_key.cert.into()) + .collect::>(); + + let expected_chain = |path: &VerifiedPath<'_>| { + assert_eq!(path.anchor().subject, anchor.subject); + assert!(public_values_eq(path.end_entity().subject, ee_cert.subject)); + assert_eq!(path.intermediate_certificates().count(), chain_length); + + let intermediate_certs = intermediates_der + .iter() + .map(|der: &CertificateDer| Cert::from_der(untrusted::Input::from(der)).unwrap()) + .collect::>(); + + for (cert, expected) in path + .intermediate_certificates() + .rev() + .zip(intermediate_certs.iter()) + { + assert!(public_values_eq(cert.subject, expected.subject)); + assert_eq!(cert.der(), expected.der()); + } + + for (cert, expected) in path + .intermediate_certificates() + .zip(intermediate_certs.iter().rev()) + { + assert!(public_values_eq(cert.subject, expected.subject)); + assert_eq!(cert.der(), expected.der()); + } + + Ok(()) + }; + + verify_chain( + anchors, + &intermediates_der, + &ee_cert, + Some(&expected_chain), + None, + ) + .map(|_| ()) + } + + fn build_linear_chain( + ca_cert: &CertifiedKey, + chain_length: usize, + all_same_subject: bool, + ) -> Vec { + let mut intermediates: Vec = Vec::with_capacity(chain_length); + for i in 0..chain_length { + let (issuer, issuer_key) = if i == 0 { + (&ca_cert.cert, &ca_cert.key_pair) + } else { + (&intermediates[i - 1].cert, &intermediates[i - 1].key_pair) + }; + let intermediate = issuer_params(match all_same_subject { + true => "Bogus Subject".to_string(), + false => format!("Bogus Subject {i}"), + }); + let intermediate_key_pair = make_keypair(); + let intermediate = intermediate + .signed_by(&intermediate_key_pair, issuer, issuer_key) + .unwrap(); + intermediates.push(CertifiedKey { + cert: intermediate, + key_pair: intermediate_key_pair, + }); + } + intermediates + } + fn verify_chain<'a>( trust_anchors: &'a [TrustAnchor<'a>], intermediate_certs: &'a [CertificateDer<'a>],