diff --git a/Cargo.toml b/Cargo.toml index dc7b55562a..024e121c9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,7 +74,7 @@ __tls = [] # Enables common rustls code. # Equivalent to rustls-tls-manual-roots but shorter :) -__rustls = ["hyper-rustls", "tokio-rustls", "rustls", "__tls", "rustls-pemfile"] +__rustls = ["hyper-rustls", "tokio-rustls", "rustls", "__tls", "rustls-pemfile", "rustls-pki-types"] # When enabled, disable using the cached SYS_PROXIES. __internal_proxy_sys_no_cache = [] @@ -120,10 +120,11 @@ native-tls-crate = { version = "0.2.10", optional = true, package = "native-tls" tokio-native-tls = { version = "0.3.0", optional = true } # rustls-tls -hyper-rustls = { version = "0.24.0", default-features = false, optional = true } -rustls = { version = "0.21.6", features = ["dangerous_configuration"], optional = true } -tokio-rustls = { version = "0.24", optional = true } -webpki-roots = { version = "0.25", optional = true } +hyper-rustls = { version = "0.26.0", default-features = false, optional = true } +rustls = { version = "0.22.2", optional = true } +rustls-pki-types = { version = "1.1.0", features = ["alloc"] ,optional = true } +tokio-rustls = { version = "0.25", optional = true } +webpki-roots = { version = "0.26.0", optional = true } rustls-native-certs = { version = "0.6", optional = true } rustls-pemfile = { version = "1.0", optional = true } @@ -150,7 +151,7 @@ futures-channel = { version="0.3", optional = true} [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] env_logger = "0.8" -hyper = { version = "0.14", default-features = false, features = ["tcp", "stream", "http1", "http2", "client", "server", "runtime"] } +hyper = { version = "1.1.0", default-features = false, features = ["http1", "http2", "client", "server"] } serde = { version = "1.0", features = ["derive"] } libflate = "1.0" brotli_crate = { package = "brotli", version = "3.3.0" } diff --git a/src/async_impl/client.rs b/src/async_impl/client.rs index 929fa1d132..382c261cba 100644 --- a/src/async_impl/client.rs +++ b/src/async_impl/client.rs @@ -13,9 +13,7 @@ use http::header::{ }; use http::uri::Scheme; use http::Uri; -use hyper_util::client::legacy::{ - connect::HttpConnector, /*, ResponseFuture as HyperResponseFuture*/ -}; +use hyper_util::client::legacy::connect::HttpConnector; #[cfg(feature = "native-tls-crate")] use native_tls_crate::TlsConnector; use pin_project_lite::pin_project; @@ -468,18 +466,7 @@ impl ClientBuilder { #[cfg(feature = "rustls-tls-webpki-roots")] if config.tls_built_in_root_certs { - use rustls::OwnedTrustAnchor; - - let trust_anchors = - webpki_roots::TLS_SERVER_ROOTS.iter().map(|trust_anchor| { - OwnedTrustAnchor::from_subject_spki_name_constraints( - trust_anchor.subject, - trust_anchor.spki, - trust_anchor.name_constraints, - ) - }); - - root_cert_store.add_trust_anchors(trust_anchors); + root_cert_store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); } #[cfg(feature = "rustls-tls-native-roots")] @@ -489,11 +476,10 @@ impl ClientBuilder { for cert in rustls_native_certs::load_native_certs() .map_err(crate::error::builder)? { - let cert = rustls::Certificate(cert.0); // Continue on parsing errors, as native stores often include ancient or syntactically // invalid certificates, like root certificates without any X509 extensions. // Inspiration: https://github.com/rustls/rustls/blob/633bf4ba9d9521a95f68766d04c22e2b01e68318/rustls/src/anchors.rs#L105-L112 - match root_cert_store.add(&cert) { + match root_cert_store.add(cert.into()) { Ok(_) => valid_count += 1, Err(err) => { invalid_count += 1; @@ -536,12 +522,8 @@ impl ClientBuilder { } // Build TLS config - let config_builder = rustls::ClientConfig::builder() - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(&versions) - .map_err(crate::error::builder)? - .with_root_certificates(root_cert_store); + let config_builder = + rustls::ClientConfig::builder().with_root_certificates(root_cert_store); // Finalize TLS config let mut tls = if let Some(id) = config.identity { diff --git a/src/async_impl/h3_client/connect.rs b/src/async_impl/h3_client/connect.rs index 968704713d..ec732f66a2 100644 --- a/src/async_impl/h3_client/connect.rs +++ b/src/async_impl/h3_client/connect.rs @@ -5,7 +5,7 @@ use bytes::Bytes; use h3::client::SendRequest; use h3_quinn::{Connection, OpenStreams}; use http::Uri; -use hyper::client::connect::dns::Name; +use hyper_util::client::legacy::connect::dns::Name; use quinn::{ClientConfig, Endpoint, TransportConfig}; use std::net::{IpAddr, SocketAddr}; use std::str::FromStr; diff --git a/src/async_impl/h3_client/dns.rs b/src/async_impl/h3_client/dns.rs index 9cb50d1e35..bd59daaed3 100644 --- a/src/async_impl/h3_client/dns.rs +++ b/src/async_impl/h3_client/dns.rs @@ -1,5 +1,5 @@ use core::task; -use hyper::client::connect::dns::Name; +use hyper_util::client::legacy::connect::dns::Name; use std::future::Future; use std::net::SocketAddr; use std::task::Poll; diff --git a/src/async_impl/h3_client/pool.rs b/src/async_impl/h3_client/pool.rs index 6fcb8e7196..6d3f047b68 100644 --- a/src/async_impl/h3_client/pool.rs +++ b/src/async_impl/h3_client/pool.rs @@ -13,7 +13,7 @@ use h3::client::SendRequest; use h3_quinn::{Connection, OpenStreams}; use http::uri::{Authority, Scheme}; use http::{Request, Response, Uri}; -use hyper::Body as HyperBody; +use hyper::body as HyperBody; use log::trace; pub(super) type Key = (Scheme, Authority); diff --git a/src/connect.rs b/src/connect.rs index 5c338a2b68..ef1056746b 100644 --- a/src/connect.rs +++ b/src/connect.rs @@ -9,6 +9,8 @@ use hyper_util::rt::TokioIo; use native_tls_crate::{TlsConnector, TlsConnectorBuilder}; use tower_service::Service; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; + use pin_project_lite::pin_project; use std::future::Future; use std::io::{self, IoSlice}; @@ -212,8 +214,9 @@ impl Connector { let tls = tls_proxy.clone(); let host = dst.host().ok_or("no host in url")?.to_string(); let conn = socks::connect(proxy, dst, dns).await?; - let server_name = rustls::ServerName::try_from(host.as_str()) - .map_err(|_| "Invalid Server Name")?; + let server_name = + rustls_pki_types::ServerName::try_from(host.as_str().to_owned()) + .map_err(|_| "Invalid Server Name")?; let io = RustlsConnector::from(tls) .connect(server_name, conn) .await?; @@ -301,8 +304,8 @@ impl Connector { if let hyper_rustls::MaybeHttpsStream::Https(stream) = io { if !self.nodelay { - let (io, _) = stream.get_ref(); - io.set_nodelay(false)?; + let (io, _) = stream.inner().get_ref(); + io.inner().inner().set_nodelay(false)?; } Ok(Conn { inner: self.verbose.wrap(RustlsTlsConn { inner: stream }), @@ -376,7 +379,7 @@ impl Connector { tls_proxy, } => { if dst.scheme() == Some(&Scheme::HTTPS) { - use rustls::ServerName; + use rustls_pki_types::ServerName; use std::convert::TryFrom; use tokio_rustls::TlsConnector as RustlsConnector; @@ -387,16 +390,18 @@ impl Connector { let tls = tls.clone(); let conn = http.call(proxy_dst).await?; log::trace!("tunneling HTTPS over proxy"); - let maybe_server_name = - ServerName::try_from(host.as_str()).map_err(|_| "Invalid Server Name"); + let maybe_server_name = ServerName::try_from(host.as_str().to_owned()) + .map_err(|_| "Invalid Server Name"); let tunneled = tunnel(conn, host, port, self.user_agent.clone(), auth).await?; let server_name = maybe_server_name?; let io = RustlsConnector::from(tls) - .connect(server_name, tunneled) + .connect(server_name, TokioIo::new(tunneled)) .await?; return Ok(Conn { - inner: self.verbose.wrap(RustlsTlsConn { inner: io }), + inner: self.verbose.wrap(RustlsTlsConn { + inner: TokioIo::new(io), + }), is_proxy: false, tls_info: false, }); @@ -533,54 +538,47 @@ impl TlsInfoFactory for hyper_tls::MaybeHttpsStream TlsInfoFactory for TokioIo { - fn tls_info(&self) -> Option { - self.inner().tls_info() - } -} - -#[cfg(feature = "default-tls")] -impl TlsInfoFactory for hyper_tls::MaybeHttpsStream { - fn tls_info(&self) -> Option { - match self { - hyper_tls::MaybeHttpsStream::Https(tls) => tls.tls_info(), - hyper_tls::MaybeHttpsStream::Http(_) => None, - } - } -} - -#[cfg(feature = "default-tls")] -impl TlsInfoFactory for hyper_tls::TlsStream -where - T: tokio::io::AsyncRead + tokio::io::AsyncWrite, -{ +#[cfg(feature = "__rustls")] +impl TlsInfoFactory for tokio_rustls::client::TlsStream>> { fn tls_info(&self) -> Option { let peer_certificate = self .get_ref() - .peer_certificate() - .ok() - .flatten() - .and_then(|c| c.to_der().ok()); + .1 + .peer_certificates() + .and_then(|certs| certs.first()) + .map(|c| c.first()) + .and_then(|c| c.map(|cc| vec![*cc])); Some(crate::tls::TlsInfo { peer_certificate }) } } - #[cfg(feature = "__rustls")] -impl TlsInfoFactory for tokio_rustls::TlsStream { +impl TlsInfoFactory + for tokio_rustls::client::TlsStream< + TokioIo>>, + > +{ fn tls_info(&self) -> Option { let peer_certificate = self .get_ref() .1 .peer_certificates() .and_then(|certs| certs.first()) - .map(|c| c.0.clone()); + .map(|c| c.first()) + .and_then(|c| c.map(|cc| vec![*cc])); Some(crate::tls::TlsInfo { peer_certificate }) } } -*/ + +#[cfg(feature = "__rustls")] +impl TlsInfoFactory for hyper_rustls::MaybeHttpsStream> { + fn tls_info(&self) -> Option { + match self { + hyper_rustls::MaybeHttpsStream::Https(tls) => tls.tls_info(), + hyper_rustls::MaybeHttpsStream::Http(_) => None, + } + } +} pub(crate) trait AsyncConn: Read + Write + Connection + Send + Sync + Unpin + 'static @@ -692,7 +690,6 @@ where T: Read + Write + Unpin, { use hyper_util::rt::TokioIo; - use tokio::io::{AsyncReadExt, AsyncWriteExt}; let mut buf = format!( "\ @@ -793,7 +790,7 @@ mod native_tls_conn { } } - impl Connection for NativeTlsConn>>> { + impl Connection for NativeTlsConn>>> { fn connected(&self) -> Connected { self.inner .inner() @@ -870,32 +867,57 @@ mod native_tls_conn { mod rustls_tls_conn { use super::TlsInfoFactory; use hyper::rt::{Read, ReadBufCursor, Write}; + use hyper_rustls::MaybeHttpsStream; use hyper_util::client::legacy::connect::{Connected, Connection}; + use hyper_util::rt::TokioIo; use pin_project_lite::pin_project; use std::{ io::{self, IoSlice}, pin::Pin, task::{Context, Poll}, }; + use tokio::io::{AsyncRead, AsyncWrite}; + use tokio::net::TcpStream; use tokio_rustls::client::TlsStream; pin_project! { pub(super) struct RustlsTlsConn { - #[pin] pub(super) inner: TlsStream, + #[pin] pub(super) inner: TokioIo>, } } - impl Connection for RustlsTlsConn { + impl Connection for RustlsTlsConn>> { + fn connected(&self) -> Connected { + if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + self.inner + .inner() + .get_ref() + .0 + .inner() + .connected() + .negotiated_h2() + } else { + self.inner.inner().get_ref().0.inner().connected() + } + } + } + impl Connection for RustlsTlsConn>>> { fn connected(&self) -> Connected { - if self.inner.get_ref().1.alpn_protocol() == Some(b"h2") { - self.inner.get_ref().0.connected().negotiated_h2() + if self.inner.inner().get_ref().1.alpn_protocol() == Some(b"h2") { + self.inner + .inner() + .get_ref() + .0 + .inner() + .connected() + .negotiated_h2() } else { - self.inner.get_ref().0.connected() + self.inner.inner().get_ref().0.inner().connected() } } } - impl Read for RustlsTlsConn { + impl Read for RustlsTlsConn { fn poll_read( self: Pin<&mut Self>, cx: &mut Context, @@ -906,14 +928,14 @@ mod rustls_tls_conn { } } - impl Write for RustlsTlsConn { + impl Write for RustlsTlsConn { fn poll_write( self: Pin<&mut Self>, cx: &mut Context, buf: &[u8], ) -> Poll> { let this = self.project(); - AsyncWrite::poll_write(this.inner, cx, buf) + Write::poll_write(this.inner, cx, buf) } fn poll_write_vectored( @@ -922,7 +944,7 @@ mod rustls_tls_conn { bufs: &[IoSlice<'_>], ) -> Poll> { let this = self.project(); - AsyncWrite::poll_write_vectored(this.inner, cx, bufs) + Write::poll_write_vectored(this.inner, cx, bufs) } fn is_write_vectored(&self) -> bool { @@ -934,7 +956,7 @@ mod rustls_tls_conn { cx: &mut Context, ) -> Poll> { let this = self.project(); - AsyncWrite::poll_flush(this.inner, cx) + Write::poll_flush(this.inner, cx) } fn poll_shutdown( @@ -942,17 +964,13 @@ mod rustls_tls_conn { cx: &mut Context, ) -> Poll> { let this = self.project(); - AsyncWrite::poll_shutdown(this.inner, cx) - } - } - - impl TlsInfoFactory for RustlsTlsConn { - fn tls_info(&self) -> Option { - self.inner.tls_info() + Write::poll_shutdown(this.inner, cx) } } - - impl TlsInfoFactory for RustlsTlsConn> { + impl TlsInfoFactory for RustlsTlsConn + where + TokioIo>: TlsInfoFactory, + { fn tls_info(&self) -> Option { self.inner.tls_info() } diff --git a/src/dns/trust_dns.rs b/src/dns/trust_dns.rs index 5af17cd67c..da4dc2f46f 100644 --- a/src/dns/trust_dns.rs +++ b/src/dns/trust_dns.rs @@ -1,6 +1,6 @@ //! DNS resolution via the [trust_dns_resolver](https://github.com/bluejekyll/trust-dns) crate -use hyper::client::connect::dns::Name; +use hyper_util::client::legacy::connect::dns::Name; use once_cell::sync::OnceCell; pub use trust_dns_resolver::config::{ResolverConfig, ResolverOpts}; use trust_dns_resolver::{lookup_ip::LookupIpIntoIter, system_conf, TokioAsyncResolver}; diff --git a/src/tls.rs b/src/tls.rs index 8d65773943..9cb49fcdca 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -13,9 +13,11 @@ #[cfg(feature = "__rustls")] use rustls::{ - client::HandshakeSignatureValid, client::ServerCertVerified, client::ServerCertVerifier, - DigitallySignedStruct, Error as TLSError, ServerName, + client::danger::HandshakeSignatureValid, client::danger::ServerCertVerified, + client::danger::ServerCertVerifier, DigitallySignedStruct, Error as TLSError, SignatureScheme, }; +#[cfg(feature = "__rustls")] +use rustls_pki_types::{ServerName, UnixTime}; use std::fmt; /// Represents a server X509 certificate. @@ -41,7 +43,6 @@ pub struct Identity { inner: ClientCert, } -#[derive(Clone)] enum ClientCert { #[cfg(feature = "native-tls")] Pkcs12(native_tls_crate::Identity), @@ -49,11 +50,32 @@ enum ClientCert { Pkcs8(native_tls_crate::Identity), #[cfg(feature = "__rustls")] Pem { - key: rustls::PrivateKey, - certs: Vec, + key: rustls_pki_types::PrivateKeyDer<'static>, + certs: Vec>, }, } +impl Clone for ClientCert { + fn clone(&self) -> Self { + match self { + #[cfg(feature = "native-tls")] + Self::Pkcs8(i) => Self::Pkcs8(i.clone()), + #[cfg(feature = "native-tls")] + Self::Pkcs12(i) => Self::Pkcs12(i.clone()), + #[cfg(feature = "__rustls")] + ClientCert::Pem { key, certs } => ClientCert::Pem { + key: key.clone_key(), + certs: certs.clone(), + }, + #[cfg_attr( + any(feature = "native-tls", feature = "__rustls"), + allow(unreachable_patterns) + )] + _ => unreachable!(), + } + } +} + impl Certificate { /// Create a `Certificate` from a binary DER encoded certificate /// @@ -119,7 +141,7 @@ impl Certificate { match self.original { Cert::Der(buf) => root_cert_store - .add(&rustls::Certificate(buf)) + .add(buf.into()) .map_err(crate::error::builder)?, Cert::Pem(buf) => { let mut pem = Cursor::new(buf); @@ -130,7 +152,7 @@ impl Certificate { })?; for c in certs { root_cert_store - .add(&rustls::Certificate(c)) + .add(c.into()) .map_err(crate::error::builder)?; } } @@ -245,8 +267,8 @@ impl Identity { let (key, certs) = { let mut pem = Cursor::new(buf); - let mut sk = Vec::::new(); - let mut certs = Vec::::new(); + let mut sk = Vec::::new(); + let mut certs = Vec::::new(); for item in std::iter::from_fn(|| rustls_pemfile::read_one(&mut pem).transpose()) { match item.map_err(|_| { @@ -254,12 +276,16 @@ impl Identity { "Invalid identity PEM file", ))) })? { - rustls_pemfile::Item::X509Certificate(cert) => { - certs.push(rustls::Certificate(cert)) + rustls_pemfile::Item::X509Certificate(cert) => certs.push(cert.into()), + rustls_pemfile::Item::PKCS8Key(key) => { + sk.push(rustls_pki_types::PrivateKeyDer::Pkcs8(key.into())) + } + rustls_pemfile::Item::RSAKey(key) => { + sk.push(rustls_pki_types::PrivateKeyDer::Pkcs1(key.into())) + } + rustls_pemfile::Item::ECKey(key) => { + sk.push(rustls_pki_types::PrivateKeyDer::Sec1(key.into())) } - rustls_pemfile::Item::PKCS8Key(key) => sk.push(rustls::PrivateKey(key)), - rustls_pemfile::Item::RSAKey(key) => sk.push(rustls::PrivateKey(key)), - rustls_pemfile::Item::ECKey(key) => sk.push(rustls::PrivateKey(key)), _ => { return Err(crate::error::builder(TLSError::General(String::from( "No valid certificate was found", @@ -302,7 +328,8 @@ impl Identity { self, config_builder: rustls::ConfigBuilder< rustls::ClientConfig, - rustls::client::WantsTransparencyPolicyOrClientCert, + // Not sure here + rustls::client::WantsClientCert, >, ) -> crate::Result { match self.inner { @@ -428,18 +455,18 @@ impl Default for TlsBackend { } #[cfg(feature = "__rustls")] +#[derive(Debug)] pub(crate) struct NoVerifier; #[cfg(feature = "__rustls")] impl ServerCertVerifier for NoVerifier { fn verify_server_cert( &self, - _end_entity: &rustls::Certificate, - _intermediates: &[rustls::Certificate], + _end_entity: &rustls_pki_types::CertificateDer, + _intermediates: &[rustls_pki_types::CertificateDer], _server_name: &ServerName, - _scts: &mut dyn Iterator, _ocsp_response: &[u8], - _now: std::time::SystemTime, + _now: UnixTime, ) -> Result { Ok(ServerCertVerified::assertion()) } @@ -447,7 +474,7 @@ impl ServerCertVerifier for NoVerifier { fn verify_tls12_signature( &self, _message: &[u8], - _cert: &rustls::Certificate, + _cert: &rustls_pki_types::CertificateDer, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) @@ -456,11 +483,29 @@ impl ServerCertVerifier for NoVerifier { fn verify_tls13_signature( &self, _message: &[u8], - _cert: &rustls::Certificate, + _cert: &rustls_pki_types::CertificateDer, _dss: &DigitallySignedStruct, ) -> Result { Ok(HandshakeSignatureValid::assertion()) } + + fn supported_verify_schemes(&self) -> Vec { + vec![ + SignatureScheme::RSA_PKCS1_SHA1, + SignatureScheme::ECDSA_SHA1_Legacy, + SignatureScheme::RSA_PKCS1_SHA256, + SignatureScheme::ECDSA_NISTP256_SHA256, + SignatureScheme::RSA_PKCS1_SHA384, + SignatureScheme::ECDSA_NISTP384_SHA384, + SignatureScheme::RSA_PKCS1_SHA512, + SignatureScheme::ECDSA_NISTP521_SHA512, + SignatureScheme::RSA_PSS_SHA256, + SignatureScheme::RSA_PSS_SHA384, + SignatureScheme::RSA_PSS_SHA512, + SignatureScheme::ED25519, + SignatureScheme::ED448, + ] + } } /// Hyper extension carrying extra TLS layer information.