1
//! Implementation for using Rustls with a runtime.
2
//!
3
//! #
4

            
5
#[cfg(feature = "tls-server")]
6
pub(crate) mod rustls_server;
7

            
8
use crate::StreamOps;
9
use crate::tls::TlsAcceptorSettings;
10
use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
11

            
12
use async_trait::async_trait;
13
use futures::{AsyncRead, AsyncWrite};
14
use futures_rustls::rustls::{self, crypto::CryptoProvider};
15
use rustls::client::danger;
16
use rustls::crypto::{WebPkiSupportedAlgorithms, verify_tls12_signature, verify_tls13_signature};
17
use rustls::{CertificateError, Error as TLSError};
18
use rustls_pki_types::{CertificateDer, ServerName};
19
use tracing::instrument;
20
use webpki::EndEntityCert; // this is actually rustls_webpki.
21

            
22
use std::borrow::Cow;
23
use 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, rustls will fail,
36
/// and arti will not run.
37
///
38
/// We currently recommend the [aws-lc-rs] provider because,
39
/// of the two builtin providers that ship with rustls,
40
/// it appears to have better performance,
41
/// and it supports hybrid post-quantum handshakes.
42
/// It used to have a [problematic license][aws-lc-old-license],
43
/// but [this has been fixed][aws-lc-relicense] since March 2026.
44
///
45
/// See the [rustls documentation][all-providers] for a list of other rustls
46
/// cryptography providers.
47
///
48
/// [ring]: https://crates.io/crates/ring
49
/// [aws-lc-rs]: https://github.com/aws/aws-lc-rs
50
/// [aws-lc-old-license]: https://github.com/aws/aws-lc/issues/2203
51
/// [all-providers]: https://docs.rs/rustls/latest/rustls/#cryptography-providers
52
/// [aws-lc-relicense]: https://github.com/aws/aws-lc/pull/3091
53
#[cfg_attr(
54
    docsrs,
55
    doc(cfg(all(
56
        feature = "rustls",
57
        any(feature = "tokio", feature = "async-std", feature = "smol")
58
    )))
59
)]
60
#[derive(Clone)]
61
#[non_exhaustive]
62
pub struct RustlsProvider {
63
    /// Inner `ClientConfig` logic used to create connectors.
64
    config: Arc<futures_rustls::rustls::ClientConfig>,
65
}
66

            
67
impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
68
12
    fn peer_certificate(&self) -> IoResult<Option<Cow<'_, [u8]>>> {
69
12
        let (_, session) = self.get_ref();
70
12
        Ok(session
71
12
            .peer_certificates()
72
18
            .and_then(|certs| certs.first().map(|c| Cow::from(c.as_ref()))))
73
12
    }
74

            
75
    fn export_keying_material(
76
        &self,
77
        len: usize,
78
        label: &[u8],
79
        context: Option<&[u8]>,
80
    ) -> IoResult<Vec<u8>> {
81
        let (_, session) = self.get_ref();
82
        session
83
            .export_keying_material(vec![0_u8; len], label, context)
84
            .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
85
    }
86

            
87
    fn own_certificate(&self) -> IoResult<Option<Cow<'_, [u8]>>> {
88
        // This is a client stream, so (as we build them currently) we know we didn't present a certificate.
89
        Ok(None)
90
    }
91
}
92

            
93
impl<S: StreamOps> StreamOps for futures_rustls::client::TlsStream<S> {
94
    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
95
        self.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
96
    }
97

            
98
    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
99
        self.get_ref().0.new_handle()
100
    }
101
}
102

            
103
/// An implementation of [`TlsConnector`] built with `rustls`.
104
pub struct RustlsConnector<S> {
105
    /// The inner connector object.
106
    connector: futures_rustls::TlsConnector,
107
    /// Phantom data to ensure proper variance.
108
    _phantom: std::marker::PhantomData<fn(S) -> S>,
109
}
110

            
111
#[async_trait]
112
impl<S> TlsConnector<S> for RustlsConnector<S>
113
where
114
    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
115
{
116
    type Conn = futures_rustls::client::TlsStream<S>;
117

            
118
    #[instrument(skip_all, level = "trace")]
119
    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
120
        let name: ServerName<'_> = sni_hostname
121
            .try_into()
122
            .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
123
        self.connector.connect(name.to_owned(), stream).await
124
    }
