/ crates / tor-rtcompat / src / impls / rustls.rs
rustls.rs
  1  //! Implementation for using Rustls with a runtime.
  2  
  3  use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
  4  
  5  use async_trait::async_trait;
  6  use futures::{AsyncRead, AsyncWrite};
  7  use futures_rustls::rustls;
  8  use rustls::client::danger;
  9  use rustls::{CertificateError, Error as TLSError};
 10  use rustls_pki_types::{CertificateDer as Certificate, ServerName};
 11  
 12  use std::{
 13      io::{self, Error as IoError, Result as IoResult},
 14      sync::Arc,
 15  };
 16  
 17  /// A [`TlsProvider`] that uses `rustls`.
 18  ///
 19  /// It supports wrapping any reasonable stream type that implements `AsyncRead` + `AsyncWrite`.
 20  ///
 21  /// The application is responsible for calling `CryptoProvider::install_default_provider()`
 22  /// before constructing one of these providers.  If they do not, we will issue a warning,
 23  /// and install a default (ring) provider.
 24  #[cfg_attr(
 25      docsrs,
 26      doc(cfg(all(feature = "rustls", any(feature = "tokio", feature = "async-std"))))
 27  )]
 28  #[derive(Clone)]
 29  #[non_exhaustive]
 30  pub struct RustlsProvider {
 31      /// Inner `ClientConfig` logic used to create connectors.
 32      config: Arc<futures_rustls::rustls::ClientConfig>,
 33  }
 34  
 35  impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
 36      fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
 37          let (_, session) = self.get_ref();
 38          Ok(session
 39              .peer_certificates()
 40              .and_then(|certs| certs.first().map(|c| Vec::from(c.as_ref()))))
 41      }
 42  
 43      fn export_keying_material(
 44          &self,
 45          len: usize,
 46          label: &[u8],
 47          context: Option<&[u8]>,
 48      ) -> IoResult<Vec<u8>> {
 49          let (_, session) = self.get_ref();
 50          session
 51              .export_keying_material(Vec::with_capacity(len), label, context)
 52              .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
 53      }
 54  }
 55  
 56  /// An implementation of [`TlsConnector`] built with `rustls`.
 57  pub struct RustlsConnector<S> {
 58      /// The inner connector object.
 59      connector: futures_rustls::TlsConnector,
 60      /// Phantom data to ensure proper variance.
 61      _phantom: std::marker::PhantomData<fn(S) -> S>,
 62  }
 63  
 64  #[async_trait]
 65  impl<S> TlsConnector<S> for RustlsConnector<S>
 66  where
 67      S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
 68  {
 69      type Conn = futures_rustls::client::TlsStream<S>;
 70  
 71      async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
 72          let name: ServerName<'_> = sni_hostname
 73              .try_into()
 74              .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
 75          self.connector.connect(name.to_owned(), stream).await
 76      }
 77  }
 78  
 79  impl<S> TlsProvider<S> for RustlsProvider
 80  where
 81      S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
 82  {
 83      type Connector = RustlsConnector<S>;
 84  
 85      type TlsStream = futures_rustls::client::TlsStream<S>;
 86  
 87      fn tls_connector(&self) -> Self::Connector {
 88          let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
 89          RustlsConnector {
 90              connector,
 91              _phantom: std::marker::PhantomData,
 92          }
 93      }
 94  
 95      fn supports_keying_material_export(&self) -> bool {
 96          true
 97      }
 98  }
 99  
