#![warn(rust_2018_idioms, clippy::all, missing_docs)]
use std::convert::Infallible;
use std::future::Future;
use std::io;
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};
use rustls::pki_types::ServerName;
use tokio::io::{AsyncRead, AsyncWrite, BufReader, ReadBuf};
use tokio_postgres::tls;
#[cfg(feature = "runtime")]
use tokio_postgres::tls::MakeTlsConnect;
use tokio_postgres::tls::{ChannelBinding, TlsConnect};
#[cfg(feature = "runtime")]
#[derive(Clone)]
pub struct MakeTlsConnector(Arc<rustls::ClientConfig>);
#[cfg(feature = "runtime")]
impl MakeTlsConnector {
pub fn new(config: Arc<rustls::ClientConfig>) -> MakeTlsConnector {
MakeTlsConnector(config)
}
}
#[cfg(feature = "runtime")]
impl<S> MakeTlsConnect<S> for MakeTlsConnector
where
S: AsyncRead + AsyncWrite + Unpin + 'static + Send,
{
type Stream = TlsStream<S>;
type TlsConnect = TlsConnector;
type Error = Infallible;
fn make_tls_connect(&mut self, domain: &str) -> Result<TlsConnector, Self::Error> {
Ok(TlsConnector::new(self.0.clone(), domain))
}
}
pub struct TlsConnector {
connector: tokio_rustls::TlsConnector,
domain: String,
}
impl TlsConnector {
pub fn new(config: Arc<rustls::ClientConfig>, domain: &str) -> TlsConnector {
TlsConnector {
connector: tokio_rustls::TlsConnector::from(config),
domain: domain.to_string(),
}
}
}
impl<S> TlsConnect<S> for TlsConnector
where
S: AsyncRead + AsyncWrite + Unpin + 'static + Send,
{
type Stream = TlsStream<S>;
type Error = io::Error;
#[allow(clippy::type_complexity)]
type Future = Pin<Box<dyn Future<Output = Result<TlsStream<S>, io::Error>> + Send>>;
fn connect(self, stream: S) -> Self::Future {
let stream = BufReader::with_capacity(8192, stream);
Box::pin(async move {
let domain = server_name(&self.domain)?;
let stream = self.connector.connect(domain, stream).await?;
Ok(TlsStream(stream))
})
}
}
fn server_name(domain: &str) -> io::Result<ServerName<'static>> {
if domain.is_empty() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"no host name to verify the server's TLS certificate against",
));
}
match ServerName::try_from(domain) {
Ok(name) => Ok(name.to_owned()),
Err(_) => Err(io::Error::new(
io::ErrorKind::InvalidInput,
format!("{:?} is not a valid host name for TLS", domain),
)),
}
}
pub struct TlsStream<S>(tokio_rustls::client::TlsStream<BufReader<S>>);
impl<S> AsyncRead for TlsStream<S>
where
S: AsyncRead + AsyncWrite + Unpin,
{
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_read(cx, buf)
}
}
impl<S> AsyncWrite for TlsStream<S>
where
S: AsyncRead + AsyncWrite + Unpin,
{
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.0).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.0).poll_shutdown(cx)
}
}
impl<S> tls::TlsStream for TlsStream<S>
where
S: AsyncRead + AsyncWrite + Unpin,
{
fn channel_binding(&self) -> ChannelBinding {
let (_, session) = self.0.get_ref();
match session.peer_certificates().and_then(|certs| certs.first()) {
Some(cert) => match tls_server_end_point(cert) {
Some(hash) => ChannelBinding::tls_server_end_point(hash),
None => ChannelBinding::none(),
},
None => ChannelBinding::none(),
}
}
}
fn tls_server_end_point(cert: &[u8]) -> Option<Vec<u8>> {
let (oid, parameters) = signature_algorithm(cert)?;
let hash = match oid {
RSA_PSS => pss_hash(parameters)?,
oid => oid,
};
let algorithm = match hash {
RSA_MD5 | RSA_SHA1 | ECDSA_SHA1 | SHA1 => &ring::digest::SHA256,
RSA_SHA256 | ECDSA_SHA256 | SHA256 => &ring::digest::SHA256,
RSA_SHA384 | ECDSA_SHA384 | SHA384 => &ring::digest::SHA384,
RSA_SHA512 | ECDSA_SHA512 | SHA512 => &ring::digest::SHA512,
_ => return None,
};
Some(ring::digest::digest(algorithm, cert).as_ref().to_vec())
}
const RSA_MD5: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04];
const RSA_SHA1: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05];
const RSA_PSS: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a];
const RSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b];
const RSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c];
const RSA_SHA512: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d];
const ECDSA_SHA1: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01];
const ECDSA_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
const ECDSA_SHA384: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03];
const ECDSA_SHA512: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04];
const SHA1: &[u8] = &[0x2b, 0x0e, 0x03, 0x02, 0x1a];
const SHA256: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
const SHA384: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02];
const SHA512: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03];
const SEQUENCE: u8 = 0x30;
const OID: u8 = 0x06;
fn signature_algorithm(cert: &[u8]) -> Option<(&[u8], &[u8])> {
let (certificate, _) = der_element(cert, SEQUENCE)?;
let (_tbs, rest) = der_element(certificate, SEQUENCE)?;
let (algorithm_identifier, _) = der_element(rest, SEQUENCE)?;
der_element(algorithm_identifier, OID)
}
fn pss_hash(parameters: &[u8]) -> Option<&[u8]> {
const HASH_ALGORITHM: u8 = 0xa0; let (params, _) = der_element(parameters, SEQUENCE)?;
if params.first() != Some(&HASH_ALGORITHM) {
return Some(SHA1);
}
let (hash_algorithm, _) = der_element(params, HASH_ALGORITHM)?;
let (algorithm_identifier, _) = der_element(hash_algorithm, SEQUENCE)?;
let (oid, _) = der_element(algorithm_identifier, OID)?;
Some(oid)
}
fn der_element(input: &[u8], tag: u8) -> Option<(&[u8], &[u8])> {
let (&actual, input) = input.split_first()?;
if actual != tag {
return None;
}
let (&first, mut input) = input.split_first()?;
let len = if first < 0x80 {
usize::from(first)
} else {
let count = usize::from(first & 0x7f);
if count == 0 || count > std::mem::size_of::<usize>() || input.len() < count {
return None;
}
let (bytes, rest) = input.split_at(count);
input = rest;
bytes
.iter()
.fold(0usize, |len, &b| (len << 8) | usize::from(b))
};
if input.len() < len {
return None;
}
Some(input.split_at(len))
}
#[cfg(test)]
mod tests {
use super::*;
fn cert(alg: &'static rcgen::SignatureAlgorithm) -> Vec<u8> {
let key = rcgen::KeyPair::generate_for(alg).unwrap();
let params = rcgen::CertificateParams::new(vec!["localhost".to_string()]).unwrap();
params.self_signed(&key).unwrap().der().to_vec()
}
#[test]
fn hashes_with_the_signature_algorithm_hash() {
let p256 = cert(&rcgen::PKCS_ECDSA_P256_SHA256);
assert_eq!(signature_algorithm(&p256).unwrap().0, ECDSA_SHA256);
assert_eq!(
tls_server_end_point(&p256).unwrap(),
ring::digest::digest(&ring::digest::SHA256, &p256).as_ref()
);
let p384 = cert(&rcgen::PKCS_ECDSA_P384_SHA384);
assert_eq!(signature_algorithm(&p384).unwrap().0, ECDSA_SHA384);
assert_eq!(
tls_server_end_point(&p384).unwrap(),
ring::digest::digest(&ring::digest::SHA384, &p384).as_ref()
);
}
const RSA_PSS_SHA384_CERT: &[u8] = include_bytes!("../testdata/rsa_pss_sha384.der");
#[test]
fn hashes_rsa_pss_with_the_hash_its_parameters_name() {
let (oid, parameters) = signature_algorithm(RSA_PSS_SHA384_CERT).unwrap();
assert_eq!(oid, RSA_PSS);
assert_eq!(pss_hash(parameters), Some(SHA384));
assert_eq!(
tls_server_end_point(RSA_PSS_SHA384_CERT).unwrap(),
ring::digest::digest(&ring::digest::SHA384, RSA_PSS_SHA384_CERT).as_ref()
);
}
fn der(tag: u8, contents: &[u8]) -> Vec<u8> {
assert!(contents.len() < 0x80);
[&[tag, contents.len() as u8][..], contents].concat()
}
fn pss_cert(parameters: &[u8]) -> Vec<u8> {
let algorithm = der(SEQUENCE, &[der(OID, RSA_PSS), parameters.to_vec()].concat());
let signature = der(0x03, &[0x00]);
der(
SEQUENCE,
&[der(SEQUENCE, &[]), algorithm, signature].concat(),
)
}
fn pss_parameters(hash: &[u8]) -> Vec<u8> {
let hash_algorithm = der(SEQUENCE, &[der(OID, hash), der(0x05, &[])].concat());
der(SEQUENCE, &der(0xa0, &hash_algorithm))
}
#[test]
fn hashes_rsa_pss_by_its_parameters() {
for (hash, algorithm) in [
(SHA1, &ring::digest::SHA256),
(SHA256, &ring::digest::SHA256),
(SHA384, &ring::digest::SHA384),
(SHA512, &ring::digest::SHA512),
] {
let cert = pss_cert(&pss_parameters(hash));
assert_eq!(
tls_server_end_point(&cert).unwrap(),
ring::digest::digest(algorithm, &cert).as_ref()
);
}
let cert = pss_cert(&der(SEQUENCE, &[]));
assert_eq!(
tls_server_end_point(&cert).unwrap(),
ring::digest::digest(&ring::digest::SHA256, &cert).as_ref()
);
}
#[test]
fn has_no_binding_for_rsa_pss_without_a_known_hash() {
let sha224 = [0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04];
assert_eq!(
tls_server_end_point(&pss_cert(&pss_parameters(&sha224))),
None
);
assert_eq!(tls_server_end_point(&pss_cert(&[])), None);
assert_eq!(
tls_server_end_point(&pss_cert(&der(SEQUENCE, &[0xa0, 0x05]))),
None
);
}
#[test]
fn has_no_binding_for_ed25519() {
let ed25519 = cert(&rcgen::PKCS_ED25519);
assert!(signature_algorithm(&ed25519).is_some());
assert_eq!(tls_server_end_point(&ed25519), None);
}
fn connector(domain: &str) -> TlsConnector {
let provider = Arc::new(rustls::crypto::ring::default_provider());
let config = rustls::ClientConfig::builder_with_provider(provider)
.with_safe_default_protocol_versions()
.unwrap()
.with_root_certificates(rustls::RootCertStore::empty())
.with_no_client_auth();
let mut make = MakeTlsConnector::new(Arc::new(config));
MakeTlsConnect::<tokio::io::DuplexStream>::make_tls_connect(&mut make, domain).unwrap()
}
async fn handshake_error(domain: &str) -> String {
let (client, _server) = tokio::io::duplex(1024);
match connector(domain).connect(client).await {
Ok(_) => panic!("expected the handshake to fail"),
Err(err) => {
assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
err.to_string()
}
}
}
#[tokio::test]
async fn an_empty_domain_fails_only_the_handshake() {
let err = handshake_error("").await;
assert!(err.contains("no host name"), "{err}");
}
#[tokio::test]
async fn an_invalid_domain_fails_only_the_handshake() {
let err = handshake_error("not a host").await;
assert!(
err.contains("\"not a host\" is not a valid host name"),
"{err}"
);
}
#[test]
fn rejects_malformed_certificates() {
let p256 = cert(&rcgen::PKCS_ECDSA_P256_SHA256);
for len in [0, 1, 2, 10, p256.len() / 2] {
assert_eq!(tls_server_end_point(&p256[..len]), None);
}
assert_eq!(
tls_server_end_point(&[0x30, 0x84, 0xff, 0xff, 0xff, 0xff]),
None
);
}
}