125
}
126

            
127
impl<S> TlsProvider<S> for RustlsProvider
128
where
129
    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
130
{
131
    type Connector = RustlsConnector<S>;
132

            
133
    type TlsStream = futures_rustls::client::TlsStream<S>;
134

            
135
14
    fn tls_connector(&self) -> Self::Connector {
136
14
        let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
137
14
        RustlsConnector {
138
14
            connector,
139
14
            _phantom: std::marker::PhantomData,
140
14
        }
141
14
    }
142

            
143
    cfg_if::cfg_if! {
144
        if #[cfg(feature = "tls-server")] {
145
            type Acceptor = rustls_server::RustlsAcceptor<S>;
146
            type TlsServerStream = rustls_server::RustlsServerStream<S>;
147
6
            fn tls_acceptor(&self, settings: TlsAcceptorSettings) -> IoResult<Self::Acceptor> {
148
6
                rustls_server::RustlsAcceptor::new(&settings)
149
6
            }
150
        } else {
151
            type Acceptor = crate::tls::UnimplementedTls;
152
            type TlsServerStream = crate::tls::UnimplementedTls;
153
            fn tls_acceptor(&self, _settings: TlsAcceptorSettings) -> IoResult<Self::Acceptor> {
154
                Err(crate::tls::TlsServerUnsupported{}.into())
155
            }
156
        }
157
    }
158

            
159
    fn supports_keying_material_export(&self) -> bool {
160
        true
161
    }
162
}
163

            
164
/// Try to install a default crypto provider if none has been installed, so that Rustls can operate.
165
///
166
/// We only use this for testing: It is the application's responsibility to install a CryptoProvider.
167
///
168
#[cfg(any(test, feature = "testing"))]
169
12485
fn ensure_testing_provider_installed() {
170
12485
    if CryptoProvider::get_default().is_none() {
171
        // If we haven't installed a CryptoProvider at this point, we warn and install
172
        // the `ring` provider.  That isn't great, but the alternative would be to
173
        // panic.  Right now, that would cause many of our tests to fail.
174
538
        tracing::warn!(
175
            "Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
176
                        should call CryptoProvider::install_default()"
177
        );
178
538
        let _idempotent_ignore = CryptoProvider::install_default(
179
538
            futures_rustls::rustls::crypto::aws_lc_rs::default_provider(),
180
        );
181
11947
    }
182
12485
}
183

            
184
impl RustlsProvider {
185
    /// Construct a new [`RustlsProvider`].
186
12483
    pub(crate) fn new() -> Self {
187
        #[cfg(any(test, feature = "testing"))]
188
12483
        ensure_testing_provider_installed();
189

            
190
        // Be afraid: we are overriding the default certificate verification and
191
        // TLS signature checking code! See notes on `Verifier` below for
192
        // details.
193
        //
194
        // Note that the `set_certificate_verifier` function is somewhat
195
        // misnamed: it overrides not only how certificates are verified, but
196
        // also how certificates are used to check the signatures in a TLS
197
        // handshake.
198
12483
        let mut config = futures_rustls::rustls::client::ClientConfig::builder()
199
12483
            .dangerous()
200
12483
            .with_custom_certificate_verifier(Arc::new(Verifier(
201
12483
                CryptoProvider::get_default()
202
12483
                    .expect("CryptoProvider not installed")
203
12483
                    .signature_verification_algorithms,
204
12483
            )))
205
12483
            .with_no_client_auth();
206

            
207
        // tor-spec:
208
        // > Implementations SHOULD NOT allow TLS session resumption – it can exacerbate some
209
        // > attacks (e.g. the “Triple Handshake” attack from Feb 2013), and it plays havoc with
210
        // > forward secrecy guarantees.
211
12483
        config.resumption = futures_rustls::rustls::client::Resumption::disabled();
212

            
213
12483
        RustlsProvider {
214
12483
            config: Arc::new(config),
215
12483
        }
216
12483
    }
217
}
218

            
219
impl Default for RustlsProvider {
220
12483
    fn default() -> Self {
221
12483
        Self::new()
222
12483
    }
223
}
224

            
225
/// A custom [`rustls::client::danger::ServerCertVerifier`]
226
///
227
/// This verifier is necessary since Tor relays doesn't participate in the web
228
/// browser PKI, and as such their certificates won't check out as valid ones.
229
///
230
/// We enforce that the certificate itself has correctly authenticated the TLS
231
/// connection, but nothing else.
232
#[derive(Clone, Debug)]
233
struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
234

            
235
impl danger::ServerCertVerifier for Verifier {
236
12
    fn verify_server_cert(
237
12
        &self,
238
12
        end_entity: &CertificateDer,
239
12
        _roots: &[CertificateDer],
240
12
        _server_name: &ServerName,
241
12
        _ocsp_response: &[u8],
242
12
        _now: rustls_pki_types::UnixTime,
243
12
    ) -> Result<danger::ServerCertVerified, TLSError> {
244
        // We don't check anything about the certificate at this point other
245
        // than making sure it is well-formed.
246
        //
247
        // When we make a channel, we'll check that it's authenticated by the
248
        // other party's real identity key, inside the Tor handshake.
249
        //
250
        // In theory, we shouldn't have to do even this much: rustls should not
251
        // allow a handshake  without a certificate, and the certificate's
252
        // well-formedness should get checked below in one of the
253
        // verify_*_signature functions.  But this check is cheap, so let's
254
        // leave it in.
255
12
        let _cert: EndEntityCert<'_> = end_entity
256
12
            .try_into()
257
12
            .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
258

            
259
        // Note that we don't even check timeliness or key usage: Tor uses the presented
260
        // relay certificate just as a container for the relay's public link
261
        // key.  Actual timeliness checks will happen later, on the certificates
262
        // that authenticate this one, when we process the relay's CERTS cell in
263
        // `tor_proto::channel::handshake`.
264
        //
265
        // (This is what makes it safe for us _not_ to call
266
        // EndEntityCert::verify_for_usage.)
267

            
268
12
        Ok(danger::ServerCertVerified::assertion())
269
12
    }
270

            
271
6
    fn verify_tls12_signature(
272
6
        &self,
273
6
        message: &[u8],
274
6
        cert: &CertificateDer,
275
6
        dss: &rustls::DigitallySignedStruct,
276
6
    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
277
6
        verify_tls12_signature(message, cert, dss, &self.0)
278
6
    }
279

            
280
6
    fn verify_tls13_signature(
281
6
        &self,
282
6
        message: &[u8],
283
6
        cert: &CertificateDer,
284
6
        dss: &rustls::DigitallySignedStruct,
285
6
    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
286
6
        verify_tls13_signature(message, cert, dss, &self.0)
287
6
    }
288

            
289
12
    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
290
12
        self.0.supported_schemes()
291
12
    }
