Update 'rustls' to 0.20.

Also updates 'tokio-rustls' to a compatible version.

Additionally depends on 'rustls-pemfile' which includes functionality
that was previously part of 'rustls' itself.
This commit is contained in:
Sergio Benitez 2022-02-22 14:01:25 -08:00
parent 502b11c177
commit fda05bddd2
7 changed files with 55 additions and 59 deletions

View File

@ -16,7 +16,7 @@ edition = "2018"
[features] [features]
default = [] default = []
tls = ["rustls", "tokio-rustls"] tls = ["rustls", "tokio-rustls", "rustls-pemfile"]
mtls = ["tls", "x509-parser"] mtls = ["tls", "x509-parser"]
private-cookies = ["cookie/private", "cookie/key-expansion"] private-cookies = ["cookie/private", "cookie/key-expansion"]
serde = ["uncased/with-serde-alloc", "serde_"] serde = ["uncased/with-serde-alloc", "serde_"]
@ -28,8 +28,9 @@ percent-encoding = "2"
http = "0.2" http = "0.2"
time = { version = "0.3", features = ["formatting", "macros"] } time = { version = "0.3", features = ["formatting", "macros"] }
indexmap = { version = "1.5.2", features = ["std"] } indexmap = { version = "1.5.2", features = ["std"] }
rustls = { version = "0.19", optional = true } rustls = { version = "0.20", optional = true }
tokio-rustls = { version = "0.22.0", optional = true } tokio-rustls = { version = "0.23.0", optional = true }
rustls-pemfile = { version = "0.3", optional = true }
tokio = { version = "1.6.1", features = ["net", "sync", "time"] } tokio = { version = "1.6.1", features = ["net", "sync", "time"] }
log = "0.4" log = "0.4"
ref-cast = "1.0" ref-cast = "1.0"

View File

@ -54,7 +54,7 @@ pub trait Connection: AsyncRead + AsyncWrite {
/// ///
/// Defaults to an empty vector to indicate that no certificates were /// Defaults to an empty vector to indicate that no certificates were
/// presented. /// presented.
fn peer_certificates(&self) -> Option<Vec<RawCertificate>> { None } fn peer_certificates(&self) -> Option<&[RawCertificate]> { None }
} }
pin_project_lite::pin_project! { pin_project_lite::pin_project! {

View File

@ -5,7 +5,6 @@ use std::task::{Context, Poll};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::future::Future; use std::future::Future;
use rustls::{ServerConfig, SupportedCipherSuite};
use tokio_rustls::{TlsAcceptor, Accept, server::TlsStream}; use tokio_rustls::{TlsAcceptor, Accept, server::TlsStream};
use tokio::net::{TcpListener, TcpStream}; use tokio::net::{TcpListener, TcpStream};
@ -27,7 +26,7 @@ enum State {
pub struct Config<R> { pub struct Config<R> {
pub cert_chain: R, pub cert_chain: R,
pub private_key: R, pub private_key: R,
pub ciphersuites: Vec<&'static SupportedCipherSuite>, pub ciphersuites: Vec<rustls::SupportedCipherSuite>,
pub prefer_server_order: bool, pub prefer_server_order: bool,
pub ca_certs: Option<R>, pub ca_certs: Option<R>,
pub mandatory_mtls: bool, pub mandatory_mtls: bool,
@ -37,40 +36,38 @@ impl TlsListener {
pub async fn bind<R>(addr: SocketAddr, mut c: Config<R>) -> io::Result<TlsListener> pub async fn bind<R>(addr: SocketAddr, mut c: Config<R>) -> io::Result<TlsListener>
where R: io::BufRead where R: io::BufRead
{ {
let cert_chain = load_certs(&mut c.cert_chain).map_err(|e| { use rustls::server::{AllowAnyAuthenticatedClient, AllowAnyAnonymousOrAuthenticatedClient};
let msg = format!("malformed TLS certificate chain: {}", e); use rustls::server::{NoClientAuth, ServerSessionMemoryCache, ServerConfig};
io::Error::new(e.kind(), msg)
})?;
let key = load_private_key(&mut c.private_key).map_err(|e| { let cert_chain = load_certs(&mut c.cert_chain)
let msg = format!("malformed TLS private key: {}", e); .map_err(|e| io::Error::new(e.kind(), format!("bad TLS cert chain: {}", e)))?;
io::Error::new(e.kind(), msg)
})?; let key = load_private_key(&mut c.private_key)
.map_err(|e| io::Error::new(e.kind(), format!("bad TLS private key: {}", e)))?;
let client_auth = match c.ca_certs { let client_auth = match c.ca_certs {
Some(ref mut ca_certs) => { Some(ref mut ca_certs) => match load_ca_certs(ca_certs) {
let roots = load_ca_certs(ca_certs).map_err(|e| { Ok(ca_roots) if c.mandatory_mtls => AllowAnyAuthenticatedClient::new(ca_roots),
let msg = format!("malformed CA certificate(s): {}", e); Ok(ca_roots) => AllowAnyAnonymousOrAuthenticatedClient::new(ca_roots),
io::Error::new(e.kind(), msg) Err(e) => return Err(io::Error::new(e.kind(), format!("bad CA cert(s): {}", e))),
})?; },
None => NoClientAuth::new(),
if c.mandatory_mtls {
rustls::AllowAnyAuthenticatedClient::new(roots)
} else {
rustls::AllowAnyAnonymousOrAuthenticatedClient::new(roots)
}
}
None => rustls::NoClientAuth::new(),
}; };
let mut tls_config = ServerConfig::new(client_auth); let mut tls_config = ServerConfig::builder()
let cache = rustls::ServerSessionMemoryCache::new(1024); .with_cipher_suites(&c.ciphersuites)
tls_config.set_persistence(cache); .with_safe_default_kx_groups()
tls_config.ticketer = rustls::Ticketer::new(); .with_safe_default_protocol_versions()
tls_config.ciphersuites = c.ciphersuites; .map_err(|e| io::Error::new(io::ErrorKind::Other, format!("bad TLS config: {}", e)))?
.with_client_cert_verifier(client_auth)
.with_single_cert(cert_chain, key)
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("bad TLS config: {}", e)))?;
tls_config.ignore_client_order = c.prefer_server_order; tls_config.ignore_client_order = c.prefer_server_order;
tls_config.set_single_cert(cert_chain, key).expect("invalid key"); tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
tls_config.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); tls_config.session_storage = ServerSessionMemoryCache::new(1024);
tls_config.ticketer = rustls::Ticketer::new()
.map_err(|e| io::Error::new(io::ErrorKind::Other, format!("bad TLS ticketer: {}", e)))?;
let listener = TcpListener::bind(addr).await?; let listener = TcpListener::bind(addr).await?;
let acceptor = TlsAcceptor::from(Arc::new(tls_config)); let acceptor = TlsAcceptor::from(Arc::new(tls_config));
@ -120,9 +117,7 @@ impl Connection for TlsStream<TcpStream> {
self.get_ref().0.peer_address() self.get_ref().0.peer_address()
} }
fn peer_certificates(&self) -> Option<Vec<RawCertificate>> { fn peer_certificates(&self) -> Option<&[RawCertificate]> {
use rustls::Session; self.get_ref().1.peer_certificates()
self.get_ref().1.get_peer_certificates()
} }
} }

View File

@ -1,6 +1,6 @@
use std::io::{self, Cursor, Read}; use std::io::{self, Cursor, Read};
use rustls::{internal::pemfile, Certificate, PrivateKey, RootCertStore}; use rustls::{Certificate, PrivateKey, RootCertStore};
fn err(message: impl Into<std::borrow::Cow<'static, str>>) -> io::Error { fn err(message: impl Into<std::borrow::Cow<'static, str>>) -> io::Error {
io::Error::new(io::ErrorKind::Other, message.into()) io::Error::new(io::ErrorKind::Other, message.into())
@ -8,7 +8,8 @@ fn err(message: impl Into<std::borrow::Cow<'static, str>>) -> io::Error {
/// Loads certificates from `reader`. /// Loads certificates from `reader`.
pub fn load_certs(reader: &mut dyn io::BufRead) -> io::Result<Vec<Certificate>> { pub fn load_certs(reader: &mut dyn io::BufRead) -> io::Result<Vec<Certificate>> {
pemfile::certs(reader).map_err(|_| err("invalid certificate")) let certs = rustls_pemfile::certs(reader).map_err(|_| err("invalid certificate"))?;
Ok(certs.into_iter().map(Certificate).collect())
} }
/// Load and decode the private key from `reader`. /// Load and decode the private key from `reader`.
@ -19,8 +20,8 @@ pub fn load_private_key(reader: &mut dyn io::BufRead) -> io::Result<PrivateKey>
reader.read_line(&mut first_line)?; reader.read_line(&mut first_line)?;
let private_keys_fn = match first_line.trim_end() { let private_keys_fn = match first_line.trim_end() {
"-----BEGIN RSA PRIVATE KEY-----" => pemfile::rsa_private_keys, "-----BEGIN RSA PRIVATE KEY-----" => rustls_pemfile::rsa_private_keys,
"-----BEGIN PRIVATE KEY-----" => pemfile::pkcs8_private_keys, "-----BEGIN PRIVATE KEY-----" => rustls_pemfile::pkcs8_private_keys,
_ => return Err(err("invalid key header")) _ => return Err(err("invalid key header"))
}; };
@ -28,7 +29,7 @@ pub fn load_private_key(reader: &mut dyn io::BufRead) -> io::Result<PrivateKey>
.map_err(|_| err("invalid key file")) .map_err(|_| err("invalid key file"))
.and_then(|mut keys| match keys.len() { .and_then(|mut keys| match keys.len() {
0 => Err(err("no valid keys found; is the file malformed?")), 0 => Err(err("no valid keys found; is the file malformed?")),
1 => Ok(keys.remove(0)), 1 => Ok(PrivateKey(keys.remove(0))),
n => Err(err(format!("expected 1 key, found {}", n))), n => Err(err(format!("expected 1 key, found {}", n))),
})?; })?;
@ -41,9 +42,8 @@ pub fn load_private_key(reader: &mut dyn io::BufRead) -> io::Result<PrivateKey>
/// Load and decode CA certificates from `reader`. /// Load and decode CA certificates from `reader`.
pub fn load_ca_certs(reader: &mut dyn io::BufRead) -> io::Result<RootCertStore> { pub fn load_ca_certs(reader: &mut dyn io::BufRead) -> io::Result<RootCertStore> {
let mut roots = rustls::RootCertStore::empty(); let mut roots = rustls::RootCertStore::empty();
let (_, e) = roots.add_pem_file(reader).map_err(|_| err("PEM format error"))?; for cert in load_certs(reader)? {
if e != 0 { roots.add(&cert).map_err(|e| err(format!("CA cert error: {}", e)))?;
return Err(err("validity checks failed"));
} }
Ok(roots) Ok(roots)

View File

@ -630,7 +630,7 @@ mod with_tls_feature {
use crate::http::tls::Config; use crate::http::tls::Config;
use crate::http::tls::rustls::SupportedCipherSuite as RustlsCipher; use crate::http::tls::rustls::SupportedCipherSuite as RustlsCipher;
use crate::http::tls::rustls::ciphersuite as rustls; use crate::http::tls::rustls::cipher_suite;
use yansi::Paint; use yansi::Paint;
@ -675,26 +675,26 @@ mod with_tls_feature {
}) })
} }
fn rustls_ciphers(&self) -> impl Iterator<Item = &'static RustlsCipher> + '_ { fn rustls_ciphers(&self) -> impl Iterator<Item = RustlsCipher> + '_ {
self.ciphers().map(|ciphersuite| match ciphersuite { self.ciphers().map(|ciphersuite| match ciphersuite {
CipherSuite::TLS_CHACHA20_POLY1305_SHA256 => CipherSuite::TLS_CHACHA20_POLY1305_SHA256 =>
&rustls::TLS13_CHACHA20_POLY1305_SHA256, cipher_suite::TLS13_CHACHA20_POLY1305_SHA256,
CipherSuite::TLS_AES_256_GCM_SHA384 => CipherSuite::TLS_AES_256_GCM_SHA384 =>
&rustls::TLS13_AES_256_GCM_SHA384, cipher_suite::TLS13_AES_256_GCM_SHA384,
CipherSuite::TLS_AES_128_GCM_SHA256 => CipherSuite::TLS_AES_128_GCM_SHA256 =>
&rustls::TLS13_AES_128_GCM_SHA256, cipher_suite::TLS13_AES_128_GCM_SHA256,
CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 => CipherSuite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 =>
&rustls::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, cipher_suite::TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 => CipherSuite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 =>
&rustls::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, cipher_suite::TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 => CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 =>
&rustls::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 => CipherSuite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 =>
&rustls::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, cipher_suite::TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 => CipherSuite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 =>
&rustls::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, cipher_suite::TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 => CipherSuite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 =>
&rustls::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, cipher_suite::TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
}) })
} }
} }

View File

@ -298,7 +298,7 @@ impl<F: Future, C: Connection> Connection for CancellableIo<F, C> {
self.io.peer_address() self.io.peer_address()
} }
fn peer_certificates(&self) -> Option<Vec<RawCertificate>> { fn peer_certificates(&self) -> Option<&[RawCertificate]> {
self.io.peer_certificates() self.io.peer_certificates()
} }
} }

View File

@ -444,7 +444,7 @@ impl Rocket<Orbit> {
let rocket = rocket.clone(); let rocket = rocket.clone();
let connection = ConnectionMeta { let connection = ConnectionMeta {
remote: conn.peer_address(), remote: conn.peer_address(),
client_certificates: conn.peer_certificates().map(Arc::new), client_certificates: conn.peer_certificates().map(|certs| Arc::new(certs.to_vec())),
}; };
async move { async move {