Skip to content

Commit

Permalink
feat(rustls)!: change how crypto providers are selected
Browse files Browse the repository at this point in the history
  • Loading branch information
jbr committed Apr 12, 2024
1 parent 36f8106 commit 9dda246
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 13 deletions.
3 changes: 2 additions & 1 deletion rustls/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ categories = ["web-programming::http-server", "web-programming"]
[features]
default = ["platform-verifier", "aws-lc-rs", "client", "server", "tls12"]
aws-lc-rs = ["futures-rustls/aws-lc-rs"]
custom-crypto-provider = []
fips = ["futures-rustls/fips"]
ring = ["futures-rustls/ring"]
tls12 = ["futures-rustls/tls12"]
Expand All @@ -28,7 +29,7 @@ features = ["logging"]
[dependencies]
log = "0.4.21"
rustls-pemfile = { version = "2.1.1", optional = true }
rustls-platform-verifier = { version = "0.3.0", optional = true }
rustls-platform-verifier = { version = "0.3.1", optional = true }
trillium-server-common = { path = "../server-common", version = "0.5.2" }
webpki-roots = { version = "0.26", optional = true }

Expand Down
32 changes: 24 additions & 8 deletions rustls/src/client.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
use crate::crypto_provider;
use futures_rustls::{
client::TlsStream,
rustls::{pki_types::ServerName, ClientConfig, ClientConnection},
rustls::{
client::danger::ServerCertVerifier, crypto::CryptoProvider, pki_types::ServerName,
ClientConfig, ClientConnection,
},
TlsConnector,
};
use std::{
Expand Down Expand Up @@ -47,17 +51,29 @@ impl Default for RustlsClientConfig {
}

#[cfg(feature = "platform-verifier")]
fn default_client_config() -> ClientConfig {
rustls_platform_verifier::tls_config()
fn verifier(provider: Arc<CryptoProvider>) -> Arc<dyn ServerCertVerifier> {
Arc::new(rustls_platform_verifier::Verifier::new().with_provider(provider))
}

#[cfg(not(feature = "platform-verifier"))]
fn default_client_config() -> ClientConfig {
let webpki_roots = futures_rustls::rustls::RootCertStore::from_iter(
fn verifier(provider: Arc<CryptoProvider>) -> Arc<dyn ServerCertVerifier> {
let roots = Arc::new(futures_rustls::rustls::RootCertStore::from_iter(
webpki_roots::TLS_SERVER_ROOTS.iter().cloned(),
);
ClientConfig::builder()
.with_root_certificates(webpki_roots)
));
futures_rustls::rustls::client::WebPkiServerVerifier::builder_with_provider(roots, provider)
.build()
.unwrap()
}

fn default_client_config() -> ClientConfig {
let provider = crypto_provider();
let verifier = verifier(Arc::clone(&provider));

ClientConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()
.expect("crypto provider did not support safe default protocol versions")
.dangerous()
.with_custom_certificate_verifier(verifier)
.with_no_client_auth()
}

Expand Down
48 changes: 48 additions & 0 deletions rustls/src/crypto_provider.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use futures_rustls::rustls::crypto::CryptoProvider;
use std::sync::Arc;

#[cfg(feature = "aws-lc-rs")]
pub(crate) fn crypto_provider() -> Arc<CryptoProvider> {
#[cfg(any(feature = "ring", feature = "custom-crypto-provider"))]
log::error!("multiple crypto provider features enabled, choosing aws-lc-rs");

futures_rustls::rustls::crypto::aws_lc_rs::default_provider().into()
}

#[cfg(all(not(feature = "aws-lc-rs"), feature = "ring"))]
pub(crate) fn crypto_provider() -> Arc<CryptoProvider> {
#[cfg(feature = "custom-crypto-provider")]
log::error!("multiple crypto provider features enabled, choosing ring");
futures_rustls::rustls::crypto::ring::default_provider().into()
}

#[cfg(all(
not(any(feature = "aws-lc-rs", feature = "ring")),
feature = "custom-crypto-provider"
))]
pub(crate) fn crypto_provider() -> Arc<CryptoProvider> {
CryptoProvider::get_default()
.expect(concat!(
"`custom-crypto-provider` feature was enabled, but no default crypto ",
"provider was found. Either configure a ClientConfig::builder_with_provider and pass",
"it to `trillium_rustls::RustlsConfig::new` or use `CryptoProvider::install_default`."
))
.clone()
}

