1//! Implementation for using Rustls with a runtime.
2//!
3//! #
45use crate::StreamOps;
6use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
78use 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 webpki::EndEntityCert; // this is actually rustls_webpki.
1617use std::{
18 io::{self, Error as IoError, Resultas IoResult},
19 sync::Arc,
20};
2122/// 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]
54pub struct RustlsProvider {
55/// Inner `ClientConfig` logic used to create connectors.
56config: Arc<futures_rustls::rustls::ClientConfig>,
57}
5859impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
60fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
61let (_, session) = self.get_ref();
62Ok(session
63 .peer_certificates()
64 .and_then(|certs| certs.first().map(|c| Vec::from(c.as_ref()))))
65 }
6667fn export_keying_material(
68&self,
69 len: usize,
70 label: &[u8],
71 context: Option<&[u8]>,
72 ) -> IoResult<Vec<u8>> {
73let (_, 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}
7980impl<S: StreamOps> StreamOps for futures_rustls::client::TlsStream<S> {
81fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
82self.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
83 }
8485fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
86self.get_ref().0.new_handle()
87 }
88}
8990/// An implementation of [`TlsConnector`] built with `rustls`.
91pub struct RustlsConnector<S> {
92/// The inner connector object.
93connector: futures_rustls::TlsConnector,
94/// Phantom data to ensure proper variance.
95_phantom: std::marker::PhantomData<fn(S) -> S>,
96}
9798#[async_trait]
99impl<S> TlsConnector<S> for RustlsConnector<S>
100where
101S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
102{
103type Conn = futures_rustls::client::TlsStream<S>;
104105async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
106let name: ServerName<'_> = sni_hostname
107 .try_into()
108 .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
109self.connector.connect(name.to_owned(), stream).await
110}
111}
112113impl<S> TlsProvider<S> for RustlsProvider
114where
115S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
116{
117type Connector = RustlsConnector<S>;
118119type TlsStream = futures_rustls::client::TlsStream<S>;
120121fn tls_connector(&self) -> Self::Connector {
122let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
123 RustlsConnector {
124 connector,
125 _phantom: std::marker::PhantomData,
126 }
127 }
128129fn supports_keying_material_export(&self) -> bool {
130true
131}
132}
133134/// 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.)
137fn ensure_provider_installed() {
138if 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.
142tracing::warn!(
143"Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
144 should call CryptoProvider::install_default()"
145);
146let _idempotent_ignore = CryptoProvider::install_default(
147 futures_rustls::rustls::crypto::ring::default_provider(),
148 );
149 }
150}
151152impl RustlsProvider {
153/// Construct a new [`RustlsProvider`].
154pub(crate) fn new() -> Self {
155 ensure_provider_installed();
156157// Be afraid: we are overriding the default certificate verification and
158 // TLS signature checking code! See notes on `Verifier` below for
159 // details.
160 //
161 // Note that the `set_certificate_verifier` function is somewhat
162 // misnamed: it overrides not only how certificates are verified, but
163 // also how certificates are used to check the signatures in a TLS
164 // handshake.
165let config = futures_rustls::rustls::client::ClientConfig::builder()
166 .dangerous()
167 .with_custom_certificate_verifier(Arc::new(Verifier(
168 CryptoProvider::get_default()
169 .expect("CryptoProvider not installed")
170 .signature_verification_algorithms,
171 )))
172 .with_no_client_auth();
173174 RustlsProvider {
175 config: Arc::new(config),
176 }
177 }
178}
179180impl Default for RustlsProvider {
181fn default() -> Self {
182Self::new()
183 }
184}
185186/// 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)]
194struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
195196impl danger::ServerCertVerifier for Verifier {
197fn verify_server_cert(
198&self,
199 end_entity: &CertificateDer,
200 _roots: &[CertificateDer],
201 _server_name: &ServerName,
202 _ocsp_response: &[u8],
203 _now: rustls_pki_types::UnixTime,
204 ) -> 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.
216let _cert: EndEntityCert<'_> = end_entity
217 .try_into()
218 .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
219220// 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.)
228229Ok(danger::ServerCertVerified::assertion())
230 }
231232fn verify_tls12_signature(
233&self,
234 message: &[u8],
235 cert: &CertificateDer,
236 dss: &rustls::DigitallySignedStruct,
237 ) -> Result<danger::HandshakeSignatureValid, TLSError> {
238 verify_tls12_signature(message, cert, dss, &self.0)
239 }
240241fn 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 }
249250fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
251self.0.supported_schemes()
252 }
253254fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
255// We don't actually want to send any DNs for our root certs,
256 // since they aren't real.
257None
258}
259}
260261#[cfg(test)]
262mod 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 @@ -->
276use super::*;
277278/// 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.
284const TOR_CERTIFICATE: &[u8] = include_bytes!("./tor-generated.der");
285286#[test]
287fn basic_tor_cert() {
288 ensure_provider_installed();
289let der = CertificateDer::from_slice(TOR_CERTIFICATE);
290let _cert = EndEntityCert::try_from(&der).unwrap();
291 }
292}