diff --git a/Cargo.toml b/Cargo.toml index f6b752d..fa3c91d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,10 +45,10 @@ url = { version = "2.4", optional = true } ## tls native-tls = { version = "0.2.5", optional = true } # feature -rustls = { version = "0.21", features = ["dangerous_configuration"], optional = true } -rustls-pemfile = { version = "1", optional = true } -rustls-native-certs = { version = "0.6.2", optional = true } -webpki-roots = { version = "0.25", optional = true } +rustls = { version = "0.22.1", optional = true } +rustls-pemfile = { version = "2", optional = true } +rustls-native-certs = { version = "0.7", optional = true } +webpki-roots = { version = "0.26", optional = true } boring = { version = "4", optional = true } # async @@ -59,12 +59,12 @@ async-trait = { version = "0.1", optional = true } ## async-std async-std = { version = "1.8", optional = true } #async-native-tls = { version = "0.3.3", optional = true } -futures-rustls = { version = "0.24", optional = true } +futures-rustls = { version = "0.25", optional = true } ## tokio tokio1_crate = { package = "tokio", version = "1", optional = true } tokio1_native_tls_crate = { package = "tokio-native-tls", version = "0.3", optional = true } -tokio1_rustls = { package = "tokio-rustls", version = "0.24", optional = true } +tokio1_rustls = { package = "tokio-rustls", version = "0.25", optional = true } tokio1_boring = { package = "tokio-boring", version = "4", optional = true } ## dkim diff --git a/src/transport/smtp/client/async_net.rs b/src/transport/smtp/client/async_net.rs index 284ae51..d9f1b46 100644 --- a/src/transport/smtp/client/async_net.rs +++ b/src/transport/smtp/client/async_net.rs @@ -16,6 +16,8 @@ use futures_io::{ }; #[cfg(feature = "async-std1-rustls-tls")] use futures_rustls::client::TlsStream as AsyncStd1RustlsTlsStream; +#[cfg(any(feature = "tokio1-rustls-tls", feature = "async-std1-rustls-tls"))] +use rustls::pki_types::ServerName; #[cfg(feature = "tokio1-boring-tls")] use tokio1_boring::SslStream as Tokio1SslStream; #[cfg(feature = "tokio1")] @@ -350,7 +352,6 @@ impl AsyncNetworkStream { #[cfg(feature = "tokio1-rustls-tls")] return { - use rustls::ServerName; use tokio1_rustls::TlsConnector; let domain = ServerName::try_from(domain.as_str()) @@ -358,7 +359,7 @@ impl AsyncNetworkStream { let connector = TlsConnector::from(config); let stream = connector - .connect(domain, tcp_stream) + .connect(domain.to_owned(), tcp_stream) .await .map_err(error::connection)?; Ok(InnerAsyncNetworkStream::Tokio1RustlsTls(stream)) @@ -424,14 +425,13 @@ impl AsyncNetworkStream { #[cfg(feature = "async-std1-rustls-tls")] return { use futures_rustls::TlsConnector; - use rustls::ServerName; let domain = ServerName::try_from(domain.as_str()) .map_err(|_| error::connection("domain isn't a valid DNS name"))?; let connector = TlsConnector::from(config); let stream = connector - .connect(domain, tcp_stream) + .connect(domain.to_owned(), tcp_stream) .await .map_err(error::connection)?; Ok(InnerAsyncNetworkStream::AsyncStd1RustlsTls(stream)) @@ -486,8 +486,7 @@ impl AsyncNetworkStream { .unwrap() .first() .unwrap() - .clone() - .0), + .to_vec()), #[cfg(feature = "tokio1-boring-tls")] InnerAsyncNetworkStream::Tokio1BoringTls(stream) => Ok(stream .ssl() @@ -509,8 +508,7 @@ impl AsyncNetworkStream { .unwrap() .first() .unwrap() - .clone() - .0), + .to_vec()), InnerAsyncNetworkStream::None => panic!("InnerNetworkStream::None must never be built"), } } diff --git a/src/transport/smtp/client/net.rs b/src/transport/smtp/client/net.rs index 37d9795..c2ed429 100644 --- a/src/transport/smtp/client/net.rs +++ b/src/transport/smtp/client/net.rs @@ -12,7 +12,7 @@ use boring::ssl::SslStream; #[cfg(feature = "native-tls")] use native_tls::TlsStream; #[cfg(feature = "rustls-tls")] -use rustls::{ClientConnection, ServerName, StreamOwned}; +use rustls::{pki_types::ServerName, ClientConnection, StreamOwned}; use socket2::{Domain, Protocol, Type}; #[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))] @@ -189,7 +189,7 @@ impl NetworkStream { InnerTlsParameters::RustlsTls(connector) => { let domain = ServerName::try_from(tls_parameters.domain()) .map_err(|_| error::connection("domain isn't a valid DNS name"))?; - let connection = ClientConnection::new(Arc::clone(connector), domain) + let connection = ClientConnection::new(Arc::clone(connector), domain.to_owned()) .map_err(error::connection)?; let stream = StreamOwned::new(connection, tcp_stream); InnerNetworkStream::RustlsTls(stream) @@ -241,8 +241,7 @@ impl NetworkStream { .unwrap() .first() .unwrap() - .clone() - .0), + .to_vec()), #[cfg(feature = "boring-tls")] InnerNetworkStream::BoringTls(stream) => Ok(stream .ssl() diff --git a/src/transport/smtp/client/tls.rs b/src/transport/smtp/client/tls.rs index abc926e..09cce56 100644 --- a/src/transport/smtp/client/tls.rs +++ b/src/transport/smtp/client/tls.rs @@ -1,6 +1,6 @@ use std::fmt::{self, Debug}; #[cfg(feature = "rustls-tls")] -use std::{sync::Arc, time::SystemTime}; +use std::{io, sync::Arc}; #[cfg(feature = "boring-tls")] use boring::{ @@ -11,8 +11,10 @@ use boring::{ use native_tls::{Protocol, TlsConnector}; #[cfg(feature = "rustls-tls")] use rustls::{ - client::{ServerCertVerified, ServerCertVerifier, WebPkiVerifier}, - ClientConfig, Error as TlsError, RootCertStore, ServerName, + client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}, + crypto::{verify_tls12_signature, verify_tls13_signature}, + pki_types::{CertificateDer, ServerName, UnixTime}, + ClientConfig, DigitallySignedStruct, Error as TlsError, RootCertStore, SignatureScheme, }; #[cfg(any(feature = "native-tls", feature = "rustls-tls", feature = "boring-tls"))] @@ -337,8 +339,6 @@ impl TlsParametersBuilder { #[cfg(feature = "rustls-tls")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-tls")))] pub fn build_rustls(self) -> Result { - let tls = ClientConfig::builder(); - let just_version3 = &[&rustls::version::TLS13]; let supported_versions = match self.min_tls_version { TlsVersion::Tlsv10 => { @@ -351,50 +351,28 @@ impl TlsParametersBuilder { TlsVersion::Tlsv13 => just_version3, }; - let tls = tls - .with_safe_default_cipher_suites() - .with_safe_default_kx_groups() - .with_protocol_versions(supported_versions) - .map_err(error::tls)?; + let tls = ClientConfig::builder_with_protocol_versions(supported_versions); let tls = if self.accept_invalid_certs { - tls.with_custom_certificate_verifier(Arc::new(InvalidCertsVerifier {})) + tls.dangerous() + .with_custom_certificate_verifier(Arc::new(InvalidCertsVerifier {})) } else { let mut root_cert_store = RootCertStore::empty(); #[cfg(feature = "rustls-native-certs")] fn load_native_roots(store: &mut RootCertStore) -> Result<(), Error> { let native_certs = rustls_native_certs::load_native_certs().map_err(error::tls)?; - let mut valid_count = 0; - let mut invalid_count = 0; - for cert in native_certs { - match store.add(&rustls::Certificate(cert.0)) { - Ok(_) => valid_count += 1, - Err(err) => { - #[cfg(feature = "tracing")] - tracing::debug!("certificate parsing failed: {:?}", err); - invalid_count += 1; - } - } - } + let (added, ignored) = store.add_parsable_certificates(native_certs); #[cfg(feature = "tracing")] tracing::debug!( - "loaded platform certs with {valid_count} valid and {invalid_count} invalid certs" + "loaded platform certs with {added} valid and {ignored} ignored (invalid) certs" ); Ok(()) } #[cfg(feature = "rustls-tls")] fn load_webpki_roots(store: &mut RootCertStore) { - // TODO: handle this in the rustls 0.22 upgrade - #[allow(deprecated)] - store.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); + store.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); } match self.cert_store { @@ -412,14 +390,11 @@ impl TlsParametersBuilder { } for cert in self.root_certs { for rustls_cert in cert.rustls { - root_cert_store.add(&rustls_cert).map_err(error::tls)?; + root_cert_store.add(rustls_cert).map_err(error::tls)?; } } - tls.with_custom_certificate_verifier(Arc::new(WebPkiVerifier::new( - root_cert_store, - None, - ))) + tls.with_root_certificates(root_cert_store) }; let tls = tls.with_no_client_auth(); @@ -493,7 +468,7 @@ pub struct Certificate { #[cfg(feature = "native-tls")] native_tls: native_tls::Certificate, #[cfg(feature = "rustls-tls")] - rustls: Vec, + rustls: Vec>, #[cfg(feature = "boring-tls")] boring_tls: boring::x509::X509, } @@ -512,7 +487,7 @@ impl Certificate { #[cfg(feature = "native-tls")] native_tls: native_tls_cert, #[cfg(feature = "rustls-tls")] - rustls: vec![rustls::Certificate(der)], + rustls: vec![der.into()], #[cfg(feature = "boring-tls")] boring_tls: boring_tls_cert, }) @@ -532,10 +507,8 @@ impl Certificate { let mut pem = Cursor::new(pem); rustls_pemfile::certs(&mut pem) + .collect::>>() .map_err(|_| error::tls("invalid certificates"))? - .into_iter() - .map(rustls::Certificate) - .collect::>() }; Ok(Self { @@ -556,19 +529,53 @@ impl Debug for Certificate { } #[cfg(feature = "rustls-tls")] +#[derive(Debug)] struct InvalidCertsVerifier; #[cfg(feature = "rustls-tls")] impl ServerCertVerifier for InvalidCertsVerifier { fn verify_server_cert( &self, - _end_entity: &rustls::Certificate, - _intermediates: &[rustls::Certificate], - _server_name: &ServerName, - _scts: &mut dyn Iterator, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, _ocsp_response: &[u8], - _now: SystemTime, + _now: UnixTime, ) -> Result { Ok(ServerCertVerified::assertion()) } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls12_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result { + verify_tls13_signature( + message, + cert, + dss, + &rustls::crypto::ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + rustls::crypto::ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } }