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::borrow::Cow;
23use std::{
24    io::{self, Error as IoError, Result as IoResult},
25    sync::Arc,
26};
27
28/// A [`TlsProvider`] that uses `rustls`.
29///
30/// It supports wrapping any reasonable stream type that implements `AsyncRead` + `AsyncWrite`.
31///
32/// # Cryptographic providers
33///
34/// The application is responsible for calling [`CryptoProvider::install_default()`]
35/// before constructing [`TlsProvider`].  If they do not, we will issue a warning,
36/// and install a default ([ring]) provider.
37///
38/// We choose ring because, of the two builtin providers that ship with rustls,
39/// it has the best license.
40/// We _could_ instead use [aws-lc-rs] (for its early MLKEM768 support),
41/// but it is [still under the old OpenSSL license][aws-lc-license], which is GPL-incompatible.
42/// (Although Arti isn't under the GPL itself, we are trying to stay compatible with it.)
43///
44/// See the [rustls documentation][all-providers] for a list of other rustls
45/// cryptography providcers.
46///
47/// [ring]: https://crates.io/crates/ring
48/// [aws-lc-rs]: https://github.com/aws/aws-lc-rs
49/// [aws-lc-license]: https://github.com/aws/aws-lc/issues/2203
50/// [all-providers]: https://docs.rs/rustls/latest/rustls/#cryptography-providers
51#[cfg_attr(
52    docsrs,
53    doc(cfg(all(
54        feature = "rustls",
55        any(feature = "tokio", feature = "async-std", feature = "smol")
56    )))
57)]
58#[derive(Clone)]
59#[non_exhaustive]
60pub struct RustlsProvider {
61    /// Inner `ClientConfig` logic used to create connectors.
62    config: Arc<futures_rustls::rustls::ClientConfig>,
63}
64
65impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
66    fn peer_certificate(&self) -> IoResult<Option<Cow<'_, [u8]>>> {
67        let (_, session) = self.get_ref();
68        Ok(session
69            .peer_certificates()
70            .and_then(|certs| certs.first().map(|c| Cow::from(c.as_ref()))))
71    }
72
73    fn export_keying_material(
74        &self,
75        len: usize,
76        label: &[u8],
77        context: Option<&[u8]>,
78    ) -> IoResult<Vec<u8>> {
79        let (_, session) = self.get_ref();
80        session
81            .export_keying_material(vec![0_u8; len], label, context)
82            .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
83    }
84
85    fn own_certificate(&self) -> IoResult<Option<Cow<'_, [u8]>>> {
86        // This is a client stream, so (as we build them currently) we know we didn't present a 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 mut 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        // tor-spec:
203        // > Implementations SHOULD NOT allow TLS session resumption – it can exacerbate some
204        // > attacks (e.g. the “Triple Handshake” attack from Feb 2013), and it plays havoc with
205        // > forward secrecy guarantees.
206        config.resumption = futures_rustls::rustls::client::Resumption::disabled();
207
208        RustlsProvider {
209            config: Arc::new(config),
210        }
211    }
212}
213
214impl Default for RustlsProvider {
215    fn default() -> Self {
216        Self::new()
217    }
218}
219
220/// A custom [`rustls::client::danger::ServerCertVerifier`]
221///
222/// This verifier is necessary since Tor relays doesn't participate in the web
223/// browser PKI, and as such their certificates won't check out as valid ones.
224///
225/// We enforce that the certificate itself has correctly authenticated the TLS
226/// connection, but nothing else.
227#[derive(Clone, Debug)]
228struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
229
230impl danger::ServerCertVerifier for Verifier {
231    fn verify_server_cert(
232        &self,
233        end_entity: &CertificateDer,
234        _roots: &[CertificateDer],
235        _server_name: &ServerName,
236        _ocsp_response: &[u8],
237        _now: rustls_pki_types::UnixTime,
238    ) -> Result<danger::ServerCertVerified, TLSError> {
239        // We don't check anything about the certificate at this point other
240        // than making sure it is well-formed.
241        //
242        // When we make a channel, we'll check that it's authenticated by the
243        // other party's real identity key, inside the Tor handshake.
244        //
245        // In theory, we shouldn't have to do even this much: rustls should not
246        // allow a handshake  without a certificate, and the certificate's
247        // well-formedness should get checked below in one of the
248        // verify_*_signature functions.  But this check is cheap, so let's
249        // leave it in.
250        let _cert: EndEntityCert<'_> = end_entity
251            .try_into()
252            .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
253
254        // Note that we don't even check timeliness or key usage: Tor uses the presented
255        // relay certificate just as a container for the relay's public link
256        // key.  Actual timeliness checks will happen later, on the certificates
257        // that authenticate this one, when we process the relay's CERTS cell in
258        // `tor_proto::channel::handshake`.
259        //
260        // (This is what makes it safe for us _not_ to call
261        // EndEntityCert::verify_for_usage.)
262
263        Ok(danger::ServerCertVerified::assertion())
264    }
265
266    fn verify_tls12_signature(
267        &self,
268        message: &[u8],
269        cert: &CertificateDer,
270        dss: &rustls::DigitallySignedStruct,
271    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
272        verify_tls12_signature(message, cert, dss, &self.0)
273    }
274
275    fn verify_tls13_signature(
276        &self,
277        message: &[u8],
278        cert: &CertificateDer,
279        dss: &rustls::DigitallySignedStruct,
280    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
281        verify_tls13_signature(message, cert, dss, &self.0)
282    }
283
284    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
285        self.0.supported_schemes()
286    }
287
288    fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
289        // We don't actually want to send any DNs for our root certs,
290        // since they aren't real.
291        None
292    }
293}
294
295#[cfg(test)]
296mod test {
297    // @@ begin test lint list maintained by maint/add_warning @@
298    #![allow(clippy::bool_assert_comparison)]
299    #![allow(clippy::clone_on_copy)]
300    #![allow(clippy::dbg_macro)]
301    #![allow(clippy::mixed_attributes_style)]
302    #![allow(clippy::print_stderr)]
303    #![allow(clippy::print_stdout)]
304    #![allow(clippy::single_char_pattern)]
305    #![allow(clippy::unwrap_used)]
306    #![allow(clippy::unchecked_time_subtraction)]
307    #![allow(clippy::useless_vec)]
308    #![allow(clippy::needless_pass_by_value)]
309    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
310    use super::*;
311
312    /// A certificate returned by a C Tor relay implementation.
313    ///
314    /// We want to have a test for this, since some older versions of `webpki`
315    /// rejected C Tor's certificates as unparsable because they did not contain
316    /// any extensions.  Back then, we had to use `x509_signature`,
317    /// which now appears unmaintained.
318    const TOR_CERTIFICATE: &[u8] = include_bytes!("./tor-generated.der");
319
320    #[test]
321    fn basic_tor_cert() {
322        ensure_provider_installed();
323        let der = CertificateDer::from_slice(TOR_CERTIFICATE);
324        let _cert = EndEntityCert::try_from(&der).unwrap();
325    }
326}