tor_rtcompat/impls/
rustls.rs1use crate::StreamOps;
6use crate::traits::{CertifiedConn, TlsConnector, TlsProvider};
7
8use 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 tracing::instrument;
16use webpki::EndEntityCert; use std::{
19 io::{self, Error as IoError, Result as IoResult},
20 sync::Arc,
21};
22
23#[cfg_attr(
47 docsrs,
48 doc(cfg(all(
49 feature = "rustls",
50 any(feature = "tokio", feature = "async-std", feature = "smol")
51 )))
52)]
53#[derive(Clone)]
54#[non_exhaustive]
55pub struct RustlsProvider {
56 config: Arc<futures_rustls::rustls::ClientConfig>,
58}
59
60impl<S> CertifiedConn for futures_rustls::client::TlsStream<S> {
61 fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
62 let (_, session) = self.get_ref();
63 Ok(session
64 .peer_certificates()
65 .and_then(|certs| certs.first().map(|c| Vec::from(c.as_ref()))))
66 }
67
68 fn export_keying_material(
69 &self,
70 len: usize,
71 label: &[u8],
72 context: Option<&[u8]>,
73 ) -> IoResult<Vec<u8>> {
74 let (_, session) = self.get_ref();
75 session
76 .export_keying_material(Vec::with_capacity(len), label, context)
77 .map_err(|e| IoError::new(io::ErrorKind::InvalidData, e))
78 }
79}
80
81impl<S: StreamOps> StreamOps for futures_rustls::client::TlsStream<S> {
82 fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
83 self.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
84 }
85
86 fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
87 self.get_ref().0.new_handle()
88 }
89}
90
91pub struct RustlsConnector<S> {
93 connector: futures_rustls::TlsConnector,
95 _phantom: std::marker::PhantomData<fn(S) -> S>,
97}
98
99#[async_trait]
100impl<S> TlsConnector<S> for RustlsConnector<S>
101where
102 S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
103{
104 type Conn = futures_rustls::client::TlsStream<S>;
105
106 #[instrument(skip_all, level = "trace")]
107 async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
108 let name: ServerName<'_> = sni_hostname
109 .try_into()
110 .map_err(|e| IoError::new(io::ErrorKind::InvalidInput, e))?;
111 self.connector.connect(name.to_owned(), stream).await
112 }
113}
114
115impl<S> TlsProvider<S> for RustlsProvider
116where
117 S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
118{
119 type Connector = RustlsConnector<S>;
120
121 type TlsStream = futures_rustls::client::TlsStream<S>;
122
123 fn tls_connector(&self) -> Self::Connector {
124 let connector = futures_rustls::TlsConnector::from(Arc::clone(&self.config));
125 RustlsConnector {
126 connector,
127 _phantom: std::marker::PhantomData,
128 }
129 }
130
131 fn supports_keying_material_export(&self) -> bool {
132 true
133 }
134}
135
136fn ensure_provider_installed() {
140 if CryptoProvider::get_default().is_none() {
141 tracing::warn!(
145 "Creating a RustlsRuntime, but no CryptoProvider is installed. The application \
146 should call CryptoProvider::install_default()"
147 );
148 let _idempotent_ignore = CryptoProvider::install_default(
149 futures_rustls::rustls::crypto::ring::default_provider(),
150 );
151 }
152}
153
154impl RustlsProvider {
155 pub(crate) fn new() -> Self {
157 ensure_provider_installed();
158
159 let config = futures_rustls::rustls::client::ClientConfig::builder()
168 .dangerous()
169 .with_custom_certificate_verifier(Arc::new(Verifier(
170 CryptoProvider::get_default()
171 .expect("CryptoProvider not installed")
172 .signature_verification_algorithms,
173 )))
174 .with_no_client_auth();
175
176 RustlsProvider {
177 config: Arc::new(config),
178 }
179 }
180}
181
182impl Default for RustlsProvider {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188#[derive(Clone, Debug)]
196struct Verifier(pub(crate) WebPkiSupportedAlgorithms);
197
198impl danger::ServerCertVerifier for Verifier {
199 fn verify_server_cert(
200 &self,
201 end_entity: &CertificateDer,
202 _roots: &[CertificateDer],
203 _server_name: &ServerName,
204 _ocsp_response: &[u8],
205 _now: rustls_pki_types::UnixTime,
206 ) -> Result<danger::ServerCertVerified, TLSError> {
207 let _cert: EndEntityCert<'_> = end_entity
219 .try_into()
220 .map_err(|_| TLSError::InvalidCertificate(CertificateError::BadEncoding))?;
221
222 Ok(danger::ServerCertVerified::assertion())
232 }
233
234 fn verify_tls12_signature(
235 &self,
236 message: &[u8],
237 cert: &CertificateDer,
238 dss: &rustls::DigitallySignedStruct,
239 ) -> Result<danger::HandshakeSignatureValid, TLSError> {
240 verify_tls12_signature(message, cert, dss, &self.0)
241 }
242
243 fn verify_tls13_signature(
244 &self,
245 message: &[u8],
246 cert: &CertificateDer,
247 dss: &rustls::DigitallySignedStruct,
248 ) -> Result<danger::HandshakeSignatureValid, TLSError> {
249 verify_tls13_signature(message, cert, dss, &self.0)
250 }
251
252 fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
253 self.0.supported_schemes()
254 }
255
256 fn root_hint_subjects(&self) -> Option<&[rustls::DistinguishedName]> {
257 None
260 }
261}
262
263#[cfg(test)]
264mod test {
265 #![allow(clippy::bool_assert_comparison)]
267 #![allow(clippy::clone_on_copy)]
268 #![allow(clippy::dbg_macro)]
269 #![allow(clippy::mixed_attributes_style)]
270 #![allow(clippy::print_stderr)]
271 #![allow(clippy::print_stdout)]
272 #![allow(clippy::single_char_pattern)]
273 #![allow(clippy::unwrap_used)]
274 #![allow(clippy::unchecked_time_subtraction)]
275 #![allow(clippy::useless_vec)]
276 #![allow(clippy::needless_pass_by_value)]
277 use super::*;
279
280 const TOR_CERTIFICATE: &[u8] = include_bytes!("./tor-generated.der");
287
288 #[test]
289 fn basic_tor_cert() {
290 ensure_provider_installed();
291 let der = CertificateDer::from_slice(TOR_CERTIFICATE);
292 let _cert = EndEntityCert::try_from(&der).unwrap();
293 }
294}