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

            
5
use crate::StreamOps;
6
use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
7

            
8
use async_trait::async_trait;
9
use futures::{AsyncRead, AsyncWrite};
10
use futures_rustls::rustls::{self, crypto::CryptoProvider};
11
use rustls::client::danger;
12
use rustls::crypto::{WebPkiSupportedAlgorithms, verify_tls12_signature, verify_tls13_signature};
13
use rustls::{CertificateError, Error as TLSError};
14
use rustls_pki_types::{CertificateDer, ServerName};
15
use webpki::EndEntityCert; // this is actually rustls_webpki.
16

            
17
use std::{
18
    io::{self, Error as IoError, Result as IoResult},
19
    sync::Arc,
20
};
21

            
22
/// A [`TlsProvider`] that uses `rustls`.
23
///
24
/// It supports wrapping any reasonable stream type that implements `AsyncRead` + `AsyncWrite`.
25
///
26
/// # Cryptographic providers
27
///
28
/// The application is responsible for calling [`CryptoProvider::install_default()`]
29
/// before constructing [`TlsProvider`].  If they do not, we will issue a warning,
30
/// and install a default ([ring]) provider.
31
///
32
/// We choose ring because, of the two builtin providers that ship with rustls,
33
/// it has the best license.
34
/// We _could_ instead use [aws-lc-rs] (for its early MLKEM768 support),
35
/// but it is [still under the old OpenSSL license][aws-lc-license], which is GPL-incompatible.
36
/// (Although Arti isn't under the GPL itself, we are trying to stay compatible with it.)
37
///
38
/// See the [rustls documentation][all-providers] for a list of other rustls
39
/// cryptography providcers.
40
///
41
/// [ring]: https://crates.io/crates/ring
42
/// [aws-lc-rs]: https://github.com/aws/aws-lc-rs
43
/// [aws-lc-license]: https://github.com/aws/aws-lc/issues/2203
44
/// [all-providers]: https://docs.rs/rustls/latest/rustls/#cryptography-providers
45
#[cfg_attr(
46
    docsrs,
47
    doc(cfg(all(
48
        feature = "rustls",
49
        any(feature = "tokio", feature = "async-std", feature = "smol")
50
    )))
51
)]
52
#[derive(Clone)]
53
#[non_exhaustive]
54
pub struct RustlsProvider {
55
    /// Inner `ClientConfig` logic used to create connectors.
56
    config: Arc<futures_rustls::rustls::ClientConfig>,
57
}
58

            
59
impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
60
6
    fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
61
6
        let (_, session) = self.get_ref();
62
6
        Ok(session
63
6
            .peer_certificates()
64
9
            .and_then(|certs| certs.first().map(|c| Vec::from(c.as_ref()))))
65
6
    }
66

            
67
    fn export_keying_material(
68
        &self,
69
        len: usize,
70
        label: &[u8],
71
        context: Option<&[u8]>,
72
    ) -> IoResult<Vec<u8>> {
73
        let (_, session) = self.get_ref();
74
        session
75
            .export_keying_material(Vec::with_capacity(len), label, context)
76
            .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
77
    }
78
}
79

            
80
impl<S: StreamOps> StreamOps for futures_rustls::client::TlsStream<S> {
81
    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
82
        self.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
83
    }
84

            
85
    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
86
        self.get_ref().0.new_handle()
87
    }
88
}
89

            
90
/// An implementation of [`TlsConnector`] built with `rustls`.
91
pub struct RustlsConnector<S> {
92
    /// The inner connector object.
93
    connector: futures_rustls::TlsConnector,
94
    /// Phantom data to ensure proper variance.
95
    _phantom: std::marker::PhantomData<fn(S) -> S>,
96
}
97

            
98
#[async_trait]
99
impl<S> TlsConnector<S> for RustlsConnector<S>
100
where
101
    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
102
{
103
    type Conn = futures_rustls::client::TlsStream<S>;
104

            
105
6
    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
106
6
        let name: ServerName<'_> = sni_hostname
107
6
            .try_into()
108
6
            .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
109
6
        self.connector.connect(name.to_owned(), stream).await
110
12
    }
111
}
112

            
113
impl<S> TlsProvider<S> for RustlsProvider
114
where
115
    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
