diff --git a/Cargo.lock b/Cargo.lock index a604aa36..2399eb9c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -36,9 +36,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "bytes" @@ -48,9 +48,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.61" +version = "1.2.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" dependencies = [ "find-msvc-tools", "shlex", @@ -172,15 +172,15 @@ checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "log" -version = "0.4.29" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" [[package]] name = "memchr" -version = "2.8.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" [[package]] name = "once_cell" @@ -266,12 +266,10 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +version = "0.24.0-dev.1" +source = "git+https://github.com/rustls/rustls.git?branch=main#6868ee84dd62b4292b760cb7ba487cb37f86a95f" dependencies = [ "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -280,9 +278,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -292,16 +290,16 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-platform-verifier" -version = "0.7.0" +version = "0.8.0" dependencies = [ "android_logger", "base64", @@ -313,24 +311,44 @@ dependencies = [ "rustls", "rustls-native-certs", "rustls-platform-verifier-android", + "rustls-ring", + "rustls-util", "rustls-webpki", "security-framework", "security-framework-sys", "webpki-root-certs", - "windows-sys 0.61.2", + "windows-sys 0.52.0", ] [[package]] name = "rustls-platform-verifier-android" version = "0.1.1" +[[package]] +name = "rustls-ring" +version = "0.1.0-dev.1" +source = "git+https://github.com/rustls/rustls.git?branch=main#6868ee84dd62b4292b760cb7ba487cb37f86a95f" +dependencies = [ + "ring", + "rustls", + "rustls-pki-types", + "subtle", +] + +[[package]] +name = "rustls-util" +version = "0.1.0-dev.1" +source = "git+https://github.com/rustls/rustls.git?branch=main#6868ee84dd62b4292b760cb7ba487cb37f86a95f" +dependencies = [ + "rustls", +] + [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.104.0-alpha.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "bea702cca24d344fc70973022bf7eb920c224e318466eb49784272337dd24b1a" dependencies = [ - "ring", "rustls-pki-types", "untrusted", ] @@ -384,9 +402,9 @@ checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "shlex" -version = "1.3.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" [[package]] name = "simd_cesu8" diff --git a/README.md b/README.md index 4e2ef919..9c643877 100644 --- a/README.md +++ b/README.md @@ -33,14 +33,14 @@ CRL distribution point to fetch revocation information from, revocation checking [^2]: The fall-back webpki verifier configured for Linux/WASM does not support providing CRLs for revocation checking. If you require revocation checking on these platforms, prefer constructing your own -`WebPkiServerVerifier`, providing necessary CRLs. See the Rustls [`ServerCertVerifierBuilder`] docs for more +`WebPkiServerVerifier`, providing necessary CRLs. See the Rustls [`ServerVerifierBuilder`] docs for more information. [^3]: On Linux the [rustls-native-certs] and [openssl-probe] crates are used to try and discover the system CA bundle. Users may wish to augment these certificates with [webpki-roots] using [`Verifier::new_with_extra_roots`] in case a system CA bundle is unavailable. -[`ServerCertVerifierBuilder`]: https://docs.rs/rustls/latest/rustls/client/struct.ServerCertVerifierBuilder.html +[`ServerVerifierBuilder`]: https://docs.rs/rustls/latest/rustls/client/struct.ServerVerifierBuilder.html [`Verifier::new_with_extra_roots`]: https://docs.rs/rustls-platform-verifier/latest/rustls_platform_verifier/struct.Verifier.html#method.new_with_extra_roots [rustls-native-certs]: https://github.com/rustls/rustls-native-certs [openssl-probe]: https://github.com/alexcrichton/openssl-probe @@ -88,26 +88,17 @@ On most platforms, no setup should be required beyond adding the dependency via rustls-platform-verifier = "0.5" ``` -To get a rustls `ClientConfig` configured to use the platform verifier use: - -```rust -use rustls::ClientConfig; -use rustls_platform_verifier::ConfigVerifierExt; -let config = ClientConfig::with_platform_verifier(); -``` - -This crate will use the [rustls process-default crypto provider](https://docs.rs/rustls/latest/rustls/crypto/struct.CryptoProvider.html#using-the-per-process-default-cryptoprovider). To construct a `ClientConfig` with a different `CryptoProvider`, use: +To get a rustls `ClientConfig` configured to use the platform verifier, use: ```rust use rustls::ClientConfig; use rustls_platform_verifier::BuilderVerifierExt; -let arc_crypto_provider = std::sync::Arc::new(rustls::crypto::ring::default_provider()); -let config = ClientConfig::builder_with_provider(arc_crypto_provider) - .with_safe_default_protocol_versions() - .unwrap() +let provider = std::sync::Arc::new(rustls_ring::DEFAULT_PROVIDER.clone()); +let config = ClientConfig::builder(provider) .with_platform_verifier() .unwrap() - .with_no_client_auth(); + .with_no_client_auth() + .unwrap(); ``` ### Android diff --git a/rustls-platform-verifier/Cargo.toml b/rustls-platform-verifier/Cargo.toml index e4030665..48367438 100644 --- a/rustls-platform-verifier/Cargo.toml +++ b/rustls-platform-verifier/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rustls-platform-verifier" -version = "0.7.0" +version = "0.8.0" description = "rustls-platform-verifier supports verifying TLS certificates in rustls with the operating system verifier" keywords = ["tls", "certificate", "verification", "os", "native"] repository = "https://github.com/rustls/rustls-platform-verifier" @@ -15,7 +15,7 @@ name = "rustls_platform_verifier" # Enables a C interface to use for testing where `cargo` can't be used. # This feature is not stable, nor is the interface exported when it is enabled. # Do not rely on this or use it in production. -ffi-testing = ["android_logger", "rustls/ring"] +ffi-testing = ["android_logger", "dep:rustls-ring"] # Enables APIs that expose lower-level verifier types for debugging purposes. dbg = [] # Enables `log::debug` base64-encoded logging of all end-entity certificates processed @@ -25,7 +25,8 @@ cert-logging = ["base64"] docsrs = ["jni", "once_cell"] [dependencies] -rustls = { version = "0.23.27", default-features = false, features = ["std"] } +rustls = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.24.0-dev.1", default-features = false, features = ["webpki"] } +rustls-ring = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.1.0-dev.1", default-features = false, features = ["std"], optional = true } log = { version = "0.4" } base64 = { version = "0.22", optional = true } # Only used when the `cert-logging` feature is enabled. jni = { version = "0.22.4", default-features = false, optional = true } # Only used during doc generation @@ -33,17 +34,17 @@ once_cell = { version = "1.9", optional = true } # Only used during doc generati [target.'cfg(all(unix, not(target_os = "android"), not(target_vendor = "apple"), not(target_arch = "wasm32")))'.dependencies] rustls-native-certs = "0.8" -webpki = { package = "rustls-webpki", version = "0.103", default-features = false } +webpki = { package = "rustls-webpki", version = "0.104.0-alpha.7", default-features = false } [target.'cfg(target_os = "android")'.dependencies] once_cell = "1.9" rustls-platform-verifier-android = { path = "../android-release-support", version = "0.1.0" } jni = { version = "0.22", default-features = false } -webpki = { package = "rustls-webpki", version = "0.103", default-features = false } +webpki = { package = "rustls-webpki", version = "0.104.0-alpha.7", default-features = false } android_logger = { version = "0.15", optional = true } # Only used during testing. [target.'cfg(target_arch = "wasm32")'.dependencies] -webpki = { package = "rustls-webpki", version = "0.103", default-features = false } +webpki = { package = "rustls-webpki", version = "0.104.0-alpha.7", default-features = false } webpki-root-certs = "1" [target.'cfg(any(target_vendor = "apple"))'.dependencies] @@ -56,7 +57,9 @@ security-framework-sys = "2.15" windows-sys = { version = ">=0.52.0, <0.62.0", default-features = false, features = ["Win32_Foundation", "Win32_Security_Cryptography"] } [dev-dependencies] -rustls = { version = "0.23", default-features = false, features = ["ring"] } +rustls = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.24.0-dev.1", default-features = false, features = ["webpki"] } +rustls-ring = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.1.0-dev.1", default-features = false, features = ["std"] } +rustls-util = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.1.0-dev.1", default-features = false } webpki-root-certs = "1" [package.metadata.docs.rs] diff --git a/rustls-platform-verifier/examples/update-certs.rs b/rustls-platform-verifier/examples/update-certs.rs index 1970f97d..d152aa08 100644 --- a/rustls-platform-verifier/examples/update-certs.rs +++ b/rustls-platform-verifier/examples/update-certs.rs @@ -1,6 +1,8 @@ -use std::{fs, io::Write, net::TcpStream, sync::Arc}; +use std::{fs, io::Write, iter, net::TcpStream, sync::Arc}; -use rustls::{pki_types::ServerName, ClientConfig, ClientConnection, RootCertStore, Stream}; +use rustls::crypto::Identity; +use rustls::{pki_types::ServerName, ClientConfig, RootCertStore, VecInput}; +use rustls_util::Stream; use webpki_root_certs::TLS_SERVER_ROOT_CERTS; fn main() -> Result<(), Box> { @@ -8,28 +10,43 @@ fn main() -> Result<(), Box> { let (_, ignored) = roots.add_parsable_certificates(TLS_SERVER_ROOT_CERTS.iter().cloned()); assert_eq!(ignored, 0, "{ignored} root certificates were ignored"); let config = Arc::new( - ClientConfig::builder() + ClientConfig::builder(Arc::new(rustls_ring::DEFAULT_PROVIDER.clone())) .with_root_certificates(roots) - .with_no_client_auth(), + .with_no_client_auth()?, ); for &host in HOSTS { - let server_name = ServerName::try_from(host)?; - let mut conn = ClientConnection::new(config.clone(), server_name)?; + let server_name = ServerName::try_from(host.to_owned())?; + let mut output = Vec::new(); + let mut conn = config.connect(server_name).build(&mut output)?; let mut sock = TcpStream::connect((host, 443))?; - let mut stream = Stream::new(&mut conn, &mut sock); + let mut input = VecInput::default(); + let mut received_plaintext = Vec::new(); + let mut stream = Stream::new( + &mut input, + &mut received_plaintext, + &mut output, + &mut conn, + &mut sock, + ); eprintln!("connecting to {host}..."); - if let Err(err) = stream.write_all(b"GET / HTTP/1.1\r\n\r\n") { - eprintln!("failed to write to {host}: {err}"); - } + stream.write_all(format!("GET / HTTP/1.1\r\nHost: {host}\r\n\r\n").as_bytes())?; + stream.flush()?; - let Some(certs) = conn.peer_certificates() else { + let Some(Identity::X509(certs)) = stream + .conn + .peer_identity() + .map(|identity| identity.identity()) + else { eprintln!("no certificates received for {host}"); continue; }; - for (i, der) in certs.iter().enumerate() { + for (i, der) in iter::once(&certs.end_entity) + .chain(certs.intermediates.iter()) + .enumerate() + { let host_name = host.replace('.', "_"); let fname = format!( "{}/src/tests/verification_real_world/{host_name}_valid_{}.crt", diff --git a/rustls-platform-verifier/src/lib.rs b/rustls-platform-verifier/src/lib.rs index 3c1da291..8302f463 100644 --- a/rustls-platform-verifier/src/lib.rs +++ b/rustls-platform-verifier/src/lib.rs @@ -2,13 +2,12 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] -use std::sync::Arc; - #[cfg(feature = "dbg")] use rustls::crypto::CryptoProvider; #[cfg(feature = "dbg")] use rustls::pki_types::CertificateDer; use rustls::{client::WantsClientCert, ClientConfig, ConfigBuilder, WantsVerifier}; +use std::sync::Arc; mod verification; pub use verification::Verifier; @@ -39,7 +38,7 @@ pub use tests::ffi::*; pub fn verifier_for_dbg( root: CertificateDer<'static>, crypto_provider: Arc, -) -> Arc { +) -> Arc { Arc::new(Verifier::new_with_fake_root(root, crypto_provider)) } @@ -50,10 +49,12 @@ pub trait BuilderVerifierExt { /// ```rust /// use rustls::ClientConfig; /// use rustls_platform_verifier::BuilderVerifierExt; - /// let config = ClientConfig::builder() + /// let provider = std::sync::Arc::new(rustls_ring::DEFAULT_PROVIDER.clone()); + /// let config = ClientConfig::builder(provider) /// .with_platform_verifier() /// .unwrap() - /// .with_no_client_auth(); + /// .with_no_client_auth() + /// .unwrap(); /// ``` fn with_platform_verifier( self, @@ -64,7 +65,7 @@ impl BuilderVerifierExt for ConfigBuilder { fn with_platform_verifier( self, ) -> Result, rustls::Error> { - let verifier = Verifier::new(self.crypto_provider().clone())?; + let verifier = Verifier::new(self.provider().clone())?; Ok(self .dangerous() .with_custom_certificate_verifier(Arc::new(verifier))) @@ -73,20 +74,27 @@ impl BuilderVerifierExt for ConfigBuilder { /// Extension trait to help build a [`ClientConfig`] with the platform verifier. pub trait ConfigVerifierExt { - /// Build a [`ClientConfig`] with the platform verifier and the default `CryptoProvider`. + /// Configures an existing [`ClientConfig`] with the platform verifier. /// /// ```rust - /// use rustls::ClientConfig; + /// use rustls::{ClientConfig, RootCertStore}; /// use rustls_platform_verifier::ConfigVerifierExt; - /// let config = ClientConfig::with_platform_verifier(); + /// let provider = std::sync::Arc::new(rustls_ring::DEFAULT_PROVIDER.clone()); + /// let config = ClientConfig::builder(provider) + /// .with_root_certificates(RootCertStore::empty()) + /// .with_no_client_auth() + /// .unwrap() + /// .with_platform_verifier() + /// .unwrap(); /// ``` - fn with_platform_verifier() -> Result; + fn with_platform_verifier(self) -> Result; } impl ConfigVerifierExt for ClientConfig { - fn with_platform_verifier() -> Result { - Ok(ClientConfig::builder() - .with_platform_verifier()? - .with_no_client_auth()) + fn with_platform_verifier(mut self) -> Result { + let verifier = Verifier::new(self.provider().clone())?; + self.dangerous() + .set_certificate_verifier(Arc::new(verifier)); + Ok(self) } } diff --git a/rustls-platform-verifier/src/tests/mod.rs b/rustls-platform-verifier/src/tests/mod.rs index ba2aa6c1..b9e68dbc 100644 --- a/rustls-platform-verifier/src/tests/mod.rs +++ b/rustls-platform-verifier/src/tests/mod.rs @@ -8,9 +8,10 @@ mod verification_real_world; mod verification_mock; +use rustls::crypto::CryptoProvider; +use rustls::error::CertificateError; use rustls::{ - crypto::CryptoProvider, - pki_types, CertificateError, + pki_types, Error::{self as TlsError, InvalidCertificate}, }; @@ -47,8 +48,8 @@ pub fn assert_cert_error_eq( if let Err(InvalidCertificate(CertificateError::Other(err))) = &expected { let expected_err = expected_err.expect("error not provided for `Other` case handling"); let err: &E = err - .0 - .downcast_ref() + .source() + .and_then(|source| source.downcast_ref()) .expect("incorrect `Other` inner error kind"); assert_eq!(err, expected_err); } else { @@ -62,10 +63,10 @@ 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 { - // Wed, 12 Aug 2026 10:23 UTC - pki_types::UnixTime::since_unix_epoch(Duration::from_secs(1_786_530_173)) + // Thu, 20 Aug 2026 16:25 UTC + pki_types::UnixTime::since_unix_epoch(Duration::from_secs(1_787_243_100)) } fn test_provider() -> Arc { - Arc::new(rustls::crypto::ring::default_provider()) + Arc::new(rustls_ring::DEFAULT_PROVIDER.clone()) } diff --git a/rustls-platform-verifier/src/tests/verification_mock/ca.go b/rustls-platform-verifier/src/tests/verification_mock/ca.go index ec3b4de1..2043f1f7 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/ca.go +++ b/rustls-platform-verifier/src/tests/verification_mock/ca.go @@ -191,7 +191,7 @@ func generateInt(intName string, serial int64, now time.Time, caKey crypto.Signe Organization: []string{intName}, }, NotBefore: now.Add(-OneDay), - NotAfter: now.Add(OneYear), + NotAfter: now.Add(2 * OneYear), IsCA: true, KeyUsage: x509.KeyUsageCertSign, BasicConstraintsValid: true, @@ -221,7 +221,7 @@ func generateRoot(name string, now time.Time) (crypto.Signer, error) { Organization: []string{name}, }, NotBefore: now.Add(-OneDay), - NotAfter: now.Add(OneYear), + NotAfter: now.Add(2 * OneYear), IsCA: true, KeyUsage: x509.KeyUsageCertSign, BasicConstraintsValid: true, @@ -257,7 +257,7 @@ func generateOCSPResponse(name string, status int, now time.Time, caKey crypto.S Status: status, SerialNumber: eeCert.SerialNumber, ThisUpdate: thisUpdate, - NextUpdate: thisUpdate.Add(1 * OneYear), + NextUpdate: thisUpdate.Add(2 * OneYear), } if status == ocsp.Revoked { diff --git a/rustls-platform-verifier/src/tests/verification_mock/mod.rs b/rustls-platform-verifier/src/tests/verification_mock/mod.rs index 98a22e96..8aaeed95 100644 --- a/rustls-platform-verifier/src/tests/verification_mock/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_mock/mod.rs @@ -21,17 +21,18 @@ not(target_os = "visionos") ))] +use rustls::client::danger::{ServerIdentity, ServerVerifier}; +use rustls::crypto::Identity; +use rustls::error::{CertificateError, OtherError}; +use rustls::pki_types; +#[cfg(not(any(target_vendor = "apple", windows)))] +use rustls::pki_types::{DnsName, ServerName}; +use rustls::Error as TlsError; use std::convert::TryFrom; +use std::iter; use std::net::IpAddr; #[cfg(not(any(target_vendor = "apple", windows)))] use std::net::{Ipv4Addr, Ipv6Addr}; -use std::sync::Arc; - -use rustls::client::danger::ServerCertVerifier; -use rustls::pki_types; -#[cfg(not(any(target_vendor = "apple", windows)))] -use rustls::pki_types::{DnsName, ServerName}; -use rustls::{CertificateError, Error as TlsError, OtherError}; use super::TestCase; use crate::tests::{assert_cert_error_eq, test_provider, verification_time}; @@ -118,13 +119,11 @@ pub(super) fn verification_without_mock_root() { // Similarly on UNIX platforms using the Webpki verifier, it can't fetch extra certificates through // AIA chasing or other mechanisms, and so we know this test will correctly verify an unknown // root in a chain fails validation. - let result = verifier.verify_server_cert( - &end_entity, - &intermediates, - &server_name, - &[], - verification_time(), - ); + let identity = + Identity::from_cert_chain(iter::once(end_entity).chain(intermediates).collect()).unwrap(); + let identity = ServerIdentity::new(&identity, &server_name, verification_time()); + + let result = verifier.verify_identity(&identity); assert_eq!( result.map(|_| ()), @@ -321,7 +320,7 @@ mock_root_test_cases! { stapled_ocsp: None, verification_time: verification_time(), expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::from(EkuError))))), + CertificateError::Other(OtherError::new(EkuError)))), other_error: Some(EkuError), }, wrong_eku_ipv4 [ any(windows, unix) ] => TestCase { @@ -330,7 +329,7 @@ mock_root_test_cases! { stapled_ocsp: None, verification_time: verification_time(), expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::from(EkuError))))), + CertificateError::Other(OtherError::new(EkuError)))), other_error: Some(EkuError), }, wrong_eku_ipv6 [ any(windows, unix) ] => TestCase { @@ -339,7 +338,7 @@ mock_root_test_cases! { stapled_ocsp: None, verification_time: verification_time(), expected_result: Err(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::from(EkuError))))), + CertificateError::Other(OtherError::new(EkuError)))), other_error: Some(EkuError), }, } @@ -372,13 +371,20 @@ fn test_with_mock_root( assert!(matches!(server_name, pki_types::ServerName::DnsName(_))); } - let result = verifier.verify_server_cert( - &end_entity, - &intermediates, + let presented_identity = + Identity::from_cert_chain(iter::once(end_entity).chain(intermediates).collect()).unwrap(); + let mut identity = ServerIdentity::new( + &presented_identity, &server_name, - test_case.stapled_ocsp.unwrap_or(&[]), test_case.verification_time, ); + identity.ocsp_response = test_case.stapled_ocsp.unwrap_or(&[]); + + let result = verifier.verify_identity(&identity); + + if let Ok(verified) = &result { + assert_eq!(verified.identity(), &presented_identity); + } assert_cert_error_eq( &result.map(|_| ()), 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 0968a956..0edccb18 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 66e49a7e..e3a1d8cd 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 b4f4012e..16697cf4 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 3b4aa064..7ac8cf86 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 b547d64d..0d357df2 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 70b398f5..0be67da8 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 e920cd38..396feabc 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 156afaa2..bfe83341 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 6dff38ec..3d6ef39c 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 17bdbc05..c770f2f1 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 0c2364f4..93279da5 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 028baa2a..35e7f321 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 bf6c751a..739b1677 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 bbd64a37..1a1650d9 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 e311b4fa..22bfbccd 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 7c059e53..f56d7555 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 0292e4c6..82f41279 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_real_world/mod.rs b/rustls-platform-verifier/src/tests/verification_real_world/mod.rs index bf8d7dab..2350d775 100644 --- a/rustls-platform-verifier/src/tests/verification_real_world/mod.rs +++ b/rustls-platform-verifier/src/tests/verification_real_world/mod.rs @@ -35,11 +35,15 @@ //! Thus we don't expect these tests to be flaky w.r.t. that, except for //! potentially poor performance. -use rustls::client::danger::ServerCertVerifier; +use std::iter; + +use rustls::client::danger::{ServerIdentity, ServerVerifier}; +use rustls::crypto::Identity; +use rustls::error::CertificateError; use rustls::pki_types; #[cfg(not(any(target_vendor = "apple", windows)))] use rustls::pki_types::{DnsName, ServerName}; -use rustls::{CertificateError, Error as TlsError}; +use rustls::Error as TlsError; use super::TestCase; use crate::tests::{assert_cert_error_eq, test_provider, verification_time}; @@ -166,17 +170,13 @@ fn real_world_test(test_case: &TestCase) { let server_name = pki_types::ServerName::try_from(test_case.reference_id).unwrap(); - let stapled_ocsp = test_case.stapled_ocsp.unwrap_or(&[]); + let identity = + Identity::from_cert_chain(iter::once(end_entity_cert).chain(intermediates).collect()) + .unwrap(); + let mut identity = ServerIdentity::new(&identity, &server_name, test_case.verification_time); + identity.ocsp_response = test_case.stapled_ocsp.unwrap_or(&[]); - let result = verifier - .verify_server_cert( - &end_entity_cert, - &intermediates, - &server_name, - stapled_ocsp, - test_case.verification_time, - ) - .map(|_| ()); + let result = verifier.verify_identity(&identity).map(|_| ()); assert_cert_error_eq( &result.map(|_| ()), diff --git a/rustls-platform-verifier/src/verification/android.rs b/rustls-platform-verifier/src/verification/android.rs index 3916a5fa..b38c5af2 100644 --- a/rustls-platform-verifier/src/verification/android.rs +++ b/rustls-platform-verifier/src/verification/android.rs @@ -1,17 +1,22 @@ +use core::hash::Hasher; use jni::{ jni_sig, jni_str, objects::{JByteArray, JObject, JObjectArray, JString, JValue}, signature::MethodSignature, Env, }; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}; +use rustls::client::danger::{ + HandshakeSignatureValid, ServerIdentity, ServerVerifier, SignatureVerificationInput, +}; +use rustls::crypto::{ + verify_tls12_signature, verify_tls13_signature, CertificateIdentity, CryptoProvider, Identity, + SignatureScheme, VerifiedIdentity, +}; +use rustls::error::{CertificateError, OtherError}; use rustls::pki_types; +use rustls::Error as TlsError; use rustls::Error::InvalidCertificate; -use rustls::{ - CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, -}; -use std::sync::Arc; +use std::{iter, sync::Arc}; use super::{log_server_cert, ALLOWED_EKUS}; use crate::android::{with_context, CachedClass}; @@ -89,21 +94,18 @@ impl Verifier { fn verify_certificate( &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: Option<&[u8]>, - now: pki_types::UnixTime, + certificates: &CertificateIdentity<'_>, + identity: &ServerIdentity<'_, '_>, ) -> Result<(), TlsError> { - let certificate_chain = std::iter::once(end_entity) - .chain(intermediates) + let certificate_chain = iter::once(&certificates.end_entity) + .chain(&certificates.intermediates) .map(|cert| cert.as_ref()) .enumerate(); // Convert the unix timestamp into milliseconds, expressed as // an i64 to later be converted into a Java Long used for a Date // constructor. - let now: i64 = (now.as_secs() * 1000) + let now: i64 = (identity.now.as_secs() * 1000) .try_into() .map_err(|_| TlsError::FailedToGetCurrentTime)?; @@ -113,7 +115,7 @@ impl Verifier { let cert_list = { let array = JObjectArray::::new( cx.env, - intermediates.len() + 1, + certificates.intermediates.len() + 1, &JByteArray::null(), )?; @@ -139,9 +141,10 @@ impl Verifier { array }; - let ocsp_response = match ocsp_response { - Some(b) => cx.env.byte_array_from_slice(b)?, - None => JByteArray::null(), + let ocsp_response = if identity.ocsp_response.is_empty() { + JByteArray::null() + } else { + cx.env.byte_array_from_slice(identity.ocsp_response)? }; #[cfg(any(test, feature = "ffi-testing"))] @@ -172,7 +175,7 @@ impl Verifier { ) -> org.rustls.platformverifier.VerificationResult ); - let server_name = server_name.to_str(); + let server_name = identity.server_name.to_str(); // Android's verifier doesn't require this but trim trailing `.` labels for consistency across platforms. let server_name = server_name.strip_suffix('.').unwrap_or(&server_name); @@ -210,8 +213,8 @@ impl Verifier { VerifierStatus::Ok => { // If everything else was OK, check the hostname. rustls::client::verify_server_name( - &rustls::server::ParsedCertificate::try_from(end_entity)?, - server_name, + &rustls::server::ParsedCertificate::try_from(&certificates.end_entity)?, + identity.server_name, ) } VerifierStatus::Unavailable => Err(TlsError::General(String::from( @@ -230,7 +233,7 @@ impl Verifier { Err(InvalidCertificate(CertificateError::BadEncoding)) } VerifierStatus::InvalidExtension => Err(InvalidCertificate( - CertificateError::Other(OtherError(std::sync::Arc::new(super::EkuError))), + CertificateError::Other(OtherError::new(super::EkuError)), )), } } @@ -276,25 +279,23 @@ fn extract_result_info(env: &mut Env<'_>, result: JObject<'_>) -> (VerifierStatu } #[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( +impl ServerVerifier for Verifier { + fn verify_identity<'a>( &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - let ocsp_data = if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None + identity: &ServerIdentity<'a, '_>, + ) -> Result, TlsError> { + let Identity::X509(certificates) = identity.identity else { + return Err(InvalidCertificate(CertificateError::Other( + OtherError::new(std::io::Error::other( + "platform verifier only supports X.509 certificates", + )), + ))); }; - match self.verify_certificate(end_entity, intermediates, server_name, ocsp_data, now) { - Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), + log_server_cert(&certificates.end_entity); + + match self.verify_certificate(certificates, identity) { + Ok(()) => Ok(VerifiedIdentity::assertion(identity.identity.clone())), Err(e) => { // This error only tells us what the system errored with, so it doesn't leak anything // sensitive. @@ -306,28 +307,20 @@ impl ServerCertVerifier for Verifier { fn verify_tls12_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { verify_tls12_signature( - message, - cert, - dss, + input, &self.crypto_provider.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { verify_tls13_signature( - message, - cert, - dss, + input, &self.crypto_provider.signature_verification_algorithms, ) } @@ -337,4 +330,14 @@ impl ServerCertVerifier for Verifier { .signature_verification_algorithms .supported_schemes() } + + fn request_ocsp_response(&self) -> bool { + true + } + + fn hash_config(&self, h: &mut dyn Hasher) { + h.write(b"rustls-platform-verifier-android"); + #[cfg(any(test, feature = "ffi-testing"))] + h.write_u8(u8::from(self.test_only_root_ca_override.is_some())); + } } diff --git a/rustls-platform-verifier/src/verification/apple.rs b/rustls-platform-verifier/src/verification/apple.rs index b4e34e7d..06f9ae66 100644 --- a/rustls-platform-verifier/src/verification/apple.rs +++ b/rustls-platform-verifier/src/verification/apple.rs @@ -1,13 +1,18 @@ use std::sync::Arc; +use core::hash::Hasher; use core_foundation::date::CFDate; use core_foundation_sys::date::kCFAbsoluteTimeIntervalSince1970; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}; -use rustls::pki_types; -use rustls::{ - CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, SignatureScheme, +use rustls::client::danger::{ + HandshakeSignatureValid, ServerIdentity, ServerVerifier, SignatureVerificationInput, +}; +use rustls::crypto::{ + verify_tls12_signature, verify_tls13_signature, CertificateIdentity, CryptoProvider, Identity, + SignatureScheme, VerifiedIdentity, }; +use rustls::error::{CertificateError, OtherError}; +use rustls::pki_types; +use rustls::Error as TlsError; use security_framework::{ certificate::SecCertificate, policy::SecPolicy, secure_transport::SslProtocolSide, trust::SecTrust, @@ -104,20 +109,29 @@ impl Verifier { fn verify_certificate( &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &str, - ocsp_response: Option<&[u8]>, - now: pki_types::UnixTime, + certificates: &CertificateIdentity<'_>, + identity: &ServerIdentity<'_, '_>, ) -> Result<(), TlsError> { - let certificates: Vec = std::iter::once(end_entity.as_ref()) - .chain(intermediates.iter().map(|cert| cert.as_ref())) + let certificates: Vec = std::iter::once(certificates.end_entity.as_ref()) + .chain(certificates.intermediates.iter().map(|cert| cert.as_ref())) .map(|cert| { SecCertificate::from_der(cert) .map_err(|_| TlsError::InvalidCertificate(CertificateError::BadEncoding)) }) .collect::, _>>()?; + let server_name = identity.server_name.to_str(); + // Apple's verifier doesn't require this but trim trailing `.` labels for consistency across platforms. + let server_name = server_name.strip_suffix('.').unwrap_or(&server_name); + + let ocsp_response = if identity.ocsp_response.is_empty() { + None + } else { + Some(identity.ocsp_response) + }; + + let now = identity.now; + // Create our verification policy suitable for verifying TLS chains. // This uses the "default" verification engine and parameters, the same as Windows. // @@ -170,14 +184,14 @@ impl Verifier { if !extra_roots.is_empty() { trust_evaluation .set_anchor_certificates(extra_roots) - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?; + .map_err(|e| TlsError::Other(OtherError::new(e)))?; // We want to trust both the system-installed and the extra roots. This must be set // since calling `SecTrustSetAnchorCertificates` "disables the trusting of any // anchors other than the ones specified by this function call" by default. trust_evaluation .set_trust_anchor_certificates_only(false) - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?; + .map_err(|e| TlsError::Other(OtherError::new(e)))?; } // When testing, support using fake roots and ignoring default roots present on the system for @@ -226,7 +240,7 @@ impl Verifier { CertificateError::UnknownIssuer, )), errors::errSecInvalidExtendedKeyUsage => Ok(TlsError::InvalidCertificate( - CertificateError::Other(OtherError(Arc::new(super::EkuError))), + CertificateError::Other(OtherError::new(super::EkuError)), )), errors::errSecCertificateRevoked => { Ok(TlsError::InvalidCertificate(CertificateError::Revoked)) @@ -243,31 +257,21 @@ impl Verifier { } #[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( +impl ServerVerifier for Verifier { + fn verify_identity<'a>( &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - // Convert IP addresses to name strings to ensure match check on leaf certificate. - // Ref: https://developer.apple.com/documentation/security/1392592-secpolicycreatessl - let server = server_name.to_str(); - // Apple's verifier doesn't require this but trim trailing `.` labels for consistency across platforms. - let server = server.strip_suffix('.').unwrap_or(&server); - - let ocsp_data = if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None + identity: &ServerIdentity<'a, '_>, + ) -> Result, TlsError> { + let Identity::X509(certificates) = identity.identity else { + return Err(invalid_certificate( + "platform verifier only supports X.509 certificates", + )); }; - match self.verify_certificate(end_entity, intermediates, server, ocsp_data, now) { - Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), + log_server_cert(&certificates.end_entity); + + match self.verify_certificate(certificates, identity) { + Ok(()) => Ok(VerifiedIdentity::assertion(identity.identity.clone())), Err(e) => { // This error only tells us what the system errored with, so it doesn't leak anything // sensitive. @@ -279,28 +283,20 @@ impl ServerCertVerifier for Verifier { fn verify_tls12_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { verify_tls12_signature( - message, - cert, - dss, + input, &self.crypto_provider.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { verify_tls13_signature( - message, - cert, - dss, + input, &self.crypto_provider.signature_verification_algorithms, ) } @@ -310,4 +306,15 @@ impl ServerCertVerifier for Verifier { .signature_verification_algorithms .supported_schemes() } + + fn request_ocsp_response(&self) -> bool { + true + } + + fn hash_config(&self, h: &mut dyn Hasher) { + h.write(b"rustls-platform-verifier-apple"); + h.write_usize(self.extra_roots.len()); + #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] + h.write_u8(u8::from(self.test_only_root_ca_override.is_some())); + } } diff --git a/rustls-platform-verifier/src/verification/mod.rs b/rustls-platform-verifier/src/verification/mod.rs index 9d947ff2..650b7fab 100644 --- a/rustls-platform-verifier/src/verification/mod.rs +++ b/rustls-platform-verifier/src/verification/mod.rs @@ -1,5 +1,5 @@ #[cfg(any(windows, target_vendor = "apple"))] -use std::sync::Arc; +use rustls::error::{CertificateError, OtherError}; #[cfg(all( any(unix, target_arch = "wasm32"), @@ -65,8 +65,8 @@ fn log_server_cert(_end_entity: &rustls::pki_types::CertificateDer<'_>) { // error with a platform specific error message. #[cfg(any(windows, target_vendor = "apple"))] fn invalid_certificate(reason: impl Into) -> rustls::Error { - rustls::Error::InvalidCertificate(rustls::CertificateError::Other(rustls::OtherError( - Arc::from(Box::from(reason.into())), + rustls::Error::InvalidCertificate(CertificateError::Other(OtherError::new( + std::io::Error::other(reason.into()), ))) } diff --git a/rustls-platform-verifier/src/verification/others.rs b/rustls-platform-verifier/src/verification/others.rs index d18515e8..6b89e006 100644 --- a/rustls-platform-verifier/src/verification/others.rs +++ b/rustls-platform-verifier/src/verification/others.rs @@ -1,13 +1,15 @@ use std::fmt::Debug; +use std::hash::Hasher; use std::sync::Arc; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::client::danger::{ + HandshakeSignatureValid, ServerIdentity, ServerVerifier, SignatureVerificationInput, +}; use rustls::client::WebPkiServerVerifier; +use rustls::crypto::{CryptoProvider, SignatureScheme, VerifiedIdentity}; +use rustls::error::{CertificateError, OtherError}; use rustls::pki_types; -use rustls::{ - crypto::CryptoProvider, CertificateError, DigitallySignedStruct, Error as TlsError, OtherError, - SignatureScheme, -}; +use rustls::Error as TlsError; use super::log_server_cert; @@ -66,12 +68,11 @@ impl Verifier { if let Some(test_root) = test_root { root_store.add(test_root)?; return Ok(Self { - inner: WebPkiServerVerifier::builder_with_provider( - root_store.into(), - crypto_provider.clone(), - ) - .build() - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?, + inner: Arc::new( + WebPkiServerVerifier::builder(Arc::new(root_store), &crypto_provider) + .build() + .map_err(|e| TlsError::Other(OtherError::new(e)))?, + ), }); } } @@ -118,30 +119,27 @@ impl Verifier { }; Ok(Self { - inner: WebPkiServerVerifier::builder_with_provider( - root_store.into(), - crypto_provider.clone(), - ) - .build() - .map_err(|e| TlsError::Other(OtherError(Arc::new(e))))?, + inner: Arc::new( + WebPkiServerVerifier::builder(Arc::new(root_store), &crypto_provider) + .build() + .map_err(|e| TlsError::Other(OtherError::new(e)))?, + ), }) } } #[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( +impl ServerVerifier for Verifier { + fn verify_identity<'a>( &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); + identity: &ServerIdentity<'a, '_>, + ) -> Result, TlsError> { + if let rustls::crypto::Identity::X509(certificates) = identity.identity { + log_server_cert(&certificates.end_entity); + } self.inner - .verify_server_cert(end_entity, intermediates, server_name, ocsp_response, now) + .verify_identity(identity) .map_err(map_webpki_errors) // This only contains information from the system or other public // bits of the TLS handshake, so it can't leak anything. @@ -153,34 +151,37 @@ impl ServerCertVerifier for Verifier { fn verify_tls12_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { - self.inner.verify_tls12_signature(message, cert, dss) + self.inner.verify_tls12_signature(input) } fn verify_tls13_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { - self.inner.verify_tls13_signature(message, cert, dss) + self.inner.verify_tls13_signature(input) } fn supported_verify_schemes(&self) -> Vec { self.inner.supported_verify_schemes() } + + fn request_ocsp_response(&self) -> bool { + self.inner.request_ocsp_response() + } + + fn hash_config(&self, h: &mut dyn Hasher) { + h.write(b"rustls-platform-verifier-others"); + self.inner.hash_config(h); + } } fn map_webpki_errors(err: TlsError) -> TlsError { match &err { TlsError::InvalidCertificate(CertificateError::InvalidPurpose) | TlsError::InvalidCertificate(CertificateError::InvalidPurposeContext { .. }) => { - TlsError::InvalidCertificate(CertificateError::Other(OtherError(Arc::new( - super::EkuError, - )))) + TlsError::InvalidCertificate(CertificateError::Other(OtherError::new(super::EkuError))) } _ => err, } diff --git a/rustls-platform-verifier/src/verification/windows.rs b/rustls-platform-verifier/src/verification/windows.rs index 24fadc9b..6aebc0ec 100644 --- a/rustls-platform-verifier/src/verification/windows.rs +++ b/rustls-platform-verifier/src/verification/windows.rs @@ -26,13 +26,17 @@ use std::{ sync::Arc, }; -use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerifier}; -use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider}; -use rustls::pki_types; -use rustls::{ - CertificateError, DigitallySignedStruct, Error as TlsError, Error::InvalidCertificate, - SignatureScheme, +use core::hash::Hasher; +use rustls::client::danger::{ + HandshakeSignatureValid, ServerIdentity, ServerVerifier, SignatureVerificationInput, +}; +use rustls::crypto::{ + verify_tls12_signature, verify_tls13_signature, CertificateIdentity, CryptoProvider, Identity, + SignatureScheme, VerifiedIdentity, }; +use rustls::error::CertificateError; +use rustls::pki_types; +use rustls::{Error as TlsError, Error::InvalidCertificate}; use windows_sys::Win32::{ Foundation::{ CERT_E_CN_NO_MATCH, CERT_E_EXPIRED, CERT_E_INVALID_NAME, CERT_E_UNTRUSTEDROOT, @@ -597,11 +601,8 @@ impl Verifier { /// Return `Ok(())` if the certificate was valid. fn verify_certificate( &self, - primary_cert: &[u8], - intermediate_certs: &[&[u8]], - server: &[u8], - ocsp_data: Option<&[u8]>, - now: pki_types::UnixTime, + certificates: &CertificateIdentity<'_>, + identity: &ServerIdentity<'_, '_>, ) -> Result<(), TlsError> { #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] let mut store = match self.test_only_root_ca_override.as_ref() { @@ -614,19 +615,19 @@ impl Verifier { #[cfg(not(any(test, feature = "ffi-testing", feature = "dbg")))] let mut store = CertificateStore::new()?; - let mut primary_cert = store.add_cert(primary_cert)?; + let mut primary_cert = store.add_cert(certificates.end_entity.as_ref())?; - for cert in intermediate_certs.iter().copied() { - store.add_cert(cert)?; + for cert in &certificates.intermediates { + store.add_cert(cert.as_ref())?; } - if let Some(ocsp_data) = ocsp_data { + if !identity.ocsp_response.is_empty() { #[allow(clippy::as_conversions)] let data = CRYPT_INTEGER_BLOB { - cbData: ocsp_data.len().try_into().map_err(|_| { + cbData: identity.ocsp_response.len().try_into().map_err(|_| { invalid_certificate("Malformed OCSP response stapled to server certificate") })?, - pbData: ocsp_data.as_ptr() as *mut u8, + pbData: identity.ocsp_response.as_ptr() as *mut u8, }; // SAFETY: `data` is a valid pointer and matches the property ID. @@ -639,13 +640,22 @@ impl Verifier { } // Encode UTF-16, null-terminated - let server: Vec = server + let server_name = identity.server_name.to_str(); + // Trim trailing `.` labels to remove compatibility hazards with the Windows verifier. + // It performs exact equality comparisons in most cases (see https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-httpspolicycallbackdata), + // which causes problems with hostnames considered equivalent by other verifier and TLS implementations. + // + // Ref: https://github.com/rustls/rustls-platform-verifier/issues/240 + let server_name = server_name.strip_suffix('.').unwrap_or(&server_name); + let server: Vec = server_name + .as_bytes() .iter() .map(|c| u16::from(*c)) .chain(Some(0)) .collect(); - let mut cert_chain = store.new_chain_in(&primary_cert, now, store.engine.as_ref())?; + let mut cert_chain = + store.new_chain_in(&primary_cert, identity.now, store.engine.as_ref())?; // We only use `TrustStatus` here because it hasn't had verification performed on it. // SAFETY: The pointer is guaranteed to be non-null. @@ -661,11 +671,12 @@ impl Verifier { if extra_roots_may_needed && self.extra_roots.is_some() { let mut store = CertificateStore::new()?; - for cert in intermediate_certs.iter().copied() { - store.add_cert(cert)?; + for cert in &certificates.intermediates { + store.add_cert(cert.as_ref())?; } - cert_chain = store.new_chain_in(&primary_cert, now, self.extra_roots.as_ref())?; + cert_chain = + store.new_chain_in(&primary_cert, identity.now, self.extra_roots.as_ref())?; } let status = cert_chain.verify_chain_policy(server)?; @@ -695,41 +706,21 @@ impl Verifier { } #[cfg_attr(docsrs, doc(cfg(all())))] -impl ServerCertVerifier for Verifier { - fn verify_server_cert( +impl ServerVerifier for Verifier { + fn verify_identity<'a>( &self, - end_entity: &pki_types::CertificateDer<'_>, - intermediates: &[pki_types::CertificateDer<'_>], - server_name: &pki_types::ServerName, - ocsp_response: &[u8], - now: pki_types::UnixTime, - ) -> Result { - log_server_cert(end_entity); - - let name = server_name.to_str(); - // Trim trailing `.` labels to remove compatibility hazards with the Windows verifier. - // It performs exact quality comparisions in most cases (see https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/ns-wincrypt-httpspolicycallbackdata) - // which causes problems with hostnames that are considered equivalent for security purposes in other verifier and TLS implementations. - // - // Ref: https://github.com/rustls/rustls-platform-verifier/issues/240 - let name = name.strip_suffix('.').unwrap_or(&name); - - let intermediate_certs: Vec<&[u8]> = intermediates.iter().map(|c| c.as_ref()).collect(); - - let ocsp_data = if !ocsp_response.is_empty() { - Some(ocsp_response) - } else { - None + identity: &ServerIdentity<'a, '_>, + ) -> Result, TlsError> { + let Identity::X509(certificates) = identity.identity else { + return Err(invalid_certificate( + "platform verifier only supports X.509 certificates", + )); }; - match self.verify_certificate( - end_entity.as_ref(), - &intermediate_certs, - name.as_bytes(), - ocsp_data, - now, - ) { - Ok(()) => Ok(rustls::client::danger::ServerCertVerified::assertion()), + log_server_cert(&certificates.end_entity); + + match self.verify_certificate(certificates, identity) { + Ok(()) => Ok(VerifiedIdentity::assertion(identity.identity.clone())), Err(e) => { // SAFETY: // Errors are our own custom errors, WinAPI errors, or static strings. @@ -741,28 +732,20 @@ impl ServerCertVerifier for Verifier { fn verify_tls12_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { verify_tls12_signature( - message, - cert, - dss, + input, &self.crypto_provider.signature_verification_algorithms, ) } fn verify_tls13_signature( &self, - message: &[u8], - cert: &pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { verify_tls13_signature( - message, - cert, - dss, + input, &self.crypto_provider.signature_verification_algorithms, ) } @@ -772,6 +755,17 @@ impl ServerCertVerifier for Verifier { .signature_verification_algorithms .supported_schemes() } + + fn request_ocsp_response(&self) -> bool { + true + } + + fn hash_config(&self, h: &mut dyn Hasher) { + h.write(b"rustls-platform-verifier-windows"); + h.write_u8(u8::from(self.extra_roots.is_some())); + #[cfg(any(test, feature = "ffi-testing", feature = "dbg"))] + h.write_u8(u8::from(self.test_only_root_ca_override.is_some())); + } } /// A trait to represent an object that can be safely created with all zero values