tor_rtcompat/impls/
rustls.rs

1//! Implementation for using Rustls with a runtime.
2
3use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
4use crate::StreamOps;
5
6use async_trait::async_trait;
7use futures::{AsyncRead, AsyncWrite};
8use futures_rustls::rustls::{self, crypto::CryptoProvider};
9use rustls::client::danger;
10use rustls::crypto::{verify_tls12_signature, verify_tls13_signature, WebPkiSupportedAlgorithms};
11use rustls::{CertificateError, Error as TLSError};
12use rustls_pki_types::{CertificateDer, ServerName};
13use webpki::EndEntityCert; // this is actually rustls_webpki.
14
15use std::{
16    io::{self, Error as IoError, Result as IoResult},
17    sync::Arc,
18};
19
20/// A [`TlsProvider`] that uses `rustls`.
21///
22/// It supports wrapping any reasonable stream type that implements `AsyncRead` + `AsyncWrite`.
23///
24/// The application is responsible for calling `CryptoProvider::install_default_provider()`
25/// before constructing one of these providers.  If they do not, we will issue a warning,
26/// and install a default (ring) provider.
27#[cfg_attr(
28    docsrs,
29    doc(cfg(all(feature = "rustls", any(feature = "tokio", feature = "async-std"))))
30)]
31#[derive(Clone)]
32#[non_exhaustive]
33pub struct RustlsProvider {
34    /// Inner `ClientConfig` logic used to create connectors.
35    config: Arc<futures_rustls::rustls::ClientConfig>,
36}
37
38impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
39    fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
40        let (_, session) = self.get_ref();
41        Ok(session
42            .peer_certificates()
43            .and_then(|certs| certs.first().map(|c| Vec::from(c.as_ref()))))
44    }
45
46    fn export_keying_material(
47        &self,
48        len: usize,
49        label: &[u8],
50        context: Option<&[u8]>,
51    ) -> IoResult<Vec<u8>> {
52        let (_, session) = self.get_ref();
53        session
54            .export_keying_material(Vec::with_capacity(len), label, context)
55            .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
56    }
57}
58
59impl<S: StreamOps> StreamOps for futures_rustls::client::TlsStream<S> {
60    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
61        self.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
62    }
63
64    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
65        self.get_ref().0.new_handle()
66    }
67}
68
69/// An implementation of [`TlsConnector`] built with `rustls`.
70pub struct RustlsConnector<S> {
71    /// The inner connector object.
72    connector: futures_rustls::TlsConnector,
73    /// Phantom data to ensure proper variance.
74    _phantom: std::marker::PhantomData<fn(S) -> S>,
75}
76
77#[async_trait]
78impl<S> TlsConnector<S> for RustlsConnector<S>
79where
80    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
81{
82    type Conn = futures_rustls::client::TlsStream<S>;
83
84    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
85        let name: ServerName<'_> = sni_hostname
86            .try_into()
87            .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
88        self.connector.connect(name.to_owned(), stream).await
89    }
90}
91
92impl<S> TlsProvider<S> for RustlsProvider
93where
94    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
95{
96    type Connector = RustlsConnector<S>;
97
98    type TlsStream = futures_rustls::client::TlsStream<S>;
99
100    fn tls_connector(&self) -> Self::Connector {
101        let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
102        RustlsConnector {
103            connector,
104            _phantom: std::marker::PhantomData,
105        }
106    }
107
108    fn supports_keying_material_export(&self) -> bool {
109        true
110    }
111}
112
113/// Try to install a default crypto provider if none has been installed, so that Rustls can operate.
114///
115/// (Warns if we have to do this: the application should be responsible for choosing a provider.)
116fn ensure_provider_installed() {
117    if CryptoProvider::get_default().is_none() {
118        // If we haven't installed a CryptoProvider at this point, we warn and install
119        // the `ring` provider.  That isn't great, but the alternative would be to
120        // panic.  Right now, that would cause many of our tests to fail.
121        tracing::warn!(
122            "Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
123                        should call CryptoProvider::install_default()"
124        );
125        let _idempotent_ignore = CryptoProvider::install_default(
126            futures_rustls::rustls::crypto::ring::default_provider(),
127        );
128    }
129}
130
131impl RustlsProvider {
132    /// Construct a new [`RustlsProvider`.]
133    pub(crate) fn new() -> Self {
134        ensure_provider_installed();
135
136        // Be afraid: we are overriding the default certificate verification and
137        // TLS signature checking code! See notes on `Verifier` below for
138        // details.
139        //
140        // Note that the `set_certificate_verifier` function is somewhat
141        // misnamed: it overrides not only how certificates are verified, but
142        // also how certificates are used to check the signatures in a TLS
143        // handshake.
144        let config = futures_rustls::rustls::client::ClientConfig::builder()
145            .dangerous()
146            .with_custom_certificate_verifier(Arc::new(Verifier(
147                CryptoProvider::get_default()
148                    .expect("CryptoProvider not installed")
149                    .signature_verification_algorithms,
150            )))
151            .with_no_client_auth();
152
153        RustlsProvider {
154            config: Arc::new(config),
155        }
156    }
157}
158
159impl Default for RustlsProvider {
160    fn default() -> Self {
161        Self::new()
162    }
163}
164
165/// A custom [`rustls::client::danger::ServerCertVerifier`]
166///
167/// This verifier is necessary since Tor relays doesn't participate in the web
168/// browser PKI, and as such their certificates won't check out as valid ones.
169///
170/// We enforce that the certificate itself has correctly authenticated the TLS
171/// connection, but nothing else.
172#[derive(Clone, Debug)]
173struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
174
175impl danger::ServerCertVerifier for Verifier {
176    fn verify_server_cert(
177        &self,
178        end_entity: &CertificateDer,
179        _roots: &[CertificateDer],
180        _server_name: &ServerName,
181        _ocsp_response: &[u8],
182        _now: rustls_pki_types::UnixTime,
183    ) -> Result<danger::ServerCertVerified, TLSError> {
184        // We don't check anything about the certificate at this point other
185        // than making sure it is well-formed.
186        //
187        // When we make a channel, we'll check that it's authenticated by the
188        // other party's real identity key, inside the Tor handshake.
189        //
190        // In theory, we shouldn't have to do even this much: rustls should not
191        // allow a handshake  without a certificate, and the certificate's
192        // well-formedness should get checked below in one of the
193        // verify_*_signature functions.  But this check is cheap, so let's
194        // leave it in.
195        let _cert: EndEntityCert<'_> = end_entity
196            .try_into()
197            .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
198
199        // Note that we don't even check timeliness or key usage: Tor uses the presented
200        // relay certificate just as a container for the relay's public link
201        // key.  Actual timeliness checks will happen later, on the certificates
202        // that authenticate this one, when we process the relay's CERTS cell in
203        // `tor_proto::channel::handshake`.
204        //
205        // (This is what makes it safe for us _not_ to call
206        // EndEntityCert::verify_for_usage.)
207
208        Ok(danger::ServerCertVerified::assertion())
209    }
210
211    fn verify_tls12_signature(
212        &self,
213        message: &[u8],
214        cert: &CertificateDer,
215        dss: &rustls::DigitallySignedStruct,
216    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
217        verify_tls12_signature(message, cert, dss, &self.0)
218    }
219
220    fn verify_tls13_signature(
221        &self,
222        message: &[u8],
223        cert: &CertificateDer,
224        dss: &rustls::DigitallySignedStruct,
225    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
226        verify_tls13_signature(message, cert, dss, &self.0)
227    }
228
229    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
230        self.0.supported_schemes()
231    }
232
233    fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
234        // We don't actually want to send any DNs for our root certs,
235        // since they aren't real.
236        None
237    }
238}
239
240#[cfg(test)]
241mod test {
242    // @@ begin test lint list maintained by maint/add_warning @@
243    #![allow(clippy::bool_assert_comparison)]
244    #![allow(clippy::clone_on_copy)]
245    #![allow(clippy::dbg_macro)]
246    #![allow(clippy::mixed_attributes_style)]
247    #![allow(clippy::print_stderr)]
248    #![allow(clippy::print_stdout)]
249    #![allow(clippy::single_char_pattern)]
250    #![allow(clippy::unwrap_used)]
251    #![allow(clippy::unchecked_duration_subtraction)]
252    #![allow(clippy::useless_vec)]
253    #![allow(clippy::needless_pass_by_value)]
254    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
255    use super::*;
256
257    /// A certificate returned by a C Tor relay implementation.
258    ///
259    /// We want to have a test for this, since some older versions of `webpki`
260    /// rejected C Tor's certificates as unparsable because they did not contain
261    /// any extensions.  Back then, we had to use `x509_signature`,
262    /// which now appears unmaintained.
263    const TOR_CERTIFICATE: &[u8] = include_bytes!("./tor-generated.der");
264
265    #[test]
266    fn basic_tor_cert() {
267        ensure_provider_installed();
268        let der = CertificateDer::from_slice(TOR_CERTIFICATE);
269        let _cert = EndEntityCert::try_from(&der).unwrap();
270    }
271}