use std::{io, sync::Arc};
use rustls::{
client::{ClientConfig, WebPkiServerVerifier, danger::ServerCertVerifier},
crypto::CryptoProvider,
};
use webpki_types::CertificateDer;
#[derive(Debug, Clone)]
pub struct CaRootsConfig {
mode: Mode,
extra_roots: Vec<CertificateDer<'static>>,
}
#[derive(Debug, Clone)]
enum Mode {
EmbeddedWebPki,
#[cfg(feature = "platform-verifier")]
System,
ExtraRootsOnly,
#[cfg(any(test, feature = "test-utils"))]
InsecureSkipVerify,
}
impl Default for CaRootsConfig {
fn default() -> Self {
Self {
mode: Mode::EmbeddedWebPki,
extra_roots: vec![],
}
}
}
impl CaRootsConfig {
#[cfg(feature = "platform-verifier")]
pub fn system() -> Self {
Self {
mode: Mode::System,
extra_roots: Vec::new(),
}
}
pub fn embedded() -> Self {
Self {
mode: Mode::EmbeddedWebPki,
extra_roots: Vec::new(),
}
}
#[cfg(any(test, feature = "test-utils"))]
pub fn insecure_skip_verify() -> Self {
Self {
mode: Mode::InsecureSkipVerify,
extra_roots: Vec::new(),
}
}
pub fn custom(roots: impl IntoIterator<Item = CertificateDer<'static>>) -> Self {
Self {
mode: Mode::ExtraRootsOnly,
extra_roots: roots.into_iter().collect(),
}
}
pub fn with_extra_roots(
mut self,
extra_roots: impl IntoIterator<Item = CertificateDer<'static>>,
) -> Self {
self.extra_roots.extend(extra_roots);
self
}
pub fn server_cert_verifier(
&self,
crypto_provider: Arc<CryptoProvider>,
) -> io::Result<Arc<dyn ServerCertVerifier>> {
Ok(match self.mode {
#[cfg(feature = "platform-verifier")]
Mode::System => {
#[cfg(not(target_os = "android"))]
let verifier = rustls_platform_verifier::Verifier::new_with_extra_roots(
self.extra_roots.clone(),
crypto_provider,
);
#[cfg(target_os = "android")]
let verifier = rustls_platform_verifier::Verifier::new(crypto_provider);
Arc::new(verifier.map_err(io::Error::other)?)
}
Mode::EmbeddedWebPki => {
let mut root_store = rustls::RootCertStore {
roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
};
root_store.add_parsable_certificates(self.extra_roots.clone());
WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), crypto_provider)
.build()
.map_err(io::Error::other)?
}
Mode::ExtraRootsOnly => {
let mut root_store = rustls::RootCertStore { roots: vec![] };
root_store.add_parsable_certificates(self.extra_roots.clone());
WebPkiServerVerifier::builder_with_provider(Arc::new(root_store), crypto_provider)
.build()
.map_err(io::Error::other)?
}
#[cfg(any(test, feature = "test-utils"))]
Mode::InsecureSkipVerify => {
Arc::new(no_cert_verifier::NoCertVerifier { crypto_provider })
}
})
}
pub fn client_config(&self, crypto_provider: Arc<CryptoProvider>) -> io::Result<ClientConfig> {
let verifier = self.server_cert_verifier(crypto_provider.clone())?;
let config = ClientConfig::builder_with_provider(crypto_provider)
.with_safe_default_protocol_versions()
.expect("protocols supported by ring")
.dangerous()
.with_custom_certificate_verifier(verifier)
.with_no_client_auth();
Ok(config)
}
}
#[cfg(feature = "tls-ring")]
pub fn default_provider() -> Arc<CryptoProvider> {
Arc::new(rustls::crypto::ring::default_provider())
}
#[cfg(all(feature = "tls-aws-lc-rs", not(feature = "tls-ring")))]
pub fn default_provider() -> Arc<CryptoProvider> {
Arc::new(rustls::crypto::aws_lc_rs::default_provider())
}
#[cfg(all(any(test, feature = "test-utils"), with_crypto_provider))]
pub fn make_dangerous_client_config() -> rustls::ClientConfig {
tracing::warn!(
"Insecure config: SSL certificates from relay servers will be trusted without verification"
);
let crypto_provider = crate::tls::default_provider();
rustls::client::ClientConfig::builder_with_provider(crypto_provider.clone())
.with_protocol_versions(&[&rustls::version::TLS13])
.expect("protocols supported by ring")
.dangerous()
.with_custom_certificate_verifier(Arc::new(no_cert_verifier::NoCertVerifier {
crypto_provider,
}))
.with_no_client_auth()
}
#[cfg(any(test, feature = "test-utils"))]
mod no_cert_verifier {
use std::sync::Arc;
use rustls::{
client::danger::{ServerCertVerified, ServerCertVerifier},
crypto::CryptoProvider,
pki_types::{CertificateDer, ServerName},
};
#[derive(Debug)]
pub(super) struct NoCertVerifier {
pub(super) crypto_provider: Arc<CryptoProvider>,
}
impl ServerCertVerifier for NoCertVerifier {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer,
_intermediates: &[CertificateDer],
_server_name: &ServerName,
_ocsp_response: &[u8],
_now: rustls::pki_types::UnixTime,
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
self.crypto_provider
.signature_verification_algorithms
.supported_schemes()
}
}
}