Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ rustls-tls-manual-roots = ["rustls-tls-manual-roots-no-provider", "__rustls-ring
rustls-tls-webpki-roots = ["rustls-tls-webpki-roots-no-provider", "__rustls-ring"]
rustls-tls-native-roots = ["rustls-tls-native-roots-no-provider", "__rustls-ring"]

rustls-platform-verifier-fallback = ["__rustls", "dep:rustls-platform-verifier"]

blocking = ["dep:futures-channel", "futures-channel?/sink", "dep:futures-util", "futures-util?/io", "futures-util?/sink", "tokio/sync"]

charset = ["dep:encoding_rs", "dep:mime"]
Expand Down Expand Up @@ -153,6 +155,7 @@ rustls = { version = "0.23.4", optional = true, default-features = false, featur
tokio-rustls = { version = "0.26", optional = true, default-features = false, features = ["tls12"] }
webpki-roots = { version = "1", optional = true }
rustls-native-certs = { version = "0.8.0", optional = true }
rustls-platform-verifier = { version = "0.6", optional = true }

## cookies
cookie_crate = { version = "0.18.0", package = "cookie", optional = true }
Expand Down
92 changes: 83 additions & 9 deletions src/async_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,8 @@ impl ClientBuilder {
}
#[cfg(feature = "__rustls")]
TlsBackend::Rustls => {
#[cfg(feature = "rustls-platform-verifier-fallback")]
use crate::tls::FallbackPlatformVerifier;
use crate::tls::{IgnoreHostname, NoVerifier};

// Set root certificates.
Expand Down Expand Up @@ -738,15 +740,67 @@ impl ClientBuilder {
.dangerous()
.with_custom_certificate_verifier(Arc::new(NoVerifier))
} else if !config.hostname_verification {
config_builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(IgnoreHostname::new(
root_cert_store,
signature_algorithms,
)))
let ignore_hostname_verifier =
Arc::new(IgnoreHostname::new(root_cert_store, signature_algorithms));

#[cfg(feature = "rustls-platform-verifier-fallback")]
{
let fallback_verifier =
FallbackPlatformVerifier::with_platform_fallback(
ignore_hostname_verifier,
provider,
)
.map_err(|e| {
crate::error::builder(format!(
"failed to create platform verifier: {e:?}"
))
})?;
config_builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(fallback_verifier))
}
#[cfg(not(feature = "rustls-platform-verifier-fallback"))]
{
config_builder
.dangerous()
.with_custom_certificate_verifier(ignore_hostname_verifier)
}
} else {
if config.crls.is_empty() {
config_builder.with_root_certificates(root_cert_store)
#[cfg(feature = "rustls-platform-verifier-fallback")]
{
let standard_verifier =
rustls::client::WebPkiServerVerifier::builder_with_provider(
Arc::new(root_cert_store),
provider.clone(),
)
.allow_unknown_revocation_status()
.build()
.map_err(|e| {
crate::error::builder(format!(
"invalid TLS verification settings: {e:?}"
))
})?;

let fallback_verifier =
FallbackPlatformVerifier::with_platform_fallback(
standard_verifier,
provider,
)
.map_err(|e| {
crate::error::builder(format!(
"failed to create platform verifier: {e:?}"
))
})?;

config_builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(fallback_verifier))
}
#[cfg(not(feature = "rustls-platform-verifier-fallback"))]
{
config_builder.with_root_certificates(root_cert_store)
}
} else {
let crls = config
.crls
Expand All @@ -756,14 +810,34 @@ impl ClientBuilder {
let verifier =
rustls::client::WebPkiServerVerifier::builder_with_provider(
Arc::new(root_cert_store),
provider,
provider.clone(),
)
.with_crls(crls)
.build()
.map_err(|_| {
crate::error::builder("invalid TLS verification settings")
})?;
config_builder.with_webpki_verifier(verifier)

#[cfg(feature = "rustls-platform-verifier-fallback")]
{
let fallback_verifier =
FallbackPlatformVerifier::with_platform_fallback(
verifier, provider,
)
.map_err(|e| {
crate::error::builder(format!(
"failed to create platform verifier: {e:?}"
))
})?;

config_builder
.dangerous()
.with_custom_certificate_verifier(Arc::new(fallback_verifier))
}
#[cfg(not(feature = "rustls-platform-verifier-fallback"))]
{
config_builder.with_webpki_verifier(verifier)
}
}
};

Expand Down
82 changes: 82 additions & 0 deletions src/tls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ use rustls::{
use rustls_pki_types::pem::PemObject;
#[cfg(feature = "__rustls")]
use rustls_pki_types::{ServerName, UnixTime};
#[cfg(feature = "rustls-platform-verifier-fallback")]
use std::sync::Arc;
use std::{
fmt,
io::{BufRead, BufReader},
Expand Down Expand Up @@ -722,6 +724,86 @@ impl ServerCertVerifier for IgnoreHostname {
}
}

/// A certificate verifier that tries a primary verifier first,
/// and falls back to a platform verifier if the primary fails.
#[cfg(feature = "rustls-platform-verifier-fallback")]
#[derive(Debug)]
pub(crate) struct FallbackPlatformVerifier {
primary: Arc<dyn ServerCertVerifier>,
fallback: Arc<dyn ServerCertVerifier>,
}

#[cfg(feature = "rustls-platform-verifier-fallback")]
impl FallbackPlatformVerifier {
pub(crate) fn with_platform_fallback(
primary: Arc<dyn ServerCertVerifier>,
provider: Arc<rustls::crypto::CryptoProvider>,
) -> Result<Self, TLSError> {
let fallback = Arc::new(rustls_platform_verifier::Verifier::new(provider)?);
Ok(Self { primary, fallback })
}
}

#[cfg(feature = "rustls-platform-verifier-fallback")]
impl ServerCertVerifier for FallbackPlatformVerifier {
fn verify_server_cert(
&self,
end_entity: &rustls_pki_types::CertificateDer<'_>,
intermediates: &[rustls_pki_types::CertificateDer<'_>],
server_name: &ServerName<'_>,
ocsp_response: &[u8],
now: UnixTime,
) -> Result<ServerCertVerified, TLSError> {
match self.primary.verify_server_cert(
end_entity,
intermediates,
server_name,
ocsp_response,
now,
) {
Ok(verified) => Ok(verified),
Err(primary_err) => {
match self.fallback.verify_server_cert(
end_entity,
intermediates,
server_name,
ocsp_response,
now,
) {
Ok(verified) => Ok(verified),
Err(_) => Err(primary_err),
}
}
}
}

fn verify_tls12_signature(
&self,
message: &[u8],
cert: &rustls_pki_types::CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
// Both use rustls::crypto::verify_tls12_signature
self.primary.verify_tls12_signature(message, cert, dss)
}

fn verify_tls13_signature(
&self,
message: &[u8],
cert: &rustls_pki_types::CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, TLSError> {
// Both use rustls::crypto::verify_tls13_signature
self.primary.verify_tls13_signature(message, cert, dss)
}

fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
// Both verifiers use the same CryptoProvider, so they support the same
// signature schemes. Just return the primary's schemes.
self.primary.supported_verify_schemes()
}
}

/// Hyper extension carrying extra TLS layer information.
/// Made available to clients on responses when `tls_info` is set.
#[derive(Clone)]
Expand Down
Loading