From 4dd653f7affb167e0a4f93a1f371f29d77dd7dbd Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Wed, 10 Jun 2026 14:56:44 +0200 Subject: [PATCH] Force intermediates and revocation to be specified --- src/verify_cert.rs | 32 +++++++---------- tests/amazon.rs | 61 ++++++++++++--------------------- tests/client_auth.rs | 2 ++ tests/client_auth_revocation.rs | 14 ++++---- tests/custom_ekus.rs | 8 ++++- tests/integration.rs | 31 +++++++++++------ tests/tls_server_certs.rs | 2 ++ tests/x509_limbo.rs | 23 +++++-------- 8 files changed, 81 insertions(+), 92 deletions(-) diff --git a/src/verify_cert.rs b/src/verify_cert.rs index d57362ae..1a0e74d0 100644 --- a/src/verify_cert.rs +++ b/src/verify_cert.rs @@ -46,6 +46,11 @@ pub struct PathBuilder<'a, 'p> { impl<'a, 'p: 'a> PathBuilder<'a, 'p> { /// Build a new [`PathBuilder`] with the given parameters. /// + /// * `intermediate_certs` is the list of intermediate certificates to use for path building. + /// These certificates should be sent by the peer. They are not trusted, but can be used to + /// build a path to a trusted root. + /// * `revocation` provides the revocation options to use for path building. If `None`, no + /// revocation checks will be performed. /// * `eku` is the intended usage of the certificate, indicating what kind /// of usage we're verifying the certificate for. The default [`ExtendedKeyUsageValidator`] /// implementation is [`ExtendedKeyUsage`]. @@ -54,6 +59,8 @@ impl<'a, 'p: 'a> PathBuilder<'a, 'p> { /// public key is not validated against this list. /// * `trust_anchors` is the list of root CAs to trust in the built path. pub fn new( + intermediate_certs: &'p [CertificateDer<'p>], + revocation: Option>, eku: &'p dyn ExtendedKeyUsageValidator, supported_sig_algs: &'a [&'a dyn SignatureVerificationAlgorithm], trust_anchors: &'p [TrustAnchor<'p>], @@ -62,28 +69,12 @@ impl<'a, 'p: 'a> PathBuilder<'a, 'p> { eku, supported_sig_algs, trust_anchors, - intermediate_certs: &[], - revocation: None, + intermediate_certs, + revocation, verify_path: None, } } - /// Set the sequence of intermediate certificates to use for path building. - /// - /// These should be sent by the peer. Defaults to empty. - pub fn with_intermediate_certs(mut self, intermediate_certs: &'p [CertificateDer<'p>]) -> Self { - self.intermediate_certs = intermediate_certs; - self - } - - /// Set the revocation options to use for path building. - /// - /// By default, revocation checking is disabled. - pub fn with_revocation(mut self, revocation: RevocationOptions<'a>) -> Self { - self.revocation = Some(revocation); - self - } - /// Set a path verification function to use for path building. /// /// `verify()` will only be called for potentially verified paths, that is, paths that @@ -1509,11 +1500,12 @@ mod tests { let mut path = PartialPath::new(ee_cert); let builder = PathBuilder::new( + intermediate_certs, + None, &ExtendedKeyUsage::SERVER_AUTH, rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, trust_anchors, - ) - .with_intermediate_certs(intermediate_certs); + ); let builder = match verify_path { Some(verify) => builder.with_path_verification(verify), None => builder, diff --git a/tests/amazon.rs b/tests/amazon.rs index 7e2955d1..444af416 100644 --- a/tests/amazon.rs +++ b/tests/amazon.rs @@ -237,58 +237,42 @@ pub fn amazon() { Some(&all_crls), ] { let builder = PathBuilder::new( + &intermediates, + crls.map(|c| revocation_options_for_test(c)), &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(&intermediates); - - let builder = match crls { - Some(crls) => builder.with_revocation(revocation_options_for_test(crls)), - None => builder, - }; + ); assert!(builder.build(&cert, time).is_ok()); let builder = PathBuilder::new( + &intermediates_legacy, + crls.map(|c| revocation_options_for_test(c)), &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &legacy_anchors, - ) - .with_intermediate_certs(&intermediates_legacy); - - let builder = match crls { - Some(crls) => builder.with_revocation(revocation_options_for_test(crls)), - None => builder, - }; + ); assert!(builder.build(&cert, time).is_ok()); let builder = PathBuilder::new( + &intermediates_legacy, + crls.map(|c| revocation_options_for_test(c)), &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &all_anchors, - ) - .with_intermediate_certs(&intermediates_legacy); - - let builder = match crls { - Some(crls) => builder.with_revocation(revocation_options_for_test(crls)), - None => builder, - }; + ); assert!(builder.build(&cert, time).is_ok()); let builder = PathBuilder::new( + &intermediates_legacy, + crls.map(|c| revocation_options_for_test(c)), &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &all_anchors, - ) - .with_intermediate_certs(&intermediates_legacy); - - let builder = match crls { - Some(crls) => builder.with_revocation(revocation_options_for_test(crls)), - None => builder, - }; + ); // verify should find shortest path let path = builder.build(&cert, time).unwrap(); @@ -302,28 +286,24 @@ pub fn amazon() { for &crls in &[None, Some(&roots_crls)] { let builder = PathBuilder::new( + &intermediates, + crls.map(|c| revocation_options_for_test(c)), &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(&intermediates); - - let builder = match crls { - Some(crls) => builder.with_revocation(revocation_options_for_test(crls)), - None => builder, - }; + ); assert!(builder.build(&cert, time).is_ok()); } for &crls in &[&intermediates_crls, &all_crls] { let builder = PathBuilder::new( + &intermediates, + Some(revocation_options_for_test(crls)), &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(&intermediates) - .with_revocation(revocation_options_for_test(crls)); + ); assert!( builder @@ -335,11 +315,12 @@ pub fn amazon() { for &(cert, _dns_name) in expired_certs { let builder = PathBuilder::new( + &intermediates, + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(&intermediates); + ); let cert = CertificateDer::from(cert); let cert = EndEntityCert::try_from(&cert).unwrap(); diff --git a/tests/client_auth.rs b/tests/client_auth.rs index b7cdb1b3..770acbf5 100644 --- a/tests/client_auth.rs +++ b/tests/client_auth.rs @@ -79,6 +79,8 @@ fn cert_with_serverauth_eku_rejected_for_client_auth() { fn check_cert(ee: &[u8], ca: CertificateDer<'static>) -> Result<(), webpki::Error> { let anchors = &[anchor_from_trusted_cert(&ca).unwrap()]; let builder = PathBuilder::new( + &[], + None, &ExtendedKeyUsage::CLIENT_AUTH, rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, anchors, diff --git a/tests/client_auth_revocation.rs b/tests/client_auth_revocation.rs index bfbcad65..5c946808 100644 --- a/tests/client_auth_revocation.rs +++ b/tests/client_auth_revocation.rs @@ -50,13 +50,13 @@ fn check_cert( .map(|cert| CertificateDer::from(*cert)) .collect::>(); - let builder = PathBuilder::new(&ExtendedKeyUsage::CLIENT_AUTH, ALGS, anchors) - .with_intermediate_certs(&intermediates); - - let builder = match revocation { - Some(crls) => builder.with_revocation(crls), - None => builder, - }; + let builder = PathBuilder::new( + &intermediates, + revocation, + &ExtendedKeyUsage::CLIENT_AUTH, + ALGS, + anchors, + ); let ee = CertificateDer::from(ee); let cert = webpki::EndEntityCert::try_from(&ee).unwrap(); diff --git a/tests/custom_ekus.rs b/tests/custom_ekus.rs index 4f5d8f6c..ee70080d 100644 --- a/tests/custom_ekus.rs +++ b/tests/custom_ekus.rs @@ -14,7 +14,13 @@ fn check_cert( ) { let ca = CertificateDer::from(ca); let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; - let builder = PathBuilder::new(eku, rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, &anchors); + let builder = PathBuilder::new( + &[], + None, + eku, + rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, + &anchors, + ); let ee = CertificateDer::from(ee); let cert = webpki::EndEntityCert::try_from(&ee).unwrap(); diff --git a/tests/integration.rs b/tests/integration.rs index ea7bc62b..1fadb90d 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -33,11 +33,12 @@ fn netflix() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let intermediates = &[inter]; let builder = PathBuilder::new( + intermediates, + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(intermediates); + ); let time = UnixTime::since_unix_epoch(Duration::from_secs(1_492_441_716)); // 2017-04-17T15:08:36Z let ee = CertificateDer::from(ee); @@ -56,11 +57,12 @@ fn sanofi_rsa_signature_with_absent_algorithm_params() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let intermediates = &[inter]; let builder = PathBuilder::new( + intermediates, + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(intermediates); + ); let time = UnixTime::since_unix_epoch(Duration::from_secs(1_746_549_566)); // 2025-05-06T17:39:26Z let ee = CertificateDer::from(ee); @@ -81,11 +83,12 @@ fn cloudflare_dns() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let intermediates = &[inter]; let builder = PathBuilder::new( + intermediates, + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(intermediates); + ); let time = UnixTime::since_unix_epoch(Duration::from_secs(1_663_495_771)); let ee = CertificateDer::from(ee); @@ -131,6 +134,8 @@ fn wpt() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let builder = PathBuilder::new( + &[], + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, @@ -148,6 +153,8 @@ fn ed25519() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let builder = PathBuilder::new( + &[], + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, @@ -167,11 +174,12 @@ fn critical_extensions() { let anchors = [anchor_from_trusted_cert(&root).unwrap()]; let intermediates = &[ca]; let builder = PathBuilder::new( + intermediates, + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(intermediates); + ); let time = UnixTime::since_unix_epoch(Duration::from_secs(1_670_779_098)); let ee = CertificateDer::from( @@ -215,6 +223,8 @@ fn read_ee_with_neg_serial() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let builder = PathBuilder::new( + &[], + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, @@ -380,11 +390,12 @@ fn cert_time_validity() { let anchors = [anchor_from_trusted_cert(&ca).unwrap()]; let builder = PathBuilder::new( + slice::from_ref(&inter), + None, &ExtendedKeyUsage::SERVER_AUTH, ALL_VERIFICATION_ALGS, &anchors, - ) - .with_intermediate_certs(slice::from_ref(&inter)); + ); let not_before = UnixTime::since_unix_epoch(Duration::from_secs(1_478_563_200)); let not_after = UnixTime::since_unix_epoch(Duration::from_secs(1_541_203_199)); diff --git a/tests/tls_server_certs.rs b/tests/tls_server_certs.rs index 3c04cca8..89aba249 100644 --- a/tests/tls_server_certs.rs +++ b/tests/tls_server_certs.rs @@ -37,6 +37,8 @@ fn check_cert( let ca_cert_der = CertificateDer::from(ca); let anchors = [anchor_from_trusted_cert(&ca_cert_der).unwrap()]; let builder = PathBuilder::new( + &[], + None, &ExtendedKeyUsage::SERVER_AUTH, rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, &anchors, diff --git a/tests/x509_limbo.rs b/tests/x509_limbo.rs index 1d3d7cec..9563b3e6 100644 --- a/tests/x509_limbo.rs +++ b/tests/x509_limbo.rs @@ -113,13 +113,6 @@ fn run_validation(tc: &Testcase) -> Result<(), String> { .map(|ic| cert_der_from_pem(ic)) .collect::>(); - let builder = PathBuilder::new( - &ExtendedKeyUsage::SERVER_AUTH, - rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, - &trust_anchors, - ) - .with_intermediate_certs(&intermediates); - let crls = tc .crls .iter() @@ -134,18 +127,20 @@ fn run_validation(tc: &Testcase) -> Result<(), String> { .collect::, _>>()?; let crls = crls.iter().collect::>(); - let builder = if !crls.is_empty() { - builder.with_revocation( + let builder = PathBuilder::new( + &intermediates, + (!crls.is_empty()).then(|| { RevocationOptionsBuilder::new(crls.as_slice()) .unwrap() .with_depth(RevocationCheckDepth::Chain) .with_status_policy(UnknownStatusPolicy::Deny) .with_expiration_policy(ExpirationPolicy::Enforce) - .build(), - ) - } else { - builder - }; + .build() + }), + &ExtendedKeyUsage::SERVER_AUTH, + rustls_aws_lc_rs::ALL_VERIFICATION_ALGS, + &trust_anchors, + ); let leaf_der = cert_der_from_pem(&tc.peer_certificate); let leaf =