Skip to main content

tor_rtcompat/impls/
rustls.rs

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