#[cfg(not(any(
feature = "ring",
feature = "aws-lc-rs",
feature = "custom-crypto-provider"
)))]
pub(crate) fn crypto_provider() -> Arc<CryptoProvider> {
compile_error!(
"\n\n`trillium-rustls` cannot compile without a crypto provider feature enabled.
Please enable `ring`, `aws-lc-rs`, or `custom-crypto-provider`.
To use ring or aws-lc-rs, nothing further is needed than enabling the feature.
To use `custom-crypto-provider`, either configure a `ClientConfig::builder_with_provider` \
and pass it to `trillium_rustls::RustlsConfig::new` or use \
`CryptoProvider::install_default` before building the trillium_rustls::RustlsConfig::default().\n\n"
)
}
63 changes: 60 additions & 3 deletions rustls/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,62 @@
unused_qualifications
)]

/*!
This crate provides rustls trait implementations for trillium
client ([`RustlsConnector`]) and server ([`RustlsAcceptor`]).
/*! This crate provides rustls trait implementations for trillium client ([`RustlsConnector`]) and
server ([`RustlsAcceptor`]).
# Cargo Features
This crate's default features should be appropriate for most users. To pare down on dependencies or
customize trillium-rustls' usage of rustls, opt out of default features and reenable the appropriate
features for your use case.
## `server` and `client` features
This crate offers a `server` feature and a `client` feature. Opting out of default features allows
you to avoid building any dependencies for the unused other component. By default, both `server` and
`client` features are enabled.
## Cryptographic backend selection
Rustls supports pluggable cryptographic backends as well as a process-default cryptographic
cryptographic backend. There are two built-in feature-enabled cryptographic backends and other
community provided cryptographic backends.
⚠️ There are three cryptographic backend cargo features, and they behave differently than the rustls
features. Please read the following section.⚠️
`trillium-rustls` tries to avoid runtime panics where possible, so compiling this crate without a
valid cryptographic backend will result in a compile time error. To opt into rustls's default
process-default behavior, enable `custom-crypto-provider` as described below. Enabling multiple
crypto providers will select exactly one of them at compile time in the following order:
### `aws-lc-rs`
This is the default cryptographic backend in concordance with rustls' default. This backend will be
selected if the feature is enabled. If either of the other two cryptographic backends are selected,
trillium-rustls will log an error but use `aws-lc-rs`.
### `ring`
If this feature is enabled, this backend will be selected even if `custom-crypto-provider` is also
enabled.
### `custom-crypto-provider`
In order to use a crypto provider other than the above two options, enable the
`custom-crypto-provider` feature and either configure a
[`trillium_rustls::rustls::ClientConfig`][rustls::ClientConfig] or
[`trillium_rustls::rustls::ServerConfig`][rustls::ServerConfig] yourself to convert the equivalent
`trillium-rustls` type, or install a custom process-default crypto provider with
[`trillium_rustls::rustls::crypto::CryptoProvider::install_default`][rustls::crypto::CryptoProvider::install_default]
prior to executing trillium-rustls code.
## Client verifier
This crate offers a `platform-verifier` feature for client usage that builds a ClientConfig with the
selected cryptographic backend and uses
[`rustls-platform-verifier`](https://docs.rs/rustls-platform-verifier/). This feature is enabled by
default. If you disable the feature, [`webpki_roots`] will be used.
*/

#[cfg(feature = "client")]
Expand All @@ -26,3 +79,7 @@ pub use server::{RustlsAcceptor, RustlsServerTransport};

pub use futures_rustls;
pub use futures_rustls::rustls;

#[cfg(any(feature = "client", feature = "server"))]
mod crypto_provider;
pub(crate) use crypto_provider::crypto_provider;
6 changes: 5 additions & 1 deletion rustls/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use std::{
};
use trillium_server_common::{async_trait, Acceptor, AsyncRead, AsyncWrite, Transport};

use crate::crypto_provider;

/**
trillium [`Acceptor`] for Rustls
*/
Expand Down Expand Up @@ -69,7 +71,9 @@ impl RustlsAcceptor {
.expect("could not read key pemfile")
.expect("no private key found in `key`");

ServerConfig::builder()
ServerConfig::builder_with_provider(crypto_provider())
.with_safe_default_protocol_versions()
.expect("crypto provider did not support safe default protocol versions")
.with_no_client_auth()
.with_single_cert(cert_chain, key_der)
.expect("could not create a rustls ServerConfig from the supplied cert and key")
Expand Down

0 comments on commit 9dda246

Please sign in to comment.