diff --git a/Cargo.toml b/Cargo.toml index 01dfa1d12..ed410b3b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,8 +87,8 @@ http3 = ["rustls", "dep:h3", "dep:h3-quinn", "dep:quinn", "tokio/macros"] __tls = ["dep:rustls-pki-types", "tokio/io-util"] # Enables common rustls code. -__rustls = ["dep:hyper-rustls", "dep:tokio-rustls", "dep:rustls", "__tls"] -__rustls-aws-lc-rs = ["hyper-rustls?/aws-lc-rs", "tokio-rustls?/aws-lc-rs", "rustls?/aws-lc-rs", "quinn?/rustls-aws-lc-rs"] +__rustls = ["dep:hyper-rustls", "dep:tokio-rustls", "dep:rustls", "dep:rustls-util", "__tls"] +__rustls-aws-lc-rs = ["hyper-rustls?/aws-lc-rs", "tokio-rustls?/aws-lc-rs", "dep:rustls-aws-lc-rs", "quinn?/rustls-aws-lc-rs"] # Enables common native-tls code. __native-tls = ["dep:hyper-tls", "dep:native-tls-crate", "__tls", "dep:tokio-native-tls"] @@ -138,10 +138,12 @@ native-tls-crate = { version = "0.2.16", optional = true, package = "native-tls" tokio-native-tls = { version = "0.3.0", optional = true } # default rustls -hyper-rustls = { version = "0.27.0", default-features = false, optional = true, features = ["http1", "tls12"] } -rustls = { version = "0.23.4", optional = true, default-features = false, features = ["std", "tls12"] } -tokio-rustls = { version = "0.26", optional = true, default-features = false, features = ["tls12"] } -rustls-platform-verifier = { version = ">=0.6.0, <0.8.0", optional = true } +hyper-rustls = { git = "https://github.com/rustls/hyper-rustls.git", rev = "836e95c4d3b111973ce0e718b8e0035a97658d01", version = "0.27.10", default-features = false, optional = true, features = ["http1", "tls12"] } +rustls = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.24.0-dev.0", optional = true, default-features = false, features = ["log", "webpki"] } +tokio-rustls = { git = "https://github.com/rustls/tokio-rustls.git", rev = "be34e90bfe59f124363d725ceb739a435bfafa1e", version = "0.26.4", optional = true, default-features = false, features = ["tls12"] } +rustls-platform-verifier = { git = "https://github.com/rustls/rustls-platform-verifier.git", rev = "733494d8ade721f249dc1b4e93196adf409ae8c0", version = "0.7", optional = true } +rustls-aws-lc-rs = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.1.0-dev.0", default-features = false, features = ["aws-lc-sys", "std"], optional = true } +rustls-util = { git = "https://github.com/rustls/rustls.git", branch = "main", version = "0.1.0", optional = true } ## cookies cookie_crate = { version = "0.18.0", package = "cookie", optional = true } diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 4996a4c1f..4a0834da1 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -692,7 +692,7 @@ impl ClientBuilder { if let Some(min_tls_version) = config.min_tls_version { versions.retain(|&supported_version| { - match tls::Version::from_rustls(supported_version.version) { + match tls::Version::from_rustls(supported_version.version()) { Some(version) => version >= min_tls_version, // Assume it's so new we don't know about it, allow it // (as of writing this is unreachable) @@ -703,7 +703,7 @@ impl ClientBuilder { if let Some(max_tls_version) = config.max_tls_version { versions.retain(|&supported_version| { - match tls::Version::from_rustls(supported_version.version) { + match tls::Version::from_rustls(supported_version.version()) { Some(version) => version <= max_tls_version, None => false, } @@ -716,16 +716,26 @@ impl ClientBuilder { // Allow user to have installed a runtime default. // If not, we ship with _our_ recommended default. - let provider = rustls::crypto::CryptoProvider::get_default() - .map(|arc| arc.clone()) - .unwrap_or_else(default_rustls_crypto_provider); + let mut provider = rustls::crypto::CryptoProvider::get_default() + .map(|arc| arc.as_ref().clone()) + .unwrap_or_else(|| default_rustls_crypto_provider().as_ref().clone()); + if !versions + .iter() + .any(|version| version.version() == rustls::enums::ProtocolVersion::TLSv1_2) + { + provider.tls12_cipher_suites = std::borrow::Cow::Borrowed(&[]); + } + if !versions + .iter() + .any(|version| version.version() == rustls::enums::ProtocolVersion::TLSv1_3) + { + provider.tls13_cipher_suites = std::borrow::Cow::Borrowed(&[]); + } + let provider = Arc::new(provider); // Build TLS config let signature_algorithms = provider.signature_verification_algorithms; - let config_builder = - rustls::ClientConfig::builder_with_provider(provider.clone()) - .with_protocol_versions(&versions) - .map_err(|_| crate::error::builder("invalid TLS versions"))?; + let config_builder = rustls::ClientConfig::builder(provider.clone()); let config_builder = if !config.certs_verification { config_builder @@ -739,11 +749,22 @@ impl ClientBuilder { )); } + let roots = crate::tls::rustls_store(config.root_certs)?; + let signature_verifier = + rustls::client::WebPkiServerVerifier::builder( + Arc::new(roots.clone()), + provider.as_ref(), + ) + .build() + .map_err(|_| { + crate::error::builder("invalid TLS verification settings") + })?; config_builder .dangerous() .with_custom_certificate_verifier(Arc::new(IgnoreHostname::new( - crate::tls::rustls_store(config.root_certs)?, + roots, signature_algorithms, + Arc::new(signature_verifier), ))) } else if !config.tls_certs_only { // Check for some misconfigurations and report them. @@ -793,16 +814,16 @@ impl ClientBuilder { .map(|e| e.as_rustls_crl()) .collect::>(); let verifier = - rustls::client::WebPkiServerVerifier::builder_with_provider( + rustls::client::WebPkiServerVerifier::builder( Arc::new(crate::tls::rustls_store(config.root_certs)?), - provider, + provider.as_ref(), ) .with_crls(crls) .build() .map_err(|_| { crate::error::builder("invalid TLS verification settings") })?; - config_builder.with_webpki_verifier(verifier) + config_builder.with_webpki_verifier(verifier.into()) } }; @@ -810,23 +831,25 @@ impl ClientBuilder { let mut tls = if let Some(id) = config.identity { id.add_to_rustls(config_builder)? } else { - config_builder.with_no_client_auth() + config_builder + .with_no_client_auth() + .map_err(crate::error::builder)? }; tls.enable_sni = config.tls_sni; if config.tls_sslkeylogfile { - tls.key_log = Arc::new(rustls::KeyLogFile::new()); + tls.key_log = Arc::new(rustls_util::KeyLogFile::new()); } // ALPN protocol match config.http_version_pref { HttpVersionPref::Http1 => { - tls.alpn_protocols = vec!["http/1.1".into()]; + tls.alpn_protocols = vec![b"http/1.1".into()]; } #[cfg(feature = "http2")] HttpVersionPref::Http2 => { - tls.alpn_protocols = vec!["h2".into()]; + tls.alpn_protocols = vec![b"h2".into()]; } #[cfg(feature = "http3")] HttpVersionPref::Http3 => { @@ -835,8 +858,8 @@ impl ClientBuilder { HttpVersionPref::All => { tls.alpn_protocols = vec![ #[cfg(feature = "http2")] - "h2".into(), - "http/1.1".into(), + b"h2".into(), + b"http/1.1".into(), ]; } } @@ -2485,12 +2508,12 @@ fn default_rustls_crypto_provider() -> Arc { "No rustls crypto provider is configured. \ When using the `rustls-no-provider` feature you must install a \ crypto provider before building a Client. For example: \ - `rustls::crypto::aws_lc_rs::default_provider().install_default().unwrap();` \ + `rustls_aws_lc_rs::DEFAULT_PROVIDER.install_default().unwrap();` \ See https://docs.rs/rustls/latest/rustls/#cryptography-providers for details." ); #[cfg(feature = "__rustls-aws-lc-rs")] - Arc::new(rustls::crypto::aws_lc_rs::default_provider()) + Arc::new(rustls_aws_lc_rs::DEFAULT_PROVIDER) } impl Client { diff --git a/src/connect.rs b/src/connect.rs index f2d456e13..de44cd099 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -966,6 +966,21 @@ impl TlsInfoFactory for TokioIo { } } +#[cfg(feature = "__rustls")] +fn rustls_peer_certificate(conn: &rustls::ClientConnection) -> Option> { + match conn.peer_identity()? { + rustls::crypto::Identity::X509(certificates) => Some(certificates.end_entity.to_vec()), + rustls::crypto::Identity::RawPublicKey(_) => None, + _ => None, + } +} + +#[cfg(feature = "__rustls")] +fn rustls_is_h2(conn: &rustls::ClientConnection) -> bool { + conn.alpn_protocol() + .is_some_and(|protocol| protocol.as_ref() == b"h2") +} + // ===== TcpStream ===== #[cfg(feature = "__tls")] @@ -1018,12 +1033,7 @@ impl TlsInfoFactory for hyper_tls::MaybeHttpsStream>> { fn tls_info(&self) -> Option { - let peer_certificate = self - .get_ref() - .1 - .peer_certificates() - .and_then(|certs| certs.first()) - .map(|c| c.to_vec()); + let peer_certificate = rustls_peer_certificate(self.get_ref().1); Some(crate::tls::TlsInfo { peer_certificate }) } } @@ -1035,12 +1045,7 @@ impl TlsInfoFactory > { fn tls_info(&self) -> Option { - let peer_certificate = self - .get_ref() - .1 - .peer_certificates() - .and_then(|certs| certs.first()) - .map(|c| c.to_vec()); + let peer_certificate = rustls_peer_certificate(self.get_ref().1); Some(crate::tls::TlsInfo { peer_certificate }) } } @@ -1112,12 +1117,7 @@ impl TlsInfoFactory for hyper_tls::MaybeHttpsStream>> { fn tls_info(&self) -> Option { - let peer_certificate = self - .get_ref() - .1 - .peer_certificates() - .and_then(|certs| certs.first()) - .map(|c| c.to_vec()); + let peer_certificate = rustls_peer_certificate(self.get_ref().1); Some(crate::tls::TlsInfo { peer_certificate }) } } @@ -1130,12 +1130,7 @@ impl TlsInfoFactory > { fn tls_info(&self) -> Option { - let peer_certificate = self - .get_ref() - .1 - .peer_certificates() - .and_then(|certs| certs.first()) - .map(|c| c.to_vec()); + let peer_certificate = rustls_peer_certificate(self.get_ref().1); Some(crate::tls::TlsInfo { peer_certificate }) } } @@ -1220,12 +1215,7 @@ impl TlsInfoFactory > { fn tls_info(&self) -> Option { - let peer_certificate = self - .get_ref() - .1 - .peer_certificates() - .and_then(|certs| certs.first()) - .map(|c| c.to_vec()); + let peer_certificate = rustls_peer_certificate(self.get_ref().1); Some(crate::tls::TlsInfo { peer_certificate }) } } @@ -1242,12 +1232,7 @@ impl TlsInfoFactory > { fn tls_info(&self) -> Option { - let peer_certificate = self - .get_ref() - .1 - .peer_certificates() - .and_then(|certs| certs.first()) - .map(|c| c.to_vec()); + let peer_certificate = rustls_peer_certificate(self.get_ref().1); Some(crate::tls::TlsInfo { peer_certificate }) } } @@ -1640,7 +1625,7 @@ mod native_tls_conn { #[cfg(feature = "__rustls")] mod rustls_tls_conn { - use super::TlsInfoFactory; + use super::{rustls_is_h2, TlsInfoFactory}; use hyper::rt::{Read, ReadBufCursor, Write}; use hyper_rustls::MaybeHttpsStream; use hyper_util::client::legacy::connect::{Connected, Connection}; @@ -1663,7 +1648,7 @@ mod rustls_tls_conn { impl Connection for RustlsTlsConn>> { fn connected(&self) -> Connected { - if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + if rustls_is_h2(self.inner.inner().get_ref().1) { self.inner .inner() .get_ref() @@ -1678,7 +1663,7 @@ mod rustls_tls_conn { } impl Connection for RustlsTlsConn>>> { fn connected(&self) -> Connected { - if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + if rustls_is_h2(self.inner.inner().get_ref().1) { self.inner .inner() .get_ref() @@ -1695,7 +1680,7 @@ mod rustls_tls_conn { #[cfg(unix)] impl Connection for RustlsTlsConn>> { fn connected(&self) -> Connected { - if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + if rustls_is_h2(self.inner.inner().get_ref().1) { self.inner .inner() .get_ref() @@ -1712,7 +1697,7 @@ mod rustls_tls_conn { #[cfg(unix)] impl Connection for RustlsTlsConn>>> { fn connected(&self) -> Connected { - if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + if rustls_is_h2(self.inner.inner().get_ref().1) { self.inner .inner() .get_ref() @@ -1731,7 +1716,7 @@ mod rustls_tls_conn { for RustlsTlsConn>> { fn connected(&self) -> Connected { - if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + if rustls_is_h2(self.inner.inner().get_ref().1) { self.inner .inner() .get_ref() @@ -1752,7 +1737,7 @@ mod rustls_tls_conn { > { fn connected(&self) -> Connected { - if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + if rustls_is_h2(self.inner.inner().get_ref().1) { self.inner .inner() .get_ref() diff --git a/src/tls.rs b/src/tls.rs index 03e9dbdb3..7e2ee5697 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -54,7 +54,7 @@ //! [`CryptoProvider::install_default`][]: //! //! ```rust,ignore -//! rustls::crypto::ring::default_provider() +//! rustls_ring::DEFAULT_PROVIDER //! .install_default() //! .expect("Failed to install rustls crypto provider"); //! @@ -67,16 +67,20 @@ #[cfg(feature = "__rustls")] use rustls::{ - client::danger::HandshakeSignatureValid, client::danger::ServerCertVerified, - client::danger::ServerCertVerifier, crypto::WebPkiSupportedAlgorithms, - server::ParsedCertificate, DigitallySignedStruct, Error as TLSError, RootCertStore, - SignatureScheme, + client::danger::{ + HandshakeSignatureValid, PeerVerified, ServerIdentity, ServerVerifier, + SignatureVerificationInput, + }, + crypto::{Identity as RustlsIdentity, SignatureScheme, WebPkiSupportedAlgorithms}, + enums::ProtocolVersion, + error::ApiMisuse, + server::ParsedCertificate, + Error as TLSError, RootCertStore, }; use rustls_pki_types::pem::PemObject; -#[cfg(feature = "__rustls")] -use rustls_pki_types::{ServerName, UnixTime}; use std::{ fmt, + hash::Hasher, io::{BufRead, BufReader}, }; @@ -438,9 +442,14 @@ impl Identity { >, ) -> crate::Result { match self.inner { - ClientCert::Pem { key, certs } => config_builder - .with_client_auth_cert(certs, key) - .map_err(crate::error::builder), + ClientCert::Pem { key, certs } => { + let identity = RustlsIdentity::from_cert_chain(certs) + .map(std::sync::Arc::new) + .map_err(crate::error::builder)?; + config_builder + .with_client_auth_cert(identity, key) + .map_err(crate::error::builder) + } #[cfg(feature = "__native-tls")] ClientCert::Pkcs12(..) | ClientCert::Pkcs8(..) => { Err(crate::error::builder("incompatible TLS identity type")) @@ -572,14 +581,14 @@ impl Version { } #[cfg(feature = "__rustls")] - pub(crate) fn from_rustls(version: rustls::ProtocolVersion) -> Option { + pub(crate) fn from_rustls(version: ProtocolVersion) -> Option { match version { - rustls::ProtocolVersion::SSLv2 => None, - rustls::ProtocolVersion::SSLv3 => None, - rustls::ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)), - rustls::ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)), - rustls::ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)), - rustls::ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)), + ProtocolVersion::SSLv2 => None, + ProtocolVersion::SSLv3 => None, + ProtocolVersion::TLSv1_0 => Some(Self(InnerVersion::Tls1_0)), + ProtocolVersion::TLSv1_1 => Some(Self(InnerVersion::Tls1_1)), + ProtocolVersion::TLSv1_2 => Some(Self(InnerVersion::Tls1_2)), + ProtocolVersion::TLSv1_3 => Some(Self(InnerVersion::Tls1_3)), _ => None, } } @@ -670,32 +679,24 @@ pub(crate) fn rustls_der( pub(crate) struct NoVerifier; #[cfg(feature = "__rustls")] -impl ServerCertVerifier for NoVerifier { - fn verify_server_cert( +impl ServerVerifier for NoVerifier { + fn verify_identity( &self, - _end_entity: &rustls_pki_types::CertificateDer, - _intermediates: &[rustls_pki_types::CertificateDer], - _server_name: &ServerName, - _ocsp_response: &[u8], - _now: UnixTime, - ) -> Result { - Ok(ServerCertVerified::assertion()) + _identity: &ServerIdentity<'_>, + ) -> Result { + Ok(PeerVerified::assertion()) } fn verify_tls12_signature( &self, - _message: &[u8], - _cert: &rustls_pki_types::CertificateDer, - _dss: &DigitallySignedStruct, + _input: &SignatureVerificationInput<'_>, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } fn verify_tls13_signature( &self, - _message: &[u8], - _cert: &rustls_pki_types::CertificateDer, - _dss: &DigitallySignedStruct, + _input: &SignatureVerificationInput<'_>, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } @@ -717,6 +718,14 @@ impl ServerCertVerifier for NoVerifier { SignatureScheme::ED448, ] } + + fn request_ocsp_response(&self) -> bool { + false + } + + fn hash_config(&self, h: &mut dyn Hasher) { + h.write(b"reqwest-no-verifier"); + } } #[cfg(feature = "__rustls")] @@ -724,6 +733,7 @@ impl ServerCertVerifier for NoVerifier { pub(crate) struct IgnoreHostname { roots: RootCertStore, signature_algorithms: WebPkiSupportedAlgorithms, + signature_verifier: std::sync::Arc, } #[cfg(feature = "__rustls")] @@ -731,57 +741,76 @@ impl IgnoreHostname { pub(crate) fn new( roots: RootCertStore, signature_algorithms: WebPkiSupportedAlgorithms, + signature_verifier: std::sync::Arc, ) -> Self { Self { roots, signature_algorithms, + signature_verifier, } } } #[cfg(feature = "__rustls")] -impl ServerCertVerifier for IgnoreHostname { - fn verify_server_cert( +impl ServerVerifier for IgnoreHostname { + fn verify_identity( &self, - end_entity: &rustls_pki_types::CertificateDer<'_>, - intermediates: &[rustls_pki_types::CertificateDer<'_>], - _server_name: &ServerName<'_>, - _ocsp_response: &[u8], - now: UnixTime, - ) -> Result { - let cert = ParsedCertificate::try_from(end_entity)?; - - rustls::client::verify_server_cert_signed_by_trust_anchor( + identity: &ServerIdentity<'_>, + ) -> Result { + let RustlsIdentity::X509(certificates) = identity.identity else { + return Err(ApiMisuse::UnverifiableCertificateType.into()); + }; + + let cert = ParsedCertificate::try_from(&certificates.end_entity)?; + let supported_algs: Vec<_> = self + .signature_algorithms + .mapping() + .iter() + .flat_map(|(_, algs)| algs.iter().copied()) + .collect(); + + rustls::client::verify_identity_signed_by_trust_anchor( &cert, &self.roots, - intermediates, - now, - self.signature_algorithms.all, + &certificates.intermediates, + identity.now, + &supported_algs, )?; - Ok(ServerCertVerified::assertion()) + Ok(PeerVerified::assertion()) } fn verify_tls12_signature( &self, - message: &[u8], - cert: &rustls_pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { - rustls::crypto::verify_tls12_signature(message, cert, dss, &self.signature_algorithms) + self.signature_verifier.verify_tls12_signature(input) } fn verify_tls13_signature( &self, - message: &[u8], - cert: &rustls_pki_types::CertificateDer<'_>, - dss: &DigitallySignedStruct, + input: &SignatureVerificationInput<'_>, ) -> Result { - rustls::crypto::verify_tls13_signature(message, cert, dss, &self.signature_algorithms) + self.signature_verifier.verify_tls13_signature(input) } fn supported_verify_schemes(&self) -> Vec { self.signature_algorithms.supported_schemes() } + + fn request_ocsp_response(&self) -> bool { + false + } + + fn hash_config(&self, h: &mut dyn Hasher) { + h.write(b"reqwest-ignore-hostname"); + for root in &self.roots.roots { + h.write(root.subject.as_ref()); + h.write(root.subject_public_key_info.as_ref()); + if let Some(name_constraints) = &root.name_constraints { + h.write(name_constraints.as_ref()); + } + } + } } /// Hyper extension carrying extra TLS layer information. diff --git a/tests/client.rs b/tests/client.rs index 710ce5473..286815f32 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -357,13 +357,11 @@ fn use_preconfigured_rustls_default() { extern crate rustls; let root_cert_store = rustls::RootCertStore::empty(); - let tls = rustls::ClientConfig::builder_with_provider(std::sync::Arc::new( - rustls::crypto::aws_lc_rs::default_provider(), - )) - .with_safe_default_protocol_versions() - .unwrap() - .with_root_certificates(root_cert_store) - .with_no_client_auth(); + let tls = + rustls::ClientConfig::builder(std::sync::Arc::new(rustls_aws_lc_rs::DEFAULT_PROVIDER)) + .with_root_certificates(root_cert_store) + .with_no_client_auth() + .unwrap(); reqwest::Client::builder() .use_preconfigured_tls(tls) diff --git a/tests/support/server.rs b/tests/support/server.rs index 343b1c239..319e3a968 100644 --- a/tests/support/server.rs +++ b/tests/support/server.rs @@ -342,7 +342,7 @@ fn install_default_crypto_provider() -> bool { .expect("failed to install the default Ring TLS provider"); #[cfg(feature = "__rustls-aws-lc-rs")] - rustls::crypto::aws_lc_rs::default_provider() + rustls_aws_lc_rs::DEFAULT_PROVIDER .install_default() .expect("failed to install the default TLS provider");