tor_rtcompat/impls/
native_tls.rs

1//! Implementation for using `native_tls`
2
3use crate::traits::{CertifiedConn, StreamOps, TlsConnector, TlsProvider};
4
5use async_trait::async_trait;
6use futures::{AsyncRead, AsyncWrite};
7use native_tls_crate as native_tls;
8use std::io::{Error as IoError, Result as IoResult};
9use tracing::instrument;
10
11/// A [`TlsProvider`] that uses `native_tls`.
12///
13/// It supports wrapping any reasonable stream type that implements `AsyncRead` + `AsyncWrite`.
14#[cfg_attr(
15    docsrs,
16    doc(cfg(all(
17        feature = "native-tls",
18        any(feature = "tokio", feature = "async-std", feature = "smol")
19    )))
20)]
21#[derive(Default, Clone)]
22#[non_exhaustive]
23pub struct NativeTlsProvider {}
24
25impl<S> CertifiedConn for async_native_tls::TlsStream<S>
26where
27    S: AsyncRead + AsyncWrite + Unpin,
28{
29    fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
30        let cert = self.peer_certificate();
31        match cert {
32            Ok(Some(c)) => {
33                let der = c.to_der().map_err(IoError::other)?;
34                Ok(Some(der))
35            }
36            Ok(None) => Ok(None),
37            Err(e) => Err(IoError::other(e)),
38        }
39    }
40
41    fn export_keying_material(
42        &self,
43        _len: usize,
44        _label: &[u8],
45        _context: Option<&[u8]>,
46    ) -> IoResult<Vec<u8>> {
47        Err(std::io::Error::new(
48            std::io::ErrorKind::Unsupported,
49            tor_error::bad_api_usage!("native-tls does not support exporting keying material"),
50        ))
51    }
52}
53
54impl<S: AsyncRead + AsyncWrite + StreamOps + Unpin> StreamOps for async_native_tls::TlsStream<S> {
55    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
56        self.get_ref().set_tcp_notsent_lowat(notsent_lowat)
57    }
58
59    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
60        self.get_ref().new_handle()
61    }
62}
63
64/// An implementation of [`TlsConnector`] built with `native_tls`.
65pub struct NativeTlsConnector<S> {
66    /// The inner connector object.
67    connector: async_native_tls::TlsConnector,
68    /// Phantom data to ensure proper variance.
69    _phantom: std::marker::PhantomData<fn(S) -> S>,
70}
71
72#[async_trait]
73impl<S> TlsConnector<S> for NativeTlsConnector<S>
74where
75    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
76{
77    type Conn = async_native_tls::TlsStream<S>;
78
79    #[instrument(skip_all, level = "trace")]
80    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
81        let conn = self
82            .connector
83            .connect(sni_hostname, stream)
84            .await
85            .map_err(IoError::other)?;
86        Ok(conn)
87    }
88}
89
90impl<S> TlsProvider<S> for NativeTlsProvider
91where
92    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
93{
94    type Connector = NativeTlsConnector<S>;
95
96    type TlsStream = async_native_tls::TlsStream<S>;
97
98    fn tls_connector(&self) -> Self::Connector {
99        let mut builder = native_tls::TlsConnector::builder();
100        // These function names are scary, but they just mean that we
101        // aren't checking whether the signer of this cert
102        // participates in the web PKI, and we aren't checking the
103        // hostname in the cert.
104        builder
105            .danger_accept_invalid_certs(true)
106            .danger_accept_invalid_hostnames(true);
107
108        // We don't participate in the web PKI, so there is no reason for us to load the standard
109        // list of CAs and CRLs. This can save us an megabyte or two.
110        builder.disable_built_in_roots(true);
111
112        let connector = builder.into();
113
114        NativeTlsConnector {
115            connector,
116            _phantom: std::marker::PhantomData,
117        }
118    }
119
120    fn supports_keying_material_export(&self) -> bool {
121        false
122    }
123}