rfc5321/
tls.rs

1#![cfg(feature = "client")]
2use hickory_proto::rr::rdata::TLSA;
3use openssl::ssl::SslOptions;
4use std::sync::Arc;
5use tokio::time::{Duration, Instant};
6use tokio_rustls::rustls::client::danger::ServerCertVerifier;
7use tokio_rustls::rustls::crypto::{aws_lc_rs as provider, CryptoProvider};
8use tokio_rustls::rustls::{ClientConfig, SupportedCipherSuite};
9use tokio_rustls::TlsConnector;
10
11#[derive(Clone, Debug)]
12struct RustlsCacheKey {
13    insecure: bool,
14    rustls_cipher_suites: Vec<SupportedCipherSuite>,
15}
16
17// SupportedCipherSuite has a PartialEq impl but not an Eq impl.
18// Since we need RustlsCacheKey to be Hash we cannot simply derive
19// PartialEq and then add an explicit impl for Eq on RustlsCacheKey
20// because we don't know the implementation details of the underlying
21// PartialEq impl. So we define our own here where we explicitly compare
22// the suite names. This may not be strictly necessary, but it seems
23// wise to be robust to possible future weirdness in that type, and
24// to be certain that our Hash impl is consistent with the Eq impl.
25impl std::cmp::PartialEq for RustlsCacheKey {
26    fn eq(&self, other: &RustlsCacheKey) -> bool {
27        if self.insecure != other.insecure {
28            return false;
29        }
30        self.rustls_cipher_suites
31            .iter()
32            .map(|s| s.suite())
33            .eq(other.rustls_cipher_suites.iter().map(|s| s.suite()))
34    }
35}
36
37impl std::cmp::Eq for RustlsCacheKey {}
38
39impl std::hash::Hash for RustlsCacheKey {
40    fn hash<H>(&self, hasher: &mut H)
41    where
42        H: std::hash::Hasher,
43    {
44        self.insecure.hash(hasher);
45        for suite in &self.rustls_cipher_suites {
46            suite.suite().as_str().hash(hasher);
47        }
48    }
49}
50
51lruttl::declare_cache! {
52static RUSTLS_CACHE: LruCacheWithTtl<RustlsCacheKey, Arc<ClientConfig>>::new("rfc5321_rustls_config", 32);
53}
54
55impl RustlsCacheKey {
56    fn get(&self) -> Option<Arc<ClientConfig>> {
57        RUSTLS_CACHE.get(self)
58    }
59
60    async fn set(self, value: Arc<ClientConfig>) {
61        RUSTLS_CACHE
62            .insert(
63                self,
64                value,
65                // We allow the state to be cached for up to 15 minutes at
66                // a time so that we have an opportunity to reload the
67                // system certificates within a reasonable time frame
68                // as/when they are updated by the system.
69                Instant::now() + Duration::from_secs(15 * 60),
70            )
71            .await;
72    }
73}
74
75#[derive(Debug, Clone, Default)]
76pub struct TlsOptions {
77    pub insecure: bool,
78    pub alt_name: Option<String>,
79    pub dane_tlsa: Vec<TLSA>,
80    pub prefer_openssl: bool,
81    pub openssl_cipher_list: Option<String>,
82    pub openssl_cipher_suites: Option<String>,
83    pub openssl_options: Option<SslOptions>,
84    pub rustls_cipher_suites: Vec<SupportedCipherSuite>,
85}
86
87impl TlsOptions {
88    /// Produce a TlsConnector for this set of TlsOptions.
89    /// We need to employ a cache around the verifier as loading
90    /// the system certificate store can be a non-trivial operation
91    /// and not be something we want to do repeatedly in a hot code
92    /// path.  The cache does unfortunately complicate some of the
93    /// internals here.
94    pub async fn build_tls_connector(&self) -> TlsConnector {
95        let key = RustlsCacheKey {
96            insecure: self.insecure,
97            rustls_cipher_suites: self.rustls_cipher_suites.clone(),
98        };
99
100        if let Some(config) = key.get() {
101            return TlsConnector::from(config);
102        }
103        let cipher_suites = if self.rustls_cipher_suites.is_empty() {
104            provider::DEFAULT_CIPHER_SUITES
105        } else {
106            &self.rustls_cipher_suites
107        };
108
109        let provider = Arc::new(CryptoProvider {
110            cipher_suites: cipher_suites.to_vec(),
111            ..provider::default_provider()
112        });
113
114        let verifier: Arc<dyn ServerCertVerifier> = if self.insecure {
115            Arc::new(danger::NoCertificateVerification::new(provider.clone()))
116        } else {
117            Arc::new(rustls_platform_verifier::Verifier::new().with_provider(provider.clone()))
118        };
119
120        let config = Arc::new(
121            ClientConfig::builder_with_provider(provider)
122                .with_protocol_versions(tokio_rustls::rustls::DEFAULT_VERSIONS)
123                .expect("inconsistent cipher-suite/versions selected")
124                .dangerous()
125                .with_custom_certificate_verifier(verifier)
126                .with_no_client_auth(),
127        );
128        key.set(config.clone()).await;
129
130        TlsConnector::from(config)
131    }
132}
133
134mod danger {
135    use std::sync::Arc;
136    use tokio_rustls::rustls::client::danger::{
137        HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier,
138    };
139    use tokio_rustls::rustls::crypto::{
140        verify_tls12_signature, verify_tls13_signature, CryptoProvider,
141    };
142    use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
143    use tokio_rustls::rustls::DigitallySignedStruct;
144
145    #[derive(Debug)]
146    pub struct NoCertificateVerification(Arc<CryptoProvider>);
147
148    impl NoCertificateVerification {
149        pub fn new(provider: Arc<CryptoProvider>) -> Self {
150            Self(provider)
151        }
152    }
153
154    impl ServerCertVerifier for NoCertificateVerification {
155        fn verify_server_cert(
156            &self,
157            _end_entity: &CertificateDer<'_>,
158            _intermediates: &[CertificateDer<'_>],
159            _server_name: &ServerName<'_>,
160            _ocsp: &[u8],
161            _now: UnixTime,
162        ) -> Result<ServerCertVerified, tokio_rustls::rustls::Error> {
163            Ok(ServerCertVerified::assertion())
164        }
165
166        fn verify_tls12_signature(
167            &self,
168            message: &[u8],
169            cert: &CertificateDer<'_>,
170            dss: &DigitallySignedStruct,
171        ) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
172            verify_tls12_signature(
173                message,
174                cert,
175                dss,
176                &self.0.signature_verification_algorithms,
177            )
178        }
179
180        fn verify_tls13_signature(
181            &self,
182            message: &[u8],
183            cert: &CertificateDer<'_>,
184            dss: &DigitallySignedStruct,
185        ) -> Result<HandshakeSignatureValid, tokio_rustls::rustls::Error> {
186            verify_tls13_signature(
187                message,
188                cert,
189                dss,
190                &self.0.signature_verification_algorithms,
191            )
192        }
193
194        fn supported_verify_schemes(&self) -> Vec<tokio_rustls::rustls::SignatureScheme> {
195            self.0.signature_verification_algorithms.supported_schemes()
196        }
197    }
198}