100  impl RustlsProvider {
101      /// Construct a new [`RustlsProvider`.]
102      pub(crate) fn new() -> Self {
103          if futures_rustls::rustls::crypto::CryptoProvider::get_default().is_none() {
104              // If we haven't installed a CryptoProvider at this point, we warn and install
105              // the `ring` provider.  That isn't great, but the alternative would be to
106              // panic.  Right now, that would cause many of our tests to fail.
107              tracing::warn!(
108                  "Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
109                              should call CryptoProvider::install_default()"
110              );
111              let _idempotent_ignore =
112                  futures_rustls::rustls::crypto::CryptoProvider::install_default(
113                      futures_rustls::rustls::crypto::ring::default_provider(),
114                  );
115          }
116  
117          // Be afraid: we are overriding the default certificate verification and
118          // TLS signature checking code! See notes on `Verifier` below for
119          // details.
120          //
121          // Note that the `set_certificate_verifier` function is somewhat
122          // misnamed: it overrides not only how certificates are verified, but
123          // also how certificates are used to check the signatures in a TLS
124          // handshake.
125          let config = futures_rustls::rustls::client::ClientConfig::builder()
126              .dangerous()
127              .with_custom_certificate_verifier(std::sync::Arc::new(Verifier {}))
128              .with_no_client_auth();
129  
130          RustlsProvider {
131              config: Arc::new(config),
132          }
133      }
134  }
135  
136  impl Default for RustlsProvider {
137      fn default() -> Self {
138          Self::new()
139      }
140  }
141  
142  /// A [`rustls::client::danger::ServerCertVerifier`] based on the [`x509_signature`] crate.
143  ///
144  /// This verifier is necessary since Tor relays doesn't participate in the web
145  /// browser PKI, and as such their certificates won't check out as valid ones.
146  ///
147  /// What's more, the `webpki` crate rejects most of Tor's certificates as
148  /// unparsable because they do not contain any extensions: That means we need to
149  /// replace the TLS-handshake signature checking functions too, since otherwise
150  /// `rustls` would  think all the certificates were invalid.
151  ///
152  /// Fortunately, the p2p people have provided `x509_signature` for this
153  /// purpose.
154  #[derive(Clone, Debug)]
155  struct Verifier {}
156  
157  impl danger::ServerCertVerifier for Verifier {
158      fn verify_server_cert(
159          &self,
160          end_entity: &Certificate,
161          _roots: &[Certificate],
162          _server_name: &ServerName,
163          _ocsp_response: &[u8],
164          _now: rustls_pki_types::UnixTime,
165      ) -> Result<danger::ServerCertVerified, TLSError> {
166          // We don't check anything about the certificate at this point other
167          // than making sure it is well-formed.
168          //
169          // When we make a channel, we'll check that it's authenticated by the
170          // other party's real identity key, inside the Tor handshake.
171          //
172          // In theory, we shouldn't have to do even this much: rustls should not
173          // allow a handshake  without a certificate, and the certificate's
174          // well-formedness should get checked below in one of the
175          // verify_*_signature functions.  But this check is cheap, so let's
176          // leave it in.
177          let _cert = get_cert(end_entity)?;
178  
179          // Note that we don't even check timeliness: Tor uses the presented
180          // relay certificate just as a container for the relay's public link
181          // key.  Actual timeliness checks will happen later, on the certificates
182          // that authenticate this one, when we process the relay's CERTS cell in
183          // `tor_proto::channel::handshake`.
184  
185          Ok(danger::ServerCertVerified::assertion())
186      }
187  
188      fn verify_tls12_signature(
189          &self,
190          message: &[u8],
191          cert: &Certificate,
192          dss: &rustls::DigitallySignedStruct,
193      ) -> Result<danger::HandshakeSignatureValid, TLSError> {
194          let cert = get_cert(cert)?;
195          let scheme = convert_scheme(dss.scheme)?;
196  
197          // NOTE:
198          //
199          // We call `check_signature` here rather than `check_tls12_signature`.
200          // That means that we're allowing the other side to use signature
201          // algorithms that aren't actually supported by TLS 1.2.
202          //
203          // It turns out, apparently, unless my experiments are wrong,  that
204          // OpenSSL will happily use PSS with TLS 1.2.  At least, it seems to do
205          // so when invoked via native_tls in the test code for this crate.
206          cert.check_signature(scheme, message, dss.signature())
207              .map(|_| danger::HandshakeSignatureValid::assertion())
208              .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadSignature))
209      }
210  
211      fn verify_tls13_signature(
212          &self,
213          message: &[u8],
214          cert: &Certificate,
215          dss: &rustls::DigitallySignedStruct,
216      ) -> Result<danger::HandshakeSignatureValid, TLSError> {
217          let cert = get_cert(cert)?;
218          let scheme = convert_scheme(dss.scheme)?;
219  
220          cert.check_tls13_signature(scheme, message, dss.signature())
221              .map(|_| danger::HandshakeSignatureValid::assertion())
222              .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadSignature))
223      }
224  
225      fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
226          rustls::crypto::ring::default_provider()
227              .signature_verification_algorithms
228              .supported_schemes()
229      }
230  }
231  
232  /// Parse a `rustls::Certificate` as an `x509_signature::X509Certificate`, if possible.
233  fn get_cert<'a>(c: &'a Certificate<'a>) -> Result<x509_signature::X509Certificate<'a>, TLSError> {
234      x509_signature::parse_certificate(c.as_ref())
235          .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadSignature))
236  }
237  
238  /// Convert from the signature scheme type used in `rustls` to the one used in
239  /// `x509_signature`.
240  ///
241  /// (We can't just use the x509_signature crate's "rustls" feature to have it
242  /// use the same enum from `rustls`, because it seems to be on a different
243  /// version from the rustls we want.)
244  fn convert_scheme(
245      scheme: rustls::SignatureScheme,
246  ) -> Result<x509_signature::SignatureScheme, TLSError> {
247      use rustls::SignatureScheme as R;
248      use x509_signature::SignatureScheme as X;
249  
250      // Yes, we do allow PKCS1 here.  That's fine in practice when PKCS1 is only
251      // used (as in TLS 1.2) for signatures; the attacks against correctly
252      // implemented PKCS1 make sense only when it's used for encryption.
253      Ok(match scheme {
254          R::RSA_PKCS1_SHA256 => X::RSA_PKCS1_SHA256,
255          R::ECDSA_NISTP256_SHA256 => X::ECDSA_NISTP256_SHA256,
256          R::RSA_PKCS1_SHA384 => X::RSA_PKCS1_SHA384,
257          R::ECDSA_NISTP384_SHA384 => X::ECDSA_NISTP384_SHA384,
258          R::RSA_PKCS1_SHA512 => X::RSA_PKCS1_SHA512,
259          R::RSA_PSS_SHA256 => X::RSA_PSS_SHA256,
260          R::RSA_PSS_SHA384 => X::RSA_PSS_SHA384,
261          R::RSA_PSS_SHA512 => X::RSA_PSS_SHA512,
262          R::ED25519 => X::ED25519,
263          R::ED448 => X::ED448,
264          _ => {
265              // Either `x509-signature` crate doesn't support these (nor should it really), or
266              // rustls itself doesn't.
267              return Err(TLSError::PeerIncompatible(
268                  rustls::PeerIncompatible::NoSignatureSchemesInCommon,
269              ));
270          }
271      })
272  }
273  
274  #[cfg(test)]
275  mod test {
276      // @@ begin test lint list maintained by maint/add_warning @@
277      #![allow(clippy::bool_assert_comparison)]
278      #![allow(clippy::clone_on_copy)]
279      #![allow(clippy::dbg_macro)]
280      #![allow(clippy::mixed_attributes_style)]
281      #![allow(clippy::print_stderr)]
282      #![allow(clippy::print_stdout)]
283      #![allow(clippy::single_char_pattern)]
284      #![allow(clippy::unwrap_used)]
285      #![allow(clippy::unchecked_duration_subtraction)]
286      #![allow(clippy::useless_vec)]
287      #![allow(clippy::needless_pass_by_value)]
288      //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
289      use super::*;
290  
291      #[test]
292      fn test_cvt_scheme() {
293          use rustls::SignatureScheme as R;
294          use x509_signature::SignatureScheme as X;
295  
296          macro_rules! check_cvt {
297              { $id:ident } =>
298              { assert_eq!(convert_scheme(R::$id).unwrap(), X::$id); }
299          }
300  
301          check_cvt!(RSA_PKCS1_SHA256);
302          check_cvt!(RSA_PKCS1_SHA384);
303          check_cvt!(RSA_PKCS1_SHA512);
304          check_cvt!(ECDSA_NISTP256_SHA256);
305          check_cvt!(ECDSA_NISTP384_SHA384);
306          check_cvt!(RSA_PSS_SHA256);
307          check_cvt!(RSA_PSS_SHA384);
308          check_cvt!(RSA_PSS_SHA512);
309          check_cvt!(ED25519);
310          check_cvt!(ED448);
311  
312          assert!(convert_scheme(R::RSA_PKCS1_SHA1).is_err());
313          assert!(convert_scheme(R::Unknown(0x1337)).is_err());
314      }
315  }