use super::service::TlsConnector;
use crate::transport::{
Error,
tls::{Certificate, Identity},
};
use http::Uri;
use std::sync::Arc;
use std::time::Duration;
use tokio_rustls::rustls::client::danger::ServerCertVerifier;
use tokio_rustls::rustls::pki_types::TrustAnchor;
#[derive(Debug, Clone, Default)]
pub struct ClientTlsConfig {
domain: Option<String>,
certs: Vec<Certificate>,
trust_anchors: Vec<TrustAnchor<'static>>,
identity: Option<Identity>,
assume_http2: bool,
#[cfg(feature = "tls-native-roots")]
with_native_roots: bool,
#[cfg(feature = "tls-webpki-roots")]
with_webpki_roots: bool,
use_key_log: bool,
timeout: Option<Duration>,
}
impl ClientTlsConfig {
pub fn new() -> Self {
Self::default()
}
pub fn domain_name(self, domain_name: impl Into<String>) -> Self {
ClientTlsConfig {
domain: Some(domain_name.into()),
..self
}
}
pub fn ca_certificate(self, ca_certificate: Certificate) -> Self {
let mut certs = self.certs;
certs.push(ca_certificate);
ClientTlsConfig { certs, ..self }
}
pub fn ca_certificates(self, ca_certificates: impl IntoIterator<Item = Certificate>) -> Self {
let mut certs = self.certs;
certs.extend(ca_certificates);
ClientTlsConfig { certs, ..self }
}
pub fn trust_anchor(self, trust_anchor: TrustAnchor<'static>) -> Self {
let mut trust_anchors = self.trust_anchors;
trust_anchors.push(trust_anchor);
ClientTlsConfig {
trust_anchors,
..self
}
}
pub fn trust_anchors(
mut self,
trust_anchors: impl IntoIterator<Item = TrustAnchor<'static>>,
) -> Self {
self.trust_anchors.extend(trust_anchors);
self
}
pub fn identity(self, identity: Identity) -> Self {
ClientTlsConfig {
identity: Some(identity),
..self
}
}
pub fn assume_http2(self, assume_http2: bool) -> Self {
ClientTlsConfig {
assume_http2,
..self
}
}
pub fn use_key_log(self) -> Self {
ClientTlsConfig {
use_key_log: true,
..self
}
}
#[cfg(feature = "tls-native-roots")]
pub fn with_native_roots(self) -> Self {
ClientTlsConfig {
with_native_roots: true,
..self
}
}
#[cfg(feature = "tls-webpki-roots")]
pub fn with_webpki_roots(self) -> Self {
ClientTlsConfig {
with_webpki_roots: true,
..self
}
}
pub fn with_enabled_roots(self) -> Self {
let config = self;
#[cfg(feature = "tls-native-roots")]
let config = config.with_native_roots();
#[cfg(feature = "tls-webpki-roots")]
let config = config.with_webpki_roots();
config
}
pub fn timeout(self, timeout: Duration) -> Self {
ClientTlsConfig {
timeout: Some(timeout),
..self
}
}
pub(crate) fn into_tls_connector(self, uri: &Uri) -> Result<TlsConnector, crate::BoxError> {
self.build_tls_connector(uri, None)
}
pub(crate) fn into_tls_connector_with_verifier(
self,
uri: &Uri,
verifier: Arc<dyn ServerCertVerifier>,
) -> Result<TlsConnector, crate::BoxError> {
self.build_tls_connector(uri, Some(verifier))
}
fn build_tls_connector(
self,
uri: &Uri,
server_cert_verifier: Option<Arc<dyn ServerCertVerifier>>,
) -> Result<TlsConnector, crate::BoxError> {
let domain = if let Some(domain) = &self.domain {
domain
} else {
let host = uri.host().ok_or_else(Error::new_invalid_uri)?;
if host.starts_with('[') && host.ends_with(']') {
&host[1..host.len() - 1]
} else {
host
}
};
TlsConnector::new(
self.certs,
self.trust_anchors,
self.identity,
server_cert_verifier,
domain,
self.assume_http2,
self.use_key_log,
self.timeout,
#[cfg(feature = "tls-native-roots")]
self.with_native_roots,
#[cfg(feature = "tls-webpki-roots")]
self.with_webpki_roots,
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_into_tls_connector_with_ipv4() {
let config = ClientTlsConfig::new();
let uri = "https://192.168.1.1:443".parse::<Uri>().unwrap();
config.into_tls_connector(&uri).unwrap();
}
#[test]
fn test_into_tls_connector_with_ipv6_() {
let config = ClientTlsConfig::new();
let uri = "https://[::1]:443".parse::<Uri>().unwrap();
config.into_tls_connector(&uri).unwrap();
}
#[test]
fn test_into_tls_connector_with_domain_name() {
let config = ClientTlsConfig::new();
let uri = "https://example.com:443".parse::<Uri>().unwrap();
config.into_tls_connector(&uri).unwrap();
}
#[test]
fn test_into_tls_connector_with_explicit_domain() {
let config = ClientTlsConfig::new().domain_name("example.com");
let uri = "https://[2001:db8::1]:443".parse::<Uri>().unwrap();
config.into_tls_connector(&uri).unwrap();
}
#[test]
fn test_into_tls_connector_with_verifier_with_ipv6() {
use tokio_rustls::rustls::DigitallySignedStruct;
use tokio_rustls::rustls::client::danger::{
HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
};
use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
#[derive(Debug)]
struct DummyVerifier;
impl ServerCertVerifier for DummyVerifier {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp_response: &[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> {
Ok(HandshakeSignatureValid::assertion())
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
tokio_rustls::rustls::crypto::ring::default_provider()
.signature_verification_algorithms
.supported_schemes()
}
}
let config = ClientTlsConfig::new();
let uri = "https://[::1]:443".parse::<Uri>().unwrap();
config
.into_tls_connector_with_verifier(&uri, Arc::new(DummyVerifier))
.unwrap();
}
}