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 }