Skip to main content

tor_rtcompat/impls/rustls/
rustls_server.rs

1//! TLS server trait implementations for Rustls.
2
3use async_trait::async_trait;
4use futures::{AsyncRead, AsyncWrite};
5use pin_project::pin_project;
6use std::{
7    io::{Error as IoError, Result as IoResult},
8    marker::PhantomData,
9    pin::Pin,
10    sync::Arc,
11    task::{Context, Poll},
12};
13use tracing::instrument;
14
15use crate::{CertifiedConn, StreamOps, tls::TlsAcceptorSettings, tls::TlsConnector};
16use futures_rustls::rustls::ServerConfig as RustlsServerConfig;
17
18/// A server-side TLS stream.
19///
20/// Created by [`RustlsAcceptor`].
21#[pin_project]
22pub struct RustlsServerStream<S> {
23    /// The underlying Rustls stream.
24    ///
25    /// We have to wrap this so that we can also expose the certificate we sent,
26    /// for use in Tor's link authentication.
27    #[pin]
28    stream: futures_rustls::server::TlsStream<S>,
29
30    /// The certificate that we sent.
31    ///
32    /// (If we sent multiple certs, this should be the one corresponding to our private key.)
33    cert_der: Arc<[u8]>,
34}
35
36impl<S: AsyncRead + AsyncWrite + Unpin> AsyncRead for RustlsServerStream<S> {
37    fn poll_read(
38        self: Pin<&mut Self>,
39        cx: &mut Context<'_>,
40        buf: &mut [u8],
41    ) -> Poll<IoResult<usize>> {
42        self.project().stream.poll_read(cx, buf)
43    }
44}
45
46impl<S: AsyncRead + AsyncWrite + Unpin> AsyncWrite for RustlsServerStream<S> {
47    fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<IoResult<usize>> {
48        self.project().stream.poll_write(cx, buf)
49    }
50
51    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<IoResult<()>> {
52        self.project().stream.poll_flush(cx)
53    }
54
55    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<IoResult<()>> {
56        self.project().stream.poll_close(cx)
57    }
58}
59
60impl<S: StreamOps> StreamOps for RustlsServerStream<S> {
61    fn set_tcp_notsent_lowat(&self, notsent_lowat: u32) -> IoResult<()> {
62        self.stream.get_ref().0.set_tcp_notsent_lowat(notsent_lowat)
63    }
64
65    fn new_handle(&self) -> Box<dyn StreamOps + Send + Unpin> {
66        self.stream.get_ref().0.new_handle()
67    }
68}
69
70impl<S: AsyncRead + AsyncWrite + Unpin> CertifiedConn for RustlsServerStream<S> {
71    fn peer_certificate(&self) -> IoResult<Option<Vec<u8>>> {
72        let (_, session) = self.stream.get_ref();
73        Ok(session
74            .peer_certificates()
75            .and_then(|certs| certs.first().map(|c| Vec::from(c.as_ref()))))
76    }
77
78    fn export_keying_material(
79        &self,
80        len: usize,
81        label: &[u8],
82        context: Option<&[u8]>,
83    ) -> IoResult<Vec<u8>> {
84        let (_, session) = self.stream.get_ref();
85        session
86            .export_keying_material(Vec::with_capacity(len), label, context)
87            .map_err(|e| IoError::new(std::io::ErrorKind::InvalidData, e))
88    }
89
90    fn own_certificate(&self) -> IoResult<Option<Vec<u8>>> {
91        Ok(Some(self.cert_der.to_vec()))
92    }
93}
94
95/// A server implementation for Rustls.
96///
97/// Accepts asynchronous streams (typically over TCP), and performs the server-side TLS handshake.
98#[derive(Clone, derive_more::Debug)]
99pub struct RustlsAcceptor<S> {
100    /// The underlying TLS acceptor.
101    #[debug(skip)]
102    acceptor: futures_rustls::TlsAcceptor,
103    /// The certificate corresponding to our private key.
104    cert_der: Arc<[u8]>,
105    /// Phantomdata to mark this type as invariant in S.
106    _phantom: PhantomData<fn(S) -> S>,
107}
108
109#[async_trait]
110impl<S> TlsConnector<S> for RustlsAcceptor<S>
111where
112    S: AsyncRead + AsyncWrite + StreamOps + Unpin + Send + 'static,
113{
114    type Conn = RustlsServerStream<S>;
115
116    #[instrument(skip_all, level = "trace")]
117    async fn negotiate_unvalidated(&self, stream: S, sni_hostname: &str) -> IoResult<Self::Conn> {
118        let _ignore = sni_hostname;
119        let stream = self.acceptor.accept(stream).await?;
120        Ok(RustlsServerStream {
121            stream,
122            cert_der: Arc::clone(&self.cert_der),
123        })
124    }
125}
126
127impl<S> RustlsAcceptor<S> {
128    /// Construct a new RustlsAcceptor from a provided [`TlsAcceptorSettings`]
129    pub(crate) fn new(settings: &TlsAcceptorSettings) -> IoResult<Self> {
130        let cert_der = settings.cert_der().into();
131
132        let cfg: RustlsServerConfig = rustls_server_config(settings)?;
133        let acceptor = futures_rustls::TlsAcceptor::from(Arc::new(cfg));
134        Ok(Self {
135            acceptor,
136            cert_der,
137            _phantom: PhantomData,
138        })
139    }
140}
141
142/// Try to convert a [`TlsAcceptorSettings`] into a configuration for a rustls server.
143///
144/// This is not necessarily suitable for general use outside of being a Tor relay.
145fn rustls_server_config(settings: &TlsAcceptorSettings) -> IoResult<RustlsServerConfig> {
146    use futures_rustls::rustls::pki_types as pki;
147    use futures_rustls::rustls::version::{TLS12, TLS13};
148
149    // Convert certificate and key into expected format.
150    let cert_chain = settings
151        .identity
152        .certificates_der()
153        .into_iter()
154        .map(|c| pki::CertificateDer::from_slice(c).into_owned())
155        .collect();
156    let key_der = settings
157        .identity
158        .private_key_pkcs8_der()
159        .map_err(IoError::other)?;
160    let key_der =
161        pki::PrivateKeyDer::Pkcs8(pki::PrivatePkcs8KeyDer::from(key_der.as_ref())).clone_key();
162
163    // Initialize the ServerConfig.
164    let config = RustlsServerConfig::builder_with_protocol_versions(&[&TLS12, &TLS13]) // 1.2 and 1.3 only.
165        .with_no_client_auth() // Don't require client authentication.
166        .with_single_cert(cert_chain, key_der)
167        .map_err(|e| IoError::new(std::io::ErrorKind::InvalidData, e))?;
168
169    // TODO: Possibly, modify config.  There are numerous fields we could adjust.
170
171    Ok(config)
172}