#![cfg(feature = "client")]
use hickory_proto::rr::rdata::TLSA;
use lruttl::LruCacheWithTtl;
use openssl::ssl::SslOptions;
use parking_lot::Mutex;
use std::sync::{Arc, LazyLock};
use std::time::{Duration, Instant};
use tokio_rustls::rustls::client::danger::ServerCertVerifier;
use tokio_rustls::rustls::crypto::{aws_lc_rs as provider, CryptoProvider};
use tokio_rustls::rustls::{ClientConfig, SupportedCipherSuite};
use tokio_rustls::TlsConnector;
struct RustlsCacheKey {
insecure: bool,
rustls_cipher_suites: Vec<SupportedCipherSuite>,
}
impl std::cmp::PartialEq for RustlsCacheKey {
fn eq(&self, other: &RustlsCacheKey) -> bool {
if self.insecure != other.insecure {
return false;
}
self.rustls_cipher_suites
.iter()
.map(|s| s.suite())
.eq(other.rustls_cipher_suites.iter().map(|s| s.suite()))
}
}
impl std::cmp::Eq for RustlsCacheKey {}
impl std::hash::Hash for RustlsCacheKey {
fn hash<H>(&self, hasher: &mut H)
where
H: std::hash::Hasher,
{
self.insecure.hash(hasher);
for suite in &self.rustls_cipher_suites {
suite.suite().as_str().hash(hasher);
}
}
}
static RUSTLS_CACHE: LazyLock<Mutex<LruCacheWithTtl<RustlsCacheKey, Arc<ClientConfig>>>> =
LazyLock::new(|| Mutex::new(LruCacheWithTtl::new_named("rfc5321_rustls_config", 32)));
impl RustlsCacheKey {
fn get(&self) -> Option<Arc<ClientConfig>> {
RUSTLS_CACHE.lock().get(self)
}
fn set(self, value: Arc<ClientConfig>) {
RUSTLS_CACHE.lock().insert(
self,
value,
Instant::now() + Duration::from_secs(15 * 60),
);
}
}
#[derive(Debug, Clone, Default)]
pub struct TlsOptions {
pub insecure: bool,
pub alt_name: Option<String>,
pub dane_tlsa: Vec<TLSA>,
pub prefer_openssl: bool,
pub openssl_cipher_list: Option<String>,
pub openssl_cipher_suites: Option<String>,
pub openssl_options: Option<SslOptions>,
pub rustls_cipher_suites: Vec<SupportedCipherSuite>,
}
impl TlsOptions {
pub fn build_tls_connector(&self) -> TlsConnector {
let key = RustlsCacheKey {
insecure: self.insecure,
rustls_cipher_suites: self.rustls_cipher_suites.clone(),
};
if let Some(config) = key.get() {
return TlsConnector::from(config);
}
let cipher_suites = if self.rustls_cipher_suites.is_empty() {
provider::DEFAULT_CIPHER_SUITES
} else {
&self.rustls_cipher_suites
};
let provider = Arc::new(CryptoProvider {
cipher_suites: cipher_suites.to_vec(),
..provider::default_provider()
});
let verifier: Arc<dyn ServerCertVerifier> = if self.insecure {
Arc::new(danger::NoCertificateVerification::new(provider.clone()))
} else {
Arc::new(rustls_platform_verifier::Verifier::new().with_provider(provider.clone()))
};
let config = Arc::new(
ClientConfig::builder_with_provider(provider)
.with_protocol_versions(tokio_rustls::rustls::DEFAULT_VERSIONS)
.expect("inconsistent cipher-suite/versions selected")
.dangerous()
.with_custom_certificate_verifier(verifier)
.with_no_client_auth(),
);
key.set(config.clone());
TlsConnector::from(config)
}
}
mod danger {
use std::sync::Arc;
use tokio_rustls::rustls::client::danger::{
HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
};
use tokio_rustls::rustls::crypto::{
verify_tls12_signature, verify_tls13_signature, CryptoProvider,
};
use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use tokio_rustls::rustls::DigitallySignedStruct;
#[derive(Debug)]
pub struct NoCertificateVerification(Arc<CryptoProvider>);
impl NoCertificateVerification {
pub fn new(provider: Arc<CryptoProvider>) -> Self {
Self(provider)
}
}
impl ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, tokio_rustls::rustls::Error> {
Ok(ServerCertVerified::assertion())
}
fn verify_tls12_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
verify_tls12_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn verify_tls13_signature(
&self,
message: &[u8],
cert: &CertificateDer<'_>,
dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
verify_tls13_signature(
message,
cert,
dss,
&self.0.signature_verification_algorithms,
)
}
fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
self.0.signature_verification_algorithms.supported_schemes()
}
}
}