292

            
293
12
    fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
294
        // We don't actually want to send any DNs for our root certs,
295
        // since they aren't real.
296
12
        None
297
12
    }
298
}
299

            
300
#[cfg(test)]
301
mod test {
302
    // @@ begin test lint list maintained by maint/add_warning @@
303
    #![allow(clippy::bool_assert_comparison)]
304
    #![allow(clippy::clone_on_copy)]
305
    #![allow(clippy::dbg_macro)]
306
    #![allow(clippy::mixed_attributes_style)]
307
    #![allow(clippy::print_stderr)]
308
    #![allow(clippy::print_stdout)]
309
    #![allow(clippy::single_char_pattern)]
310
    #![allow(clippy::unwrap_used)]
311
    #![allow(clippy::unchecked_time_subtraction)]
312
    #![allow(clippy::useless_vec)]
313
    #![allow(clippy::needless_pass_by_value)]
314
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
315
    use super::*;
316

            
317
    /// A certificate returned by a C Tor relay implementation.
318
    ///
319
    /// We want to have a test for this, since some older versions of `webpki`
320
    /// rejected C Tor's certificates as unparsable because they did not contain
321
    /// any extensions.  Back then, we had to use `x509_signature`,
322
    /// which now appears unmaintained.
323
    const TOR_CERTIFICATE: &[u8] = include_bytes!("./tor-generated.der");
324

            
325
    #[test]
326
    fn basic_tor_cert() {
327
        ensure_testing_provider_installed();
328
        let der = CertificateDer::from_slice(TOR_CERTIFICATE);
329
        let _cert = EndEntityCert::try_from(&der).unwrap();
330
    }
331
}