Skip to main content

postgres_rustls/
lib.rs

1//! TLS support for `tokio-postgres` via `rustls`.
2//!
3//! The counterpart of `postgres-native-tls`: [`MakeTlsConnector`] wraps a
4//! [`rustls::ClientConfig`], and the streams it makes report the
5//! `tls-server-end-point` channel binding, so SCRAM-SHA-256-PLUS works as it
6//! does over native-tls.
7#![warn(rust_2018_idioms, clippy::all, missing_docs)]
8
9use std::convert::Infallible;
10use std::future::Future;
11use std::io;
12use std::pin::Pin;
13use std::sync::Arc;
14use std::task::{Context, Poll};
15
16use rustls::pki_types::ServerName;
17use tokio::io::{AsyncRead, AsyncWrite, BufReader, ReadBuf};
18use tokio_postgres::tls;
19#[cfg(feature = "runtime")]
20use tokio_postgres::tls::MakeTlsConnect;
21use tokio_postgres::tls::{ChannelBinding, TlsConnect};
22
23/// A `MakeTlsConnect` implementation using `rustls`.
24///
25/// Requires the `runtime` Cargo feature (enabled by default).
26#[cfg(feature = "runtime")]
27#[derive(Clone)]
28pub struct MakeTlsConnector(Arc<rustls::ClientConfig>);
29
30#[cfg(feature = "runtime")]
31impl MakeTlsConnector {
32    /// Creates a new connector from a client configuration.
33    pub fn new(config: Arc<rustls::ClientConfig>) -> MakeTlsConnector {
34        MakeTlsConnector(config)
35    }
36}
37
38#[cfg(feature = "runtime")]
39impl<S> MakeTlsConnect<S> for MakeTlsConnector
40where
41    S: AsyncRead + AsyncWrite + Unpin + 'static + Send,
42{
43    type Stream = TlsStream<S>;
44    type TlsConnect = TlsConnector;
45    type Error = Infallible;
46
47    fn make_tls_connect(&mut self, domain: &str) -> Result<TlsConnector, Self::Error> {
48        Ok(TlsConnector::new(self.0.clone(), domain))
49    }
50}
51
52/// A `TlsConnect` implementation using `rustls`.
53pub struct TlsConnector {
54    connector: tokio_rustls::TlsConnector,
55    // Read as a server name only in `connect`: tokio-postgres makes a
56    // connector before it knows whether TLS is used, with an empty domain for
57    // a Unix socket, and that must not fail a plain-text connection.
58    domain: String,
59}
60
61impl TlsConnector {
62    /// Creates a new connector configured to connect to the specified domain,
63    /// which may also be an IP address. One that is not a valid server name
64    /// fails the handshake, not this.
65    pub fn new(config: Arc<rustls::ClientConfig>, domain: &str) -> TlsConnector {
66        TlsConnector {
67            connector: tokio_rustls::TlsConnector::from(config),
68            domain: domain.to_string(),
69        }
70    }
71}
72
73impl<S> TlsConnect<S> for TlsConnector
74where
75    S: AsyncRead + AsyncWrite + Unpin + 'static + Send,
76{
77    type Stream = TlsStream<S>;
78    type Error = io::Error;
79    #[allow(clippy::type_complexity)]
80    type Future = Pin<Box<dyn Future<Output = Result<TlsStream<S>, io::Error>> + Send>>;
81
82    fn connect(self, stream: S) -> Self::Future {
83        let stream = BufReader::with_capacity(8192, stream);
84        Box::pin(async move {
85            let domain = server_name(&self.domain)?;
86            let stream = self.connector.connect(domain, stream).await?;
87            Ok(TlsStream(stream))
88        })
89    }
90}
91
92/// The name the server's certificate is verified against, and sent as SNI.
93fn server_name(domain: &str) -> io::Result<ServerName<'static>> {
94    if domain.is_empty() {
95        return Err(io::Error::new(
96            io::ErrorKind::InvalidInput,
97            "no host name to verify the server's TLS certificate against",
98        ));
99    }
100    match ServerName::try_from(domain) {
101        Ok(name) => Ok(name.to_owned()),
102        Err(_) => Err(io::Error::new(
103            io::ErrorKind::InvalidInput,
104            format!("{:?} is not a valid host name for TLS", domain),
105        )),
106    }
107}
108
109/// The stream returned by `TlsConnector`.
110pub struct TlsStream<S>(tokio_rustls::client::TlsStream<BufReader<S>>);
111
112impl<S> AsyncRead for TlsStream<S>
113where
114    S: AsyncRead + AsyncWrite + Unpin,
115{
116    fn poll_read(
117        mut self: Pin<&mut Self>,
118        cx: &mut Context<'_>,
119        buf: &mut ReadBuf<'_>,
120    ) -> Poll<io::Result<()>> {
121        Pin::new(&mut self.0).poll_read(cx, buf)
122    }
123}
124
125impl<S> AsyncWrite for TlsStream<S>
126where
127    S: AsyncRead + AsyncWrite + Unpin,
128{
129    fn poll_write(
130        mut self: Pin<&mut Self>,
131        cx: &mut Context<'_>,
132        buf: &[u8],
133    ) -> Poll<io::Result<usize>> {
134        Pin::new(&mut self.0).poll_write(cx, buf)
135    }
136
137    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
138        Pin::new(&mut self.0).poll_flush(cx)
139    }
140
141    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
142        Pin::new(&mut self.0).poll_shutdown(cx)
143    }
144}
145
146impl<S> tls::TlsStream for TlsStream<S>
147where
148    S: AsyncRead + AsyncWrite + Unpin,
149{
150    fn channel_binding(&self) -> ChannelBinding {
151        let (_, session) = self.0.get_ref();
152        match session.peer_certificates().and_then(|certs| certs.first()) {
153            Some(cert) => match tls_server_end_point(cert) {
154                Some(hash) => ChannelBinding::tls_server_end_point(hash),
155                None => ChannelBinding::none(),
156            },
157            None => ChannelBinding::none(),
158        }
159    }
160}
161
162/// The `tls-server-end-point` channel binding of a DER certificate (RFC 5929
163/// §4.1): its hash under the hash function of its signature algorithm, with
164/// MD5 and SHA-1 replaced by SHA-256. For RSASSA-PSS that is the hash its
165/// parameters name, as PostgreSQL's server takes it. `None` for algorithms
166/// the RFC leaves undefined (e.g. Ed25519), hashes `ring` lacks, or a
167/// certificate that does not parse, as native-tls does; the connection then
168/// authenticates without the binding.
169fn tls_server_end_point(cert: &[u8]) -> Option<Vec<u8>> {
170    let (oid, parameters) = signature_algorithm(cert)?;
171    let hash = match oid {
172        RSA_PSS => pss_hash(parameters)?,
173        oid => oid,
174    };
175    let algorithm = match hash {
176        // md5WithRSAEncryption, sha1WithRSAEncryption, ecdsa-with-SHA1, and
177        // SHA-1 itself, the RSASSA-PSS default.
178        RSA_MD5 | RSA_SHA1 | ECDSA_SHA1 | SHA1 => &ring::digest::SHA256,
179        RSA_SHA256 | ECDSA_SHA256 | SHA256 => &ring::digest::SHA256,
180        RSA_SHA384 | ECDSA_SHA384 | SHA384 => &ring::digest::SHA384,
181        RSA_SHA512 | ECDSA_SHA512 | SHA512 => &ring::digest::SHA512,
182        _ => return None,
183    };
184    Some(ring::digest::digest(algorithm, cert).as_ref().to_vec())
185}
186
187// The DER contents of the signature algorithm OIDs above.
188const RSA_MD5: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04];
189const RSA_SHA1: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05];
190const RSA_PSS: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a];
191const RSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b];
192const RSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c];
193const RSA_SHA512: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d];
194const ECDSA_SHA1: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01];
195const ECDSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
196const ECDSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03];
197const ECDSA_SHA512: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04];
198
199// The DER contents of the hash OIDs an RSASSA-PSS signature can name.
200const SHA1: &[u8] = &[0x2b, 0x0e, 0x03, 0x02, 0x1a];
201const SHA256: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
202const SHA384: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02];
203const SHA512: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03];
204
205const SEQUENCE: u8 = 0x30;
206const OID: u8 = 0x06;
207
208/// A DER certificate's outer `signatureAlgorithm`, as `(the algorithm's OID,
209/// the DER of its parameters)`: `Certificate ::= SEQUENCE { tbsCertificate,
210/// signatureAlgorithm AlgorithmIdentifier, signatureValue }`,
211/// `AlgorithmIdentifier ::= SEQUENCE { algorithm OBJECT IDENTIFIER,
212/// parameters ANY OPTIONAL }`. The parameters are empty when absent.
213fn signature_algorithm(cert: &[u8]) -> Option<(&[u8], &[u8])> {
214    let (certificate, _) = der_element(cert, SEQUENCE)?;
215    let (_tbs, rest) = der_element(certificate, SEQUENCE)?;
216    let (algorithm_identifier, _) = der_element(rest, SEQUENCE)?;
217    der_element(algorithm_identifier, OID)
218}
219
220/// The OID of the hash RSASSA-PSS parameters name (RFC 4055 §3.1):
221/// `RSASSA-PSS-params ::= SEQUENCE { hashAlgorithm [0] HashAlgorithm DEFAULT
222/// sha1, ... }`, `HashAlgorithm ::= AlgorithmIdentifier`. The parameters
223/// must be present in a signature's algorithm identifier, so `None` if they
224/// are not.
225fn pss_hash(parameters: &[u8]) -> Option<&[u8]> {
226    const HASH_ALGORITHM: u8 = 0xa0; // [0], explicit, so constructed.
227    let (params, _) = der_element(parameters, SEQUENCE)?;
228    if params.first() != Some(&HASH_ALGORITHM) {
229        return Some(SHA1);
230    }
231    let (hash_algorithm, _) = der_element(params, HASH_ALGORITHM)?;
232    let (algorithm_identifier, _) = der_element(hash_algorithm, SEQUENCE)?;
233    let (oid, _) = der_element(algorithm_identifier, OID)?;
234    Some(oid)
235}
236
237/// Splits one DER element with the given tag off the front of `input`:
238/// `(its contents, what follows it)`.
239fn der_element(input: &[u8], tag: u8) -> Option<(&[u8], &[u8])> {
240    let (&actual, input) = input.split_first()?;
241    if actual != tag {
242        return None;
243    }
244    let (&first, mut input) = input.split_first()?;
245    let len = if first < 0x80 {
246        usize::from(first)
247    } else {
248        // Long form: the low bits count the length's bytes.
249        let count = usize::from(first & 0x7f);
250        if count == 0 || count > std::mem::size_of::<usize>() || input.len() < count {
251            return None;
252        }
253        let (bytes, rest) = input.split_at(count);
254        input = rest;
255        bytes
256            .iter()
257            .fold(0usize, |len, &b| (len << 8) | usize::from(b))
258    };
259    if input.len() < len {
260        return None;
261    }
262    Some(input.split_at(len))
263}
264
265#[cfg(test)]
266mod tests {
267    use super::*;
268
269    fn cert(alg: &'static rcgen::SignatureAlgorithm) -> Vec<u8> {
270        let key = rcgen::KeyPair::generate_for(alg).unwrap();
271        let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
272        params.self_signed(&key).unwrap().der().to_vec()
273    }
274
275    #[test]
276    fn hashes_with_the_signature_algorithm_hash() {
277        let p256 = cert(&rcgen::PKCS_ECDSA_P256_SHA256);
278        assert_eq!(signature_algorithm(&p256).unwrap().0, ECDSA_SHA256);
279        assert_eq!(
280            tls_server_end_point(&p256).unwrap(),
281            ring::digest::digest(&ring::digest::SHA256, &p256).as_ref()
282        );
283
284        let p384 = cert(&rcgen::PKCS_ECDSA_P384_SHA384);
285        assert_eq!(signature_algorithm(&p384).unwrap().0, ECDSA_SHA384);
286        assert_eq!(
287            tls_server_end_point(&p384).unwrap(),
288            ring::digest::digest(&ring::digest::SHA384, &p384).as_ref()
289        );
290    }
291
292    /// Made by OpenSSL 3: `openssl req -x509 -key <an RSA-PSS key> -sha384
293    /// -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:48`.
294    const RSA_PSS_SHA384_CERT: &[u8] = include_bytes!("../testdata/rsa_pss_sha384.der");
295
296    #[test]
297    fn hashes_rsa_pss_with_the_hash_its_parameters_name() {
298        let (oid, parameters) = signature_algorithm(RSA_PSS_SHA384_CERT).unwrap();
299        assert_eq!(oid, RSA_PSS);
300        assert_eq!(pss_hash(parameters), Some(SHA384));
301        assert_eq!(
302            tls_server_end_point(RSA_PSS_SHA384_CERT).unwrap(),
303            ring::digest::digest(&ring::digest::SHA384, RSA_PSS_SHA384_CERT).as_ref()
304        );
305    }
306
307    /// A DER element: `tag`, a short-form length, `contents`.
308    fn der(tag: u8, contents: &[u8]) -> Vec<u8> {
309        assert!(contents.len() < 0x80);
310        [&[tag, contents.len() as u8][..], contents].concat()
311    }
312
313    /// The outline of a certificate signed under RSASSA-PSS with the given
314    /// DER parameters: all `tls_server_end_point` reads of one.
315    fn pss_cert(parameters: &[u8]) -> Vec<u8> {
316        let algorithm = der(SEQUENCE, &[der(OID, RSA_PSS), parameters.to_vec()].concat());
317        let signature = der(0x03, &[0x00]);
318        der(
319            SEQUENCE,
320            &[der(SEQUENCE, &[]), algorithm, signature].concat(),
321        )
322    }
323
324    /// RSASSA-PSS parameters naming `hash`, with a NULL parameter as
325    /// OpenSSL writes it.
326    fn pss_parameters(hash: &[u8]) -> Vec<u8> {
327        let hash_algorithm = der(SEQUENCE, &[der(OID, hash), der(0x05, &[])].concat());
328        der(SEQUENCE, &der(0xa0, &hash_algorithm))
329    }
330
331    #[test]
332    fn hashes_rsa_pss_by_its_parameters() {
333        for (hash, algorithm) in [
334            (SHA1, &ring::digest::SHA256),
335            (SHA256, &ring::digest::SHA256),
336            (SHA384, &ring::digest::SHA384),
337            (SHA512, &ring::digest::SHA512),
338        ] {
339            let cert = pss_cert(&pss_parameters(hash));
340            assert_eq!(
341                tls_server_end_point(&cert).unwrap(),
342                ring::digest::digest(algorithm, &cert).as_ref()
343            );
344        }
345
346        // No hashAlgorithm: SHA-1, the default, so SHA-256.
347        let cert = pss_cert(&der(SEQUENCE, &[]));
348        assert_eq!(
349            tls_server_end_point(&cert).unwrap(),
350            ring::digest::digest(&ring::digest::SHA256, &cert).as_ref()
351        );
352    }
353
354    #[test]
355    fn has_no_binding_for_rsa_pss_without_a_known_hash() {
356        // SHA-224, which ring lacks.
357        let sha224 = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04];
358        assert_eq!(
359            tls_server_end_point(&pss_cert(&pss_parameters(&sha224))),
360            None
361        );
362        // No parameters, which a signature's algorithm must have.
363        assert_eq!(tls_server_end_point(&pss_cert(&[])), None);
364        // A truncated hashAlgorithm.
365        assert_eq!(
366            tls_server_end_point(&pss_cert(&der(SEQUENCE, &[0xa0, 0x05]))),
367            None
368        );
369    }
370
371    #[test]
372    fn has_no_binding_for_ed25519() {
373        let ed25519 = cert(&rcgen::PKCS_ED25519);
374        assert!(signature_algorithm(&ed25519).is_some());
375        assert_eq!(tls_server_end_point(&ed25519), None);
376    }
377
378    fn connector(domain: &str) -> TlsConnector {
379        let provider = Arc::new(rustls::crypto::ring::default_provider());
380        let config = rustls::ClientConfig::builder_with_provider(provider)
381            .with_safe_default_protocol_versions()
382            .unwrap()
383            .with_root_certificates(rustls::RootCertStore::empty())
384            .with_no_client_auth();
385        let mut make = MakeTlsConnector::new(Arc::new(config));
386        MakeTlsConnect::<tokio::io::DuplexStream>::make_tls_connect(&mut make, domain).unwrap()
387    }
388
389    async fn handshake_error(domain: &str) -> String {
390        let (client, _server) = tokio::io::duplex(1024);
391        match connector(domain).connect(client).await {
392            Ok(_) => panic!("expected the handshake to fail"),
393            Err(err) => {
394                assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
395                err.to_string()
396            }
397        }
398    }
399
400    /// tokio-postgres makes a connector for a Unix socket too, with no host
401    /// name, before it knows whether TLS is used.
402    #[tokio::test]
403    async fn an_empty_domain_fails_only_the_handshake() {
404        let err = handshake_error("").await;
405        assert!(err.contains("no host name"), "{err}");
406    }
407
408    #[tokio::test]
409    async fn an_invalid_domain_fails_only_the_handshake() {
410        let err = handshake_error("not a host").await;
411        assert!(
412            err.contains("\"not a host\" is not a valid host name"),
413            "{err}"
414        );
415    }
416
417    #[test]
418    fn rejects_malformed_certificates() {
419        let p256 = cert(&rcgen::PKCS_ECDSA_P256_SHA256);
420        for len in [0, 1, 2, 10, p256.len() / 2] {
421            assert_eq!(tls_server_end_point(&p256[..len]), None);
422        }
423        assert_eq!(
424            tls_server_end_point(&[0x30, 0x84, 0xff, 0xff, 0xff, 0xff]),
425            None
426        );
427    }
428}