116
{
117
    type Connector = RustlsConnector<S>;
118

            
119
    type TlsStream = futures_rustls::client::TlsStream<S>;
120

            
121
8
    fn tls_connector(&self) -> Self::Connector {
122
8
        let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
123
8
        RustlsConnector {
124
8
            connector,
125
8
            _phantom: std::marker::PhantomData,
126
8
        }
127
8
    }
128

            
129
    fn supports_keying_material_export(&self) -> bool {
130
        true
131
    }
132
}
133

            
134
/// Try to install a default crypto provider if none has been installed, so that Rustls can operate.
135
///
136
/// (Warns if we have to do this: the application should be responsible for choosing a provider.)
137
13734
fn ensure_provider_installed() {
138
13734
    if CryptoProvider::get_default().is_none() {
139
        // If we haven't installed a CryptoProvider at this point, we warn and install
140
        // the `ring` provider.  That isn't great, but the alternative would be to
141
        // panic.  Right now, that would cause many of our tests to fail.
142
527
        tracing::warn!(
143
            "Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
144
                        should call CryptoProvider::install_default()"
145
        );
146
527
        let _idempotent_ignore = CryptoProvider::install_default(
147
527
            futures_rustls::rustls::crypto::ring::default_provider(),
148
527
        );
149
13207
    }
150
13734
}
151

            
152
impl RustlsProvider {
153
    /// Construct a new [`RustlsProvider`].
154
13732
    pub(crate) fn new() -> Self {
155
13732
        ensure_provider_installed();
156
13732

            
157
13732
        // Be afraid: we are overriding the default certificate verification and
158
13732
        // TLS signature checking code! See notes on `Verifier` below for
159
13732
        // details.
160
13732
        //
161
13732
        // Note that the `set_certificate_verifier` function is somewhat
162
13732
        // misnamed: it overrides not only how certificates are verified, but
163
13732
        // also how certificates are used to check the signatures in a TLS
164
13732
        // handshake.
165
13732
        let config = futures_rustls::rustls::client::ClientConfig::builder()
166
13732
            .dangerous()
167
13732
            .with_custom_certificate_verifier(Arc::new(Verifier(
168
13732
                CryptoProvider::get_default()
169
13732
                    .expect("CryptoProvider not installed")
170
13732
                    .signature_verification_algorithms,
171
13732
            )))
172
13732
            .with_no_client_auth();
173
13732

            
174
13732
        RustlsProvider {
175
13732
            config: Arc::new(config),
176
13732
        }
177
13732
    }
178
}
179

            
180
impl Default for RustlsProvider {
181
13732
    fn default() -> Self {
182
13732
        Self::new()
183
13732
    }
184
}
185

            
186
/// A custom [`rustls::client::danger::ServerCertVerifier`]
187
///
188
/// This verifier is necessary since Tor relays doesn't participate in the web
189
/// browser PKI, and as such their certificates won't check out as valid ones.
190
///
191
/// We enforce that the certificate itself has correctly authenticated the TLS
192
/// connection, but nothing else.
193
#[derive(Clone, Debug)]
194
struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
195

            
196
impl danger::ServerCertVerifier for Verifier {
197
6
    fn verify_server_cert(
198
6
        &self,
199
6
        end_entity: &CertificateDer,
200
6
        _roots: &[CertificateDer],
201
6
        _server_name: &ServerName,
202
6
        _ocsp_response: &[u8],
203
6
        _now: rustls_pki_types::UnixTime,
204
6
    ) -> Result<danger::ServerCertVerified, TLSError> {
205
        // We don't check anything about the certificate at this point other
206
        // than making sure it is well-formed.
207
        //
208
        // When we make a channel, we'll check that it's authenticated by the
209
        // other party's real identity key, inside the Tor handshake.
210
        //
211
        // In theory, we shouldn't have to do even this much: rustls should not
212
        // allow a handshake  without a certificate, and the certificate's
213
        // well-formedness should get checked below in one of the
214
        // verify_*_signature functions.  But this check is cheap, so let's
215
        // leave it in.
216
6
        let _cert: EndEntityCert<'_> = end_entity
217
6
            .try_into()
218
6
            .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
219

            
220
        // Note that we don't even check timeliness or key usage: Tor uses the presented
221
        // relay certificate just as a container for the relay's public link
222
        // key.  Actual timeliness checks will happen later, on the certificates
223
        // that authenticate this one, when we process the relay's CERTS cell in
224
        // `tor_proto::channel::handshake`.
225
        //
226
        // (This is what makes it safe for us _not_ to call
227
        // EndEntityCert::verify_for_usage.)
228

            
229
6
        Ok(danger::ServerCertVerified::assertion())
230
6
    }
231

            
232
6
    fn verify_tls12_signature(
233
6
        &self,
234
6
        message: &[u8],
235
6
        cert: &CertificateDer,
236
6
        dss: &rustls::DigitallySignedStruct,
237
6
    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
238
6
        verify_tls12_signature(message, cert, dss, &self.0)
239
6
    }
240

            
241
    fn verify_tls13_signature(
242
        &self,
243
        message: &[u8],
244
        cert: &CertificateDer,
245
        dss: &rustls::DigitallySignedStruct,
246
    ) -> Result<danger::HandshakeSignatureValid, TLSError> {
247
        verify_tls13_signature(message, cert, dss, &self.0)
248
    }
249

            
250
6
    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
251
6
        self.0.supported_schemes()
252
6
    }
253

            
254
6
    fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
255
6
        // We don't actually want to send any DNs for our root certs,
256
6
        // since they aren't real.
257
6
        None
258
6
    }
259
}
260

            
261
#[cfg(test)]
262
mod test {
263
    // @@ begin test lint list maintained by maint/add_warning @@
264
    #![allow(clippy::bool_assert_comparison)]
265
    #![allow(clippy::clone_on_copy)]
266
    #![allow(clippy::dbg_macro)]
267
    #![allow(clippy::mixed_attributes_style)]
268
    #![allow(clippy::print_stderr)]
269
    #![allow(clippy::print_stdout)]
270
    #![allow(clippy::single_char_pattern)]
271
    #![allow(clippy::unwrap_used)]
272
    #![allow(clippy::unchecked_duration_subtraction)]
273
    #![allow(clippy::useless_vec)]
274
    #![allow(clippy::needless_pass_by_value)]
275
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
276
    use super::*;
277

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

            
286
    #[test]
287
    fn basic_tor_cert() {
288
        ensure_provider_installed();
289
        let der = CertificateDer::from_slice(TOR_CERTIFICATE);
290
        let _cert = EndEntityCert::try_from(&der).unwrap();
291
    }
292
}