tor_rtcompat/impls/
rustls.rs

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