tor_rtcompat/impls/
rustls.rs1#[cfg(feature = "tls-server")]
6pub(crate) mod rustls_server;
7
8use crate::StreamOps;
9use crate::tls::TlsAcceptorSettings;
10use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
11
12use async_trait::async_trait;
13use futures::{AsyncRead, AsyncWrite};
14use futures_rustls::rustls::{self, crypto::CryptoProvider};
15use rustls::client::danger;
16use rustls::crypto::{WebPkiSupportedAlgorithms, verify_tls12_signature, verify_tls13_signature};
17use rustls::{CertificateError, Error as TLSError};
18use rustls_pki_types::{CertificateDer, ServerName};
19use tracing::instrument;
20use webpki::EndEntityCert; use std::borrow::Cow;
23use std::{
24 io::{self, Error as IoError, Result as IoResult},
25 sync::Arc,
26};
27
28#[cfg_attr(
52 docsrs,
53 doc(cfg(all(
54 feature = "rustls",
55 any(feature = "tokio", feature = "async-std", feature = "smol")
56 )))
57)]
58#[derive(Clone)]
59#[non_exhaustive]
60pub struct RustlsProvider {
61 config: Arc<futures_rustls::rustls::ClientConfig>,
63}
64
65impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
66 fn peer_certificate(&self) -> IoResult<Option<Cow<'_, [u8]>>> {
67 let (_, session) = self.get_ref();
68 Ok(session
69 .peer_certificates()
70 .and_then(|certs| certs.first().map(|c| Cow::from(c.as_ref()))))
71 }
72
73 fn export_keying_material(
74 &self,
75 len: usize,
76 label: &[u8],
77 context: Option<&[u8]>,
78 ) -> IoResult<Vec<u8>> {
79 let (_, session) = self.get_ref();
80 session
81 .export_keying_material(vec![0_u8; len], label, context)
82 .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
83 }
84
85 fn own_certificate(&self) -> IoResult<Option<Cow<'_, [u8]>>> {
86 Ok(None)
88 }
89}
90
91impl<S: StreamOps> StreamOps for futures_rustls::client::TlsStream<S> {
92 fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
93 self.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
94 }
95
96 fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
97 self.get_ref().0.new_handle()
98 }
99}
100
101pub struct RustlsConnector<S> {
103 connector: futures_rustls::TlsConnector,
105 _phantom: std::marker::PhantomData<fn(S) -> S>,
107}
108
109#[async_trait]
110impl<S> TlsConnector<S> for RustlsConnector<S>
111where
112 S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
113{
114 type Conn = futures_rustls::client::TlsStream<S>;
115
116 #[instrument(skip_all, level = "trace")]
117 async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
118 let name: ServerName<'_> = sni_hostname
119 .try_into()
120 .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
121 self.connector.connect(name.to_owned(), stream).await
122 }
123}
124
125impl<S> TlsProvider<S> for RustlsProvider
126where
127 S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
128{
129 type Connector = RustlsConnector<S>;
130
131 type TlsStream = futures_rustls::client::TlsStream<S>;
132
133 fn tls_connector(&self) -> Self::Connector {
134 let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
135 RustlsConnector {
136 connector,
137 _phantom: std::marker::PhantomData,
138 }
139 }
140
141 cfg_if::cfg_if! {
142 if #[cfg(feature = "tls-server")] {
143 type Acceptor = rustls_server::RustlsAcceptor<S>;
144 type TlsServerStream = rustls_server::RustlsServerStream<S>;
145 fn tls_acceptor(&self, settings: TlsAcceptorSettings) -> IoResult<Self::Acceptor> {
146 rustls_server::RustlsAcceptor::new(&settings)
147 }
148 } else {
149 type Acceptor = crate::tls::UnimplementedTls;
150 type TlsServerStream = crate::tls::UnimplementedTls;
151 fn tls_acceptor(&self, _settings: TlsAcceptorSettings) -> IoResult<Self::Acceptor> {
152 Err(crate::tls::TlsServerUnsupported{}.into())
153 }
154 }
155 }
156
157 fn supports_keying_material_export(&self) -> bool {
158 true
159 }
160}
161
162fn ensure_provider_installed() {
166 if CryptoProvider::get_default().is_none() {
167 tracing::warn!(
171 "Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
172 should call CryptoProvider::install_default()"
173 );
174 let _idempotent_ignore = CryptoProvider::install_default(
175 futures_rustls::rustls::crypto::ring::default_provider(),
176 );
177 }
178}
179
180impl RustlsProvider {
181 pub(crate) fn new() -> Self {
183 ensure_provider_installed();
184
185 let mut config = futures_rustls::rustls::client::ClientConfig::builder()
194 .dangerous()
195 .with_custom_certificate_verifier(Arc::new(Verifier(
196 CryptoProvider::get_default()
197 .expect("CryptoProvider not installed")
198 .signature_verification_algorithms,
199 )))
200 .with_no_client_auth();
201
202 config.resumption = futures_rustls::rustls::client::Resumption::disabled();
207
208 RustlsProvider {
209 config: Arc::new(config),
210 }
211 }
212}
213
214impl Default for RustlsProvider {
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220#[derive(Clone, Debug)]
228struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
229
230impl danger::ServerCertVerifier for Verifier {
231 fn verify_server_cert(
232 &self,
233 end_entity: &CertificateDer,
234 _roots: &[CertificateDer],
235 _server_name: &ServerName,
236 _ocsp_response: &[u8],
237 _now: rustls_pki_types::UnixTime,
238 ) -> Result<danger::ServerCertVerified, TLSError> {
239 let _cert: EndEntityCert<'_> = end_entity
251 .try_into()
252 .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
253
254 Ok(danger::ServerCertVerified::assertion())
264 }
265
266 fn verify_tls12_signature(
267 &self,
268 message: &[u8],
269 cert: &CertificateDer,
270 dss: &rustls::DigitallySignedStruct,
271 ) -> Result<danger::HandshakeSignatureValid, TLSError> {
272 verify_tls12_signature(message, cert, dss, &self.0)
273 }
274
275 fn verify_tls13_signature(
276 &self,
277 message: &[u8],
278 cert: &CertificateDer,
279 dss: &rustls::DigitallySignedStruct,
280 ) -> Result<danger::HandshakeSignatureValid, TLSError> {
281 verify_tls13_signature(message, cert, dss, &self.0)
282 }
283
284 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
285 self.0.supported_schemes()
286 }
287
288 fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
289 None
292 }
293}
294
295#[cfg(test)]
296mod test {
297 #![allow(clippy::bool_assert_comparison)]
299 #![allow(clippy::clone_on_copy)]
300 #![allow(clippy::dbg_macro)]
301 #![allow(clippy::mixed_attributes_style)]
302 #![allow(clippy::print_stderr)]
303 #![allow(clippy::print_stdout)]
304 #![allow(clippy::single_char_pattern)]
305 #![allow(clippy::unwrap_used)]
306 #![allow(clippy::unchecked_time_subtraction)]
307 #![allow(clippy::useless_vec)]
308 #![allow(clippy::needless_pass_by_value)]
309 use super::*;
311
312 const TOR_CERTIFICATE: &[u8] = include_bytes!("./tor-generated.der");
319
320 #[test]
321 fn basic_tor_cert() {
322 ensure_provider_installed();
323 let der = CertificateDer::from_slice(TOR_CERTIFICATE);
324 let _cert = EndEntityCert::try_from(&der).unwrap();
325 }
326}