diff --git a/rustls-platform-verifier/src/tests/mod.rs b/rustls-platform-verifier/src/tests/mod.rs index 134a20f..9f799d8 100644 --- a/rustls-platform-verifier/src/tests/mod.rs +++ b/rustls-platform-verifier/src/tests/mod.rs @@ -62,8 +62,8 @@ pub fn assert_cert_error_eq( /// we know the test certificates are valid. This must be updated if the mock certificates /// are regenerated. pub(crate) fn verification_time() -> pki_types::UnixTime { - // Sat, 3 January 2026 14:20:06 UTC - pki_types::UnixTime::since_unix_epoch(Duration::from_secs(1_767_450_006)) + // Sat, 31 January 2026 11:28:26 UTC + pki_types::UnixTime::since_unix_epoch(Duration::from_secs(1_769_858_906)) } fn test_provider() -> Arc { diff --git a/rustls-platform-verifier/src/tests/verification_mock/ca.go b/rustls-platform-verifier/src/tests/verification_mock/ca.go index ec3b4de..dbe2f8f 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/ca.go +++ b/rustls-platform-verifier/src/tests/verification_mock/ca.go @@ -69,7 +69,7 @@ func doIt() error { var err error = nil - root1_key, err := generateRoot("root1", now) + root1_key, err := generateRoot("root1", now, "", true, 365) if err != nil { return err } @@ -96,6 +96,21 @@ func doIt() error { } } + _, err = generateRoot("root2", now, "example.com", true, 365) + if err != nil { + return err + } + + _, err = generateRoot("root3", now, "example.com", false, 365) + if err != nil { + return err + } + + _, err = generateRoot("root4", now, "example.com", false, 1000) + if err != nil { + return err + } + return nil } @@ -210,23 +225,32 @@ func generateInt(intName string, serial int64, now time.Time, caKey crypto.Signe return intKey, nil } -func generateRoot(name string, now time.Time) (crypto.Signer, error) { +func generateRoot(name string, now time.Time, commonName string, IsCA bool, validDays int64) (crypto.Signer, error) { caKey, err := generateKey() if err != nil { return nil, err } + template := x509.Certificate{ SerialNumber: big.NewInt(1), Subject: pkix.Name{ Organization: []string{name}, }, NotBefore: now.Add(-OneDay), - NotAfter: now.Add(OneYear), - IsCA: true, + NotAfter: now.Add(time.Duration(validDays) * 24 * time.Hour), + IsCA: IsCA, KeyUsage: x509.KeyUsageCertSign, BasicConstraintsValid: true, } + if len(commonName) != 0 { + template.Subject.CommonName = commonName + template.KeyUsage = 0 + // See `generateEndEntity` for list of macOS requirements. + template.ExtKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth} + template.DNSNames = []string{commonName} + } + cert, err := x509.CreateCertificate(rand.Reader, &template, &template, caKey.Public(), caKey) if err != nil { return nil, err diff --git a/rustls-platform-verifier/src/tests/verification_mock/mod.rs b/rustls-platform-verifier/src/tests/verification_mock/mod.rs index 38a2686..c871950 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_mock/mod.rs @@ -28,7 +28,7 @@ use std::net::{Ipv4Addr, Ipv6Addr}; use std::sync::Arc; use rustls::client::danger::ServerCertVerifier; -use rustls::pki_types; +use rustls::pki_types::{self, CertificateDer}; #[cfg(not(any(target_vendor = "apple", windows)))] use rustls::pki_types::{DnsName, ServerName}; use rustls::{CertificateError, Error as TlsError, OtherError}; @@ -80,13 +80,25 @@ macro_rules! no_error { }; } -const ROOT1: pki_types::CertificateDer<'static> = - pki_types::CertificateDer::from_slice(include_bytes!("root1.crt")); +const ROOT1: CertificateDer = CertificateDer::from_slice(include_bytes!("root1.crt")); const ROOT1_INT1: &[u8] = include_bytes!("root1-int1.crt"); const ROOT1_INT1_EXAMPLE_COM_GOOD: &[u8] = include_bytes!("root1-int1-ee_example.com-good.crt"); const ROOT1_INT1_LOCALHOST_IPV4_GOOD: &[u8] = include_bytes!("root1-int1-ee_127.0.0.1-good.crt"); const ROOT1_INT1_LOCALHOST_IPV6_GOOD: &[u8] = include_bytes!("root1-int1-ee_1-good.crt"); +// `ffi-testing` is currently only used for Android, which doesn't support extra roots yet. +#[cfg_attr(feature = "ffi-testing", allow(unused))] +#[cfg(not(target_os = "android"))] +const ROOT2: CertificateDer = CertificateDer::from_slice(include_bytes!("root2.crt")); + +#[cfg_attr(feature = "ffi-testing", allow(unused))] +#[cfg(not(target_os = "android"))] +const ROOT3: CertificateDer = CertificateDer::from_slice(include_bytes!("root3.crt")); + +#[cfg_attr(feature = "ffi-testing", allow(unused))] +#[cfg(not(target_os = "android"))] +const ROOT4: CertificateDer = CertificateDer::from_slice(include_bytes!("root4.crt")); + const EXAMPLE_COM: &str = "example.com"; const LOCALHOST_IPV4: &str = "127.0.0.1"; const LOCALHOST_IPV6: &str = "::1"; @@ -111,8 +123,8 @@ pub(super) fn verification_without_mock_root() { let verifier = Verifier::new(crypto_provider).unwrap(); let server_name = pki_types::ServerName::try_from(EXAMPLE_COM).unwrap(); - let end_entity = pki_types::CertificateDer::from(ROOT1_INT1_EXAMPLE_COM_GOOD); - let intermediates = [pki_types::CertificateDer::from(ROOT1_INT1)]; + let end_entity = CertificateDer::from(ROOT1_INT1_EXAMPLE_COM_GOOD); + let intermediates = [CertificateDer::from(ROOT1_INT1)]; // Fails because the server cert has no trust root in Windows, and can't since it uses a self-signed CA. // Similarly on UNIX platforms using the Webpki verifier, it can't fetch extra certificates through @@ -139,6 +151,102 @@ fn test_verification_without_mock_root() { verification_without_mock_root() } +#[cfg(not(target_os = "android"))] +#[test] +fn test_selfsigned_cert_with_extra_roots() { + let crypto_provider = test_provider(); + + let selfsigned = ROOT2; + let selfsigned_as_leaf = ROOT3; + let selfsigned_as_leaf_long_validity = ROOT4; + let roots = vec![ + selfsigned.clone(), + selfsigned_as_leaf.clone(), + selfsigned_as_leaf_long_validity.clone(), + ]; + let server_name = pki_types::ServerName::try_from(EXAMPLE_COM).unwrap(); + + let verifier = Verifier::new_with_extra_roots(roots, crypto_provider).unwrap(); + + let result = + verifier.verify_server_cert(&selfsigned, &[], &server_name, &[], verification_time()); + + #[cfg(target_vendor = "apple")] + assert!( + result.is_ok(), + "failed to validate self-signed ca certificate" + ); + + #[cfg(not(target_vendor = "apple"))] + assert!( + result.is_err(), + "self-signed ca certificate is accepted unexpectly" + ); + + let result = verifier.verify_server_cert( + &selfsigned_as_leaf, + &[], + &server_name, + &[], + verification_time(), + ); + + #[cfg(not(target_os = "windows"))] + assert!( + result.is_ok(), + "failed to validate self-signed leaf certificate" + ); + + #[cfg(target_os = "windows")] + assert!( + result.is_err(), + "self-signed leaf certificate is accepted unexpectly" + ); + + let result = verifier.verify_server_cert( + &selfsigned_as_leaf_long_validity, + &[], + &server_name, + &[], + verification_time(), + ); + + #[cfg(any(target_vendor = "apple", target_os = "windows"))] + assert!( + result.is_err(), + "self-signed leaf certificate with long validity period is accepted unexpectly" + ); + + #[cfg(not(any(target_vendor = "apple", target_os = "windows")))] + assert!( + result.is_ok(), + "failed to validate self-signed leaf certificate with long validity period" + ); +} + +#[cfg(not(target_os = "android"))] +#[test] +fn test_chain_signed_with_extra_roots() { + let crypto_provider = test_provider(); + + let server_name = pki_types::ServerName::try_from(EXAMPLE_COM).unwrap(); + let end_entity = CertificateDer::from(ROOT1_INT1_EXAMPLE_COM_GOOD); + let intermediates = [CertificateDer::from(ROOT1_INT1)]; + let roots = vec![ROOT1]; + + let verifier = Verifier::new_with_extra_roots(roots, crypto_provider).unwrap(); + + verifier + .verify_server_cert( + &end_entity, + &intermediates, + &server_name, + &[], + verification_time(), + ) + .expect("failed to validate extra root-only certificate chain"); +} + // Note: Android does not currently support IP address hosts, so these tests are disabled for // Android. // Verifies that our test trust anchor(s) are not trusted when `Verifier::new()` @@ -349,10 +457,10 @@ fn test_with_mock_root( let mut chain = test_case .chain .iter() - .map(|bytes| pki_types::CertificateDer::from(*bytes)); + .map(|bytes| CertificateDer::from(*bytes)); let end_entity = chain.next().unwrap(); - let intermediates: Vec> = chain.collect(); + let intermediates: Vec> = chain.collect(); let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap(); diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt index 0968a95..70c2370 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp index 66e49a7..c2dd9ab 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-good.ocsp differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt index b4f4012..dcbc9e9 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp index 3b4aa06..7f19fdb 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-revoked.ocsp differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-wrong_eku.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-wrong_eku.crt index b547d64..ebd47da 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-wrong_eku.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_1-wrong_eku.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt index 70b398f..bf60463 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp index e920cd3..4ab3ab3 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-good.ocsp differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.crt index 156afaa..246d2cc 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp index 6dff38e..b9ca0b2 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-revoked.ocsp differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt index 17bdbc0..709f3cf 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_127.0.0.1-wrong_eku.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.crt index 0c2364f..6d657c5 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp index 028baa2..21aa863 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-good.ocsp differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt index bf6c751..6e788e9 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp index bbd64a3..daaf181 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-revoked.ocsp differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt index e311b4f..c621212 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1-ee_example.com-wrong_eku.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt b/rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt index 7c059e5..57afcce 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1-int1.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root1.crt b/rustls-platform-verifier/src/tests/verification_mock/root1.crt index 0292e4c..fbe12b6 100644 Binary files a/rustls-platform-verifier/src/tests/verification_mock/root1.crt and b/rustls-platform-verifier/src/tests/verification_mock/root1.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root2.crt b/rustls-platform-verifier/src/tests/verification_mock/root2.crt new file mode 100644 index 0000000..72579d6 Binary files /dev/null and b/rustls-platform-verifier/src/tests/verification_mock/root2.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root3.crt b/rustls-platform-verifier/src/tests/verification_mock/root3.crt new file mode 100644 index 0000000..0c619cd Binary files /dev/null and b/rustls-platform-verifier/src/tests/verification_mock/root3.crt differ diff --git a/rustls-platform-verifier/src/tests/verification_mock/root4.crt b/rustls-platform-verifier/src/tests/verification_mock/root4.crt new file mode 100644 index 0000000..0e3f11f Binary files /dev/null and b/rustls-platform-verifier/src/tests/verification_mock/root4.crt differ