From 117d7ebd6b016a4e78d7e8316e360b85866d4956 Mon Sep 17 00:00:00 2001 From: Stephane Segning Lambou Date: Tue, 22 Sep 2026 06:54:12 +0200 Subject: [PATCH] feat(tls): add an async-tls-rustls-no-provider backend Closes #1. Every async rustls path currently hard-selects a crypto provider in code - `build_rustls_ring_transport` passes `rustls::crypto::ring::default_provider()` into `builder_with_provider`, with a parallel pair for aws-lc-rs. Because Cargo feature unification is additive, a downstream crate cannot switch that off, so enabling rustls at all means linking a C/assembly crypto library. This adds a third rustls backend that takes the provider from the process-wide default the application installed, leaving the choice to the caller: a pure-Rust provider such as `rustls-graviola`, `aws-lc-rs` in FIPS mode, or anything else. `TlsBackend` is `#[non_exhaustive]`, so the new variant is semver-minor for downstream crates. **The provider is fetched, not assumed.** `build_rustls_no_provider_transport` calls `CryptoProvider::get_default()` and returns `Error::TlsBackendInit` with a message naming `install_default` when nothing is installed. Using `rustls::ClientConfig::builder()` would have been shorter, but it *panics* in exactly that case - a returned error is the point of the feature. Two `match`es in `src/blocking_client/transport.rs` are exhaustive over `TlsBackend` (`#[non_exhaustive]` constrains downstream crates, not this one), so they needed the new variant to keep compiling under any blocking feature. Both are handled explicitly rather than with a `_` arm - `backend_is_available` returns false and `build_sync_tls_config` returns `TlsBackendUnavailable`, since there is no `ureq` counterpart - so a future backend still gets a compile error here instead of silently falling into a catch-all. `DEFAULT_TLS_BACKEND` gains a branch for the case where this is the only feature enabled; ring keeps precedence wherever it is on. Verified on this commit: - `cargo check` passes for no-provider, ring, aws-lc-rs, native, blocking-tls-rustls-ring, and `--all-features`. - `cargo tree --features async-tls-rustls-no-provider -e normal -i ring` has nothing to print, and `-i aws-lc-rs` matches no package. The same query on the ring feature returns `ring v0.17.14`, so the negative result is the query working, not the query being broken. - `cargo fmt --check` clean; `cargo clippy -- -D warnings` clean under both the new feature and the defaults; `cargo test --no-run` builds. --- Cargo.toml | 1 + src/async_client/builder.rs | 1 + src/async_client/mod.rs | 1 + src/async_client/request.rs | 1 + src/async_client/transport.rs | 135 ++++++++++++++++++++++++------ src/blocking_client/transport.rs | 9 ++ src/core/proxy.rs | 3 +- src/core/util/mod.rs | 3 +- src/core/util/transport_error.rs | 28 +++++-- src/http/response.rs | 1 + src/http/response/async_stream.rs | 1 + src/lib.rs | 14 ++++ src/tls.rs | 17 +++- src/upload/asynchronous.rs | 1 + src/upload/mod.rs | 1 + 15 files changed, 180 insertions(+), 37 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4c90027..9c39663 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ resumable-upload = ["dep:md5", "dep:sha2", "serde/derive"] # selected at runtime with `ClientBuilder::tls_backend`. async-tls-rustls-aws-lc-rs = ["_async", "dep:hyper-rustls", "dep:rustls", "dep:rustls-native-certs", "dep:webpki-roots", "hyper-rustls/aws-lc-rs", "rustls/aws_lc_rs"] async-tls-rustls-ring = ["_async", "dep:hyper-rustls", "dep:rustls", "dep:rustls-native-certs", "dep:webpki-roots", "hyper-rustls/ring", "rustls/ring"] +async-tls-rustls-no-provider = ["_async", "dep:hyper-rustls", "dep:rustls", "dep:rustls-native-certs", "dep:webpki-roots"] async-tls-native = ["_async", "dep:hyper-tls", "hyper-tls/alpn"] blocking-tls-rustls-ring = ["_blocking", "dep:rustls", "dep:rustls-native-certs", "dep:webpki-root-certs", "ureq/rustls", "ureq/platform-verifier"] blocking-tls-rustls-aws-lc-rs = ["_blocking", "dep:rustls", "dep:rustls-native-certs", "dep:webpki-root-certs", "ureq/rustls-no-provider", "ureq/rustls-webpki-roots", "ureq/platform-verifier", "rustls/aws_lc_rs"] diff --git a/src/async_client/builder.rs b/src/async_client/builder.rs index 2061351..683c4ee 100644 --- a/src/async_client/builder.rs +++ b/src/async_client/builder.rs @@ -54,6 +54,7 @@ const DEFAULT_MAX_RESPONSE_BODY_BYTES: usize = 8 * 1024 * 1024; doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] diff --git a/src/async_client/mod.rs b/src/async_client/mod.rs index 7b6dcd4..e3aadba 100644 --- a/src/async_client/mod.rs +++ b/src/async_client/mod.rs @@ -32,6 +32,7 @@ use transport::TransportClient; doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] diff --git a/src/async_client/request.rs b/src/async_client/request.rs index 78d6f8b..4a817a2 100644 --- a/src/async_client/request.rs +++ b/src/async_client/request.rs @@ -63,6 +63,7 @@ enum ContentLengthSource { doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] diff --git a/src/async_client/transport.rs b/src/async_client/transport.rs index 9127437..1175271 100644 --- a/src/async_client/transport.rs +++ b/src/async_client/transport.rs @@ -1,6 +1,7 @@ #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use std::sync::Arc; use std::time::Duration; @@ -9,24 +10,28 @@ use http::{Request, Response as HttpResponse}; use hyper::body::Incoming; #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use hyper_rustls::HttpsConnectorBuilder; #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use hyper_util::client::legacy::Client as HyperClient; #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use hyper_util::rt::TokioExecutor; #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use tracing::warn; @@ -35,7 +40,8 @@ use crate::error::Error; #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use crate::error::transport_error; use crate::execution::RequestExecutionState; @@ -43,26 +49,30 @@ use crate::proxy::ProxyConnector; #[cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" ))] use crate::tls::tls_config_error; use crate::tls::{TlsBackend, TlsOptions}; #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use crate::tls::{TlsClientIdentity, TlsRootCertificate, TlsRootStore, TlsVersion}; #[cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" ))] use crate::tls::{parse_pem_certificate_blocks, tls_version_bounds}; #[cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" ))] use crate::util::classify_transport_error; use crate::util::duration_millis_ceil; @@ -80,10 +90,18 @@ const DEFAULT_TLS_BACKEND: TlsBackend = TlsBackend::RustlsAwsLcRs; feature = "async-tls-native" ))] const DEFAULT_TLS_BACKEND: TlsBackend = TlsBackend::NativeTls; +#[cfg(all( + not(feature = "async-tls-rustls-ring"), + not(feature = "async-tls-rustls-aws-lc-rs"), + not(feature = "async-tls-native"), + feature = "async-tls-rustls-no-provider" +))] +const DEFAULT_TLS_BACKEND: TlsBackend = TlsBackend::RustlsNoProvider; #[cfg(not(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" )))] const DEFAULT_TLS_BACKEND: TlsBackend = TlsBackend::RustlsRing; @@ -93,7 +111,8 @@ pub(super) fn default_tls_backend() -> TlsBackend { #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn add_custom_rustls_root_certificates( tls_backend: TlsBackend, @@ -145,7 +164,8 @@ fn add_custom_rustls_root_certificates( #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn build_rustls_root_store( tls_backend: TlsBackend, @@ -214,7 +234,8 @@ fn build_rustls_root_store( #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] pub(crate) fn configured_rustls_protocol_versions( tls_backend: TlsBackend, @@ -237,7 +258,8 @@ pub(crate) fn configured_rustls_protocol_versions( #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn build_rustls_tls_config( tls_backend: TlsBackend, @@ -310,12 +332,14 @@ fn build_rustls_tls_config( #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] type RustlsHttpsConnector = hyper_rustls::HttpsConnector; #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] type RustlsHyperClient = HyperClient; @@ -328,7 +352,8 @@ type NativeHyperClient = HyperClient; pub(super) enum TransportClient { #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] Rustls(RustlsHyperClient), #[cfg(feature = "async-tls-native")] @@ -343,13 +368,15 @@ impl TransportClient { #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] { match self { #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] Self::Rustls(client) => client .request(request) @@ -365,7 +392,8 @@ impl TransportClient { #[cfg(not(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" )))] { let _ = self; @@ -381,14 +409,16 @@ pub(super) enum TransportRequestError { #[cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" ))] Transport(hyper_util::client::legacy::Error), Timeout, #[cfg(not(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" )))] TlsBackendUnavailable { backend: &'static str, @@ -405,7 +435,8 @@ impl TransportRequestError { #[cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" ))] Self::Transport(source) => transport_error( classify_transport_error(&source), @@ -419,7 +450,8 @@ impl TransportRequestError { #[cfg(not(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", - feature = "async-tls-native" + feature = "async-tls-native", + feature = "async-tls-rustls-no-provider" )))] Self::TlsBackendUnavailable { backend } => Error::TlsBackendUnavailable { backend }, } @@ -506,6 +538,52 @@ fn build_rustls_aws_lc_rs_transport( }) } +#[cfg(feature = "async-tls-rustls-no-provider")] +fn build_rustls_no_provider_transport( + connector: ProxyConnector, + tls_options: &TlsOptions, + pool_idle_timeout: Duration, + pool_max_idle_per_host: usize, + http2_only: bool, +) -> crate::Result { + let provider = rustls::crypto::CryptoProvider::get_default() + .cloned() + .ok_or_else(|| Error::TlsBackendInit { + backend: TlsBackend::RustlsNoProvider.as_str(), + message: "no rustls CryptoProvider is installed as the process-wide default; \ + install one (e.g. rustls-graviola) via \ + rustls::crypto::CryptoProvider::install_default(...) before building a client \ + with the async-tls-rustls-no-provider backend" + .to_string(), + })?; + let tls_config = build_rustls_tls_config(TlsBackend::RustlsNoProvider, provider, tls_options)?; + let https = HttpsConnectorBuilder::new() + .with_tls_config(tls_config) + .https_or_http() + .enable_http1() + .enable_http2() + .wrap_connector(connector); + let transport = HyperClient::builder(TokioExecutor::new()) + .pool_idle_timeout(pool_idle_timeout) + .pool_max_idle_per_host(pool_max_idle_per_host) + .http2_only(http2_only) + .build(https); + Ok(TransportClient::Rustls(transport)) +} + +#[cfg(not(feature = "async-tls-rustls-no-provider"))] +fn build_rustls_no_provider_transport( + _connector: ProxyConnector, + _tls_options: &TlsOptions, + _pool_idle_timeout: Duration, + _pool_max_idle_per_host: usize, + _http2_only: bool, +) -> crate::Result { + Err(Error::TlsBackendUnavailable { + backend: TlsBackend::RustlsNoProvider.as_str(), + }) +} + #[cfg(feature = "async-tls-native")] fn native_tls_protocol(version: TlsVersion) -> hyper_tls::native_tls::Protocol { match version { @@ -702,6 +780,13 @@ pub(super) fn build_transport_client( pool_max_idle_per_host, http2_only, ), + TlsBackend::RustlsNoProvider => build_rustls_no_provider_transport( + connector, + tls_options, + pool_idle_timeout, + pool_max_idle_per_host, + http2_only, + ), TlsBackend::NativeTls => build_native_tls_transport( connector, tls_options, diff --git a/src/blocking_client/transport.rs b/src/blocking_client/transport.rs index 00b25ee..a974b09 100644 --- a/src/blocking_client/transport.rs +++ b/src/blocking_client/transport.rs @@ -47,6 +47,8 @@ pub(super) fn backend_is_available(backend: TlsBackend) -> bool { TlsBackend::RustlsRing => cfg!(feature = "blocking-tls-rustls-ring"), TlsBackend::RustlsAwsLcRs => cfg!(feature = "blocking-tls-rustls-aws-lc-rs"), TlsBackend::NativeTls => cfg!(feature = "blocking-tls-native"), + // `async-tls-rustls-no-provider` has no blocking `ureq` counterpart. + TlsBackend::RustlsNoProvider => false, } } @@ -179,6 +181,13 @@ fn build_sync_tls_config( let provider = match backend { TlsBackend::RustlsRing | TlsBackend::RustlsAwsLcRs => ureq::tls::TlsProvider::Rustls, TlsBackend::NativeTls => ureq::tls::TlsProvider::NativeTls, + // Unreachable in practice: `backend_is_available` above already + // rejects `RustlsNoProvider`, which has no blocking `ureq` counterpart. + TlsBackend::RustlsNoProvider => { + return Err(crate::error::Error::TlsBackendUnavailable { + backend: backend.as_str(), + }); + } }; let mut tls_config_builder = ureq::tls::TlsConfig::builder().provider(provider); diff --git a/src/core/proxy.rs b/src/core/proxy.rs index cb259fc..78b65a3 100644 --- a/src/core/proxy.rs +++ b/src/core/proxy.rs @@ -3,7 +3,8 @@ feature = "_async", not(feature = "async-tls-native"), not(feature = "async-tls-rustls-ring"), - not(feature = "async-tls-rustls-aws-lc-rs") + not(feature = "async-tls-rustls-aws-lc-rs"), + not(feature = "async-tls-rustls-no-provider") ), allow(dead_code) )] diff --git a/src/core/util/mod.rs b/src/core/util/mod.rs index 9725437..7fcd013 100644 --- a/src/core/util/mod.rs +++ b/src/core/util/mod.rs @@ -50,7 +50,8 @@ pub(crate) use timing::{duration_from_millis_saturating, saturating_u64_to_usize #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] pub(crate) use transport_error::classify_transport_error; #[cfg(feature = "_blocking")] diff --git a/src/core/util/transport_error.rs b/src/core/util/transport_error.rs index 7bf8564..622179c 100644 --- a/src/core/util/transport_error.rs +++ b/src/core/util/transport_error.rs @@ -3,7 +3,8 @@ all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use std::io; @@ -12,7 +13,8 @@ use std::io; all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] use crate::error::TransportErrorKind; @@ -30,7 +32,8 @@ pub(crate) fn is_timeout_io_error(error: &io::Error) -> bool { #[cfg(any( feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] pub(crate) fn classify_transport_error( error: &hyper_util::client::legacy::Error, @@ -53,7 +56,8 @@ pub(crate) fn classify_transport_error( all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn classify_transport_error_source_chain( error: &(dyn std::error::Error + 'static), @@ -76,7 +80,8 @@ fn classify_transport_error_source_chain( all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn classify_transport_error_source( error: &(dyn std::error::Error + 'static), @@ -110,7 +115,8 @@ fn classify_transport_error_source( all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] pub(crate) fn classify_io_transport_error_kind( kind: io::ErrorKind, @@ -141,7 +147,8 @@ pub(crate) fn classify_io_transport_error_kind( all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn classify_transport_error_text(text: &str, is_connect_path: bool) -> TransportErrorKind { const DNS_MARKERS: &[&str] = &[ @@ -221,7 +228,8 @@ fn classify_transport_error_text(text: &str, is_connect_path: bool) -> Transport all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn contains_marker(text: &str, markers: &[&str]) -> bool { markers.iter().any(|marker| text.contains(marker)) @@ -231,7 +239,8 @@ fn contains_marker(text: &str, markers: &[&str]) -> bool { all(test, feature = "_async"), feature = "async-tls-native", feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] fn contains_word(text: &str, word: &str) -> bool { text.split(|character: char| !character.is_ascii_alphanumeric()) @@ -263,6 +272,7 @@ pub(crate) fn is_tls_error(error: &(dyn std::error::Error + 'static)) -> bool { #[cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" ))] diff --git a/src/http/response.rs b/src/http/response.rs index a4e171b..8b36e9e 100644 --- a/src/http/response.rs +++ b/src/http/response.rs @@ -343,6 +343,7 @@ mod blocking_stream; doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] diff --git a/src/http/response/async_stream.rs b/src/http/response/async_stream.rs index 937aaa5..ec50de3 100644 --- a/src/http/response/async_stream.rs +++ b/src/http/response/async_stream.rs @@ -448,6 +448,7 @@ impl std::fmt::Debug for StreamBody { doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] diff --git a/src/lib.rs b/src/lib.rs index a2427f1..c2932ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,6 +80,8 @@ //! //! - Async + `rustls` + `ring`: `async-tls-rustls-ring` //! - Async + `rustls` + `aws-lc-rs`: `async-tls-rustls-aws-lc-rs` +//! - Async + `rustls` + a caller-installed crypto provider (e.g. the +//! pure-Rust `rustls-graviola`): `async-tls-rustls-no-provider` //! - Async + `native-tls`: `async-tls-native` //! - Blocking + `ureq` + `rustls` + `ring`: `blocking-tls-rustls-ring` //! - Blocking + `ureq` + `rustls` + `aws-lc-rs`: `blocking-tls-rustls-aws-lc-rs` @@ -132,12 +134,18 @@ //! blocks; pass private keys through the dedicated identity key parameter. //! - Blocking `native-tls` cannot merge custom root CAs into the system trust //! store; use [`TlsRootStore::Specific`] when adding explicit roots there. +//! - `async-tls-rustls-no-provider` does not select a crypto provider itself; +//! the application must install one as the process-wide rustls default +//! (e.g. `rustls::crypto::CryptoProvider::install_default(...)`) before +//! building a client. If none is installed, building a client returns +//! [`Error::TlsBackendInit`] instead of panicking. #[cfg(all( feature = "_async", not(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" )) ))] @@ -197,6 +205,7 @@ pub(crate) use crate::http::response; doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] @@ -208,6 +217,7 @@ pub use crate::error::{Error, ErrorCode, TimeoutPhase, TransportErrorKind}; doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] @@ -219,6 +229,7 @@ pub use crate::response::Response; doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] @@ -265,6 +276,7 @@ pub mod prelude { doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] @@ -278,6 +290,7 @@ pub mod prelude { doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] @@ -303,6 +316,7 @@ pub mod advanced { any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ) ))) diff --git a/src/tls.rs b/src/tls.rs index 15b1f03..1683ab0 100644 --- a/src/tls.rs +++ b/src/tls.rs @@ -5,6 +5,7 @@ use std::fmt; feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -19,6 +20,11 @@ pub enum TlsBackend { RustlsRing, /// `rustls` backed by the `aws-lc-rs` crypto provider. RustlsAwsLcRs, + /// `rustls` backed by a crypto provider installed by the caller as the + /// process-wide default (via `rustls::crypto::CryptoProvider::install_default`), + /// e.g. the pure-Rust `rustls-graviola` provider, instead of a provider + /// hard-selected by this crate. + RustlsNoProvider, /// The platform-native TLS stack exposed by `native-tls`. NativeTls, } @@ -29,6 +35,7 @@ impl TlsBackend { match self { Self::RustlsRing => "rustls-ring", Self::RustlsAwsLcRs => "rustls-aws-lc-rs", + Self::RustlsNoProvider => "rustls-no-provider", Self::NativeTls => "native-tls", } } @@ -159,6 +166,7 @@ impl fmt::Debug for TlsOptions { feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -170,7 +178,8 @@ pub(crate) struct TlsVersionBounds { #[cfg(any( feature = "async-tls-rustls-ring", - feature = "async-tls-rustls-aws-lc-rs" + feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider" ))] impl TlsVersionBounds { pub(crate) fn contains(self, version: TlsVersion) -> bool { @@ -183,6 +192,7 @@ impl TlsVersionBounds { feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -215,6 +225,7 @@ pub(crate) fn tls_version_bounds( feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -231,6 +242,7 @@ pub(crate) fn tls_config_error(backend: TlsBackend, message: impl Into) feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -249,6 +261,7 @@ fn find_subslice(haystack: &[u8], needle: &[u8]) -> Option { feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -262,6 +275,7 @@ fn contains_pem_marker(haystack: &[u8], marker: &[u8]) -> bool { feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" @@ -275,6 +289,7 @@ fn is_pem_outer_padding(value: &[u8]) -> bool { feature = "async-tls-native", feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "blocking-tls-native", feature = "blocking-tls-rustls-ring", feature = "blocking-tls-rustls-aws-lc-rs" diff --git a/src/upload/asynchronous.rs b/src/upload/asynchronous.rs index 48b18c8..bf6ac50 100644 --- a/src/upload/asynchronous.rs +++ b/src/upload/asynchronous.rs @@ -13,6 +13,7 @@ use super::{ doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )] diff --git a/src/upload/mod.rs b/src/upload/mod.rs index 6d78766..c225024 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -646,6 +646,7 @@ pub trait BlockingResumableUploadBackend { doc(cfg(any( feature = "async-tls-rustls-ring", feature = "async-tls-rustls-aws-lc-rs", + feature = "async-tls-rustls-no-provider", feature = "async-tls-native" ))) )]