From 38e0e1b52df4b256419824dc80cea0c654e0229b Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 24 Nov 2023 10:10:30 +0100 Subject: [PATCH 1/3] Update to rustls alpha releases --- Cargo.toml | 16 ++++++----- examples/client.rs | 5 ++-- examples/server.rs | 20 ++++---------- src/acceptor/builder.rs | 5 ++-- src/config.rs | 29 +++++++------------- src/connector.rs | 5 ++-- src/connector/builder.rs | 58 +++++++++++++++++++++++++++++++++++----- src/lib.rs | 22 ++++++++------- 8 files changed, 94 insertions(+), 66 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9214af3..a5a8ce5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,26 +14,28 @@ documentation = "https://docs.rs/hyper-rustls/" http = "0.2" hyper = { version = "0.14", default-features = false, features = ["client"] } log = { version = "0.4.4", optional = true } -rustls-native-certs = { version = "0.6", optional = true } -rustls = { version = "0.21.6", default-features = false } +pki-types = { package = "rustls-pki-types", version = "0.2" } +rustls-native-certs = { version = "=0.7.0-alpha.2", optional = true } +rustls = { version = "=0.22.0-alpha.5", default-features = false } tokio = "1.0" -tokio-rustls = { version = "0.24.0", default-features = false } -webpki-roots = { version = "0.25", optional = true } +tokio-rustls = { version = "=0.25.0-alpha.3", default-features = false } +webpki-roots = { version = "=0.26.0-alpha.2", optional = true } futures-util = { version = "0.3", default-features = false } [dev-dependencies] hyper = { version = "0.14", features = ["full"] } -rustls = { version = "0.21.0", default-features = false, features = ["tls12"] } -rustls-pemfile = "1.0.0" +rustls = { version = "=0.22.0-alpha.5", default-features = false, features = ["tls12"] } +rustls-pemfile = "=2.0.0-alpha.2" tokio = { version = "1.0", features = ["io-std", "macros", "net", "rt-multi-thread"] } [features] -default = ["native-tokio", "http1", "tls12", "logging", "acceptor"] +default = ["native-tokio", "http1", "tls12", "logging", "acceptor", "ring"] acceptor = ["hyper/server", "tokio-runtime"] http1 = ["hyper/http1"] http2 = ["hyper/http2"] webpki-tokio = ["tokio-runtime", "webpki-roots"] native-tokio = ["tokio-runtime", "rustls-native-certs"] +ring = ["rustls/ring"] tokio-runtime = ["hyper/runtime"] tls12 = ["tokio-rustls/tls12", "rustls/tls12"] logging = ["log", "tokio-rustls/logging", "rustls/logging"] diff --git a/examples/client.rs b/examples/client.rs index 9f9da94..773715b 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -47,10 +47,9 @@ async fn run_client() -> io::Result<()> { let tls = match ca { Some(ref mut rd) => { // Read trust roots - let certs = rustls_pemfile::certs(rd) - .map_err(|_| error("failed to load custom CA store".into()))?; + let certs = rustls_pemfile::certs(rd).collect::, _>>()?; let mut roots = RootCertStore::empty(); - roots.add_parsable_certificates(&certs); + roots.add_parsable_certificates(certs); // TLS client config using the custom CA store for lookups rustls::ClientConfig::builder() .with_safe_defaults() diff --git a/examples/server.rs b/examples/server.rs index 964303b..65519cf 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -14,6 +14,7 @@ use hyper::server::conn::AddrIncoming; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, Server, StatusCode}; use hyper_rustls::TlsAcceptor; +use pki_types::{CertificateDer, PrivateKeyDer}; fn main() { // Serve an echo service over HTTPS, with proper error handling. @@ -80,34 +81,23 @@ async fn echo(req: Request) -> Result, hyper::Error> { } // Load public certificate from file. -fn load_certs(filename: &str) -> io::Result> { +fn load_certs(filename: &str) -> io::Result>> { // Open certificate file. let certfile = fs::File::open(filename) .map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; let mut reader = io::BufReader::new(certfile); // Load and return certificate. - let certs = rustls_pemfile::certs(&mut reader) - .map_err(|_| error("failed to load certificate".into()))?; - Ok(certs - .into_iter() - .map(rustls::Certificate) - .collect()) + rustls_pemfile::certs(&mut reader).collect() } // Load private key from file. -fn load_private_key(filename: &str) -> io::Result { +fn load_private_key(filename: &str) -> io::Result> { // Open keyfile. let keyfile = fs::File::open(filename) .map_err(|e| error(format!("failed to open {}: {}", filename, e)))?; let mut reader = io::BufReader::new(keyfile); // Load and return a single private key. - let keys = rustls_pemfile::rsa_private_keys(&mut reader) - .map_err(|_| error("failed to load private key".into()))?; - if keys.len() != 1 { - return Err(error("expected a single private key".into())); - } - - Ok(rustls::PrivateKey(keys[0].clone())) + rustls_pemfile::private_key(&mut reader).map(|key| key.unwrap()) } diff --git a/src/acceptor/builder.rs b/src/acceptor/builder.rs index 9b95a82..eb9f62a 100644 --- a/src/acceptor/builder.rs +++ b/src/acceptor/builder.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use hyper::server::conn::AddrIncoming; +use pki_types::{CertificateDer, PrivateKeyDer}; use rustls::ServerConfig; use super::TlsAcceptor; @@ -27,8 +28,8 @@ impl AcceptorBuilder { /// [with_no_client_auth]: rustls::ConfigBuilder::with_no_client_auth pub fn with_single_cert( self, - cert_chain: Vec, - key_der: rustls::PrivateKey, + cert_chain: Vec>, + key_der: PrivateKeyDer<'static>, ) -> Result, rustls::Error> { Ok(AcceptorBuilder(WantsAlpn( ServerConfig::builder() diff --git a/src/config.rs b/src/config.rs index 1c74c45..25f0a93 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,5 @@ -use rustls::client::WantsTransparencyPolicyOrClientCert; +#[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] +use rustls::client::WantsClientCert; use rustls::{ClientConfig, ConfigBuilder, WantsVerifier}; /// Methods for configuring roots @@ -13,35 +14,29 @@ pub trait ConfigBuilderExt { /// it's recommended to use `with_webpki_roots`. #[cfg(feature = "rustls-native-certs")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] - fn with_native_roots( - self, - ) -> std::io::Result>; + fn with_native_roots(self) -> std::io::Result>; /// This configures the webpki roots, which are Mozilla's set of /// trusted roots as packaged by webpki-roots. #[cfg(feature = "webpki-roots")] #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] - fn with_webpki_roots(self) -> ConfigBuilder; + fn with_webpki_roots(self) -> ConfigBuilder; } impl ConfigBuilderExt for ConfigBuilder { #[cfg(feature = "rustls-native-certs")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] #[cfg_attr(not(feature = "logging"), allow(unused_variables))] - fn with_native_roots( - self, - ) -> std::io::Result> { + fn with_native_roots(self) -> std::io::Result> { let mut roots = rustls::RootCertStore::empty(); let mut valid_count = 0; let mut invalid_count = 0; for cert in rustls_native_certs::load_native_certs().expect("could not load platform certs") { - let cert = rustls::Certificate(cert.0); - match roots.add(&cert) { + match roots.add(cert) { Ok(_) => valid_count += 1, Err(err) => { - crate::log::trace!("invalid cert der {:?}", cert.0); crate::log::debug!("certificate parsing failed: {:?}", err); invalid_count += 1 } @@ -65,18 +60,12 @@ impl ConfigBuilderExt for ConfigBuilder { #[cfg(feature = "webpki-roots")] #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] - fn with_webpki_roots(self) -> ConfigBuilder { + fn with_webpki_roots(self) -> ConfigBuilder { let mut roots = rustls::RootCertStore::empty(); - roots.add_trust_anchors( + roots.extend( webpki_roots::TLS_SERVER_ROOTS .iter() - .map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }), + .cloned(), ); self.with_root_certificates(roots) } diff --git a/src/connector.rs b/src/connector.rs index 1e3cc4e..712c32b 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -5,6 +5,7 @@ use std::task::{Context, Poll}; use std::{fmt, io}; use hyper::{client::connect::Connection, service::Service, Uri}; +use pki_types::ServerName; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::TlsConnector; @@ -106,8 +107,8 @@ where hostname = trimmed; } - let hostname = match rustls::ServerName::try_from(hostname) { - Ok(dnsname) => dnsname, + let hostname = match ServerName::try_from(hostname) { + Ok(dnsname) => dnsname.to_owned(), Err(_) => { let err = io::Error::new(io::ErrorKind::Other, "invalid dnsname"); return Box::pin(async move { Err(Box::new(err).into()) }); diff --git a/src/connector/builder.rs b/src/connector/builder.rs index e0f06d0..76b0998 100644 --- a/src/connector/builder.rs +++ b/src/connector/builder.rs @@ -1,12 +1,13 @@ +#[cfg(feature = "tokio-runtime")] +use hyper::client::HttpConnector; +#[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] +use rustls::crypto::CryptoProvider; use rustls::ClientConfig; use super::HttpsConnector; #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] use crate::config::ConfigBuilderExt; -#[cfg(feature = "tokio-runtime")] -use hyper::client::HttpConnector; - /// A builder for an [`HttpsConnector`] /// /// This makes configuration flexible and explicit and ensures connector @@ -57,8 +58,11 @@ impl ConnectorBuilder { /// See [`ConfigBuilderExt::with_native_roots`] /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults - #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] + #[cfg(all(feature = "ring", feature = "rustls-native-certs"))] + #[cfg_attr( + docsrs, + doc(cfg(all(feature = "ring", feature = "rustls-native-certs"))) + )] pub fn with_native_roots(self) -> std::io::Result> { Ok(self.with_tls_config( ClientConfig::builder() @@ -68,14 +72,34 @@ impl ConnectorBuilder { )) } + /// Shorthand for using rustls' [safe defaults][with_safe_defaults] + /// with a custom [`CryptoProvider`] and native roots + /// + /// See [`ConfigBuilderExt::with_native_roots`] + /// + /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults + #[cfg(feature = "rustls-native-certs")] + #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] + pub fn with_provider_and_native_roots( + self, + provider: &'static dyn CryptoProvider, + ) -> std::io::Result> { + Ok(self.with_tls_config( + ClientConfig::builder_with_provider(provider) + .with_safe_defaults() + .with_native_roots()? + .with_no_client_auth(), + )) + } + /// Shorthand for using rustls' [safe defaults][with_safe_defaults] /// and Mozilla roots /// /// See [`ConfigBuilderExt::with_webpki_roots`] /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults - #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] + #[cfg(all(feature = "ring", feature = "webpki-roots"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "ring", feature = "webpki-roots"))))] pub fn with_webpki_roots(self) -> ConnectorBuilder { self.with_tls_config( ClientConfig::builder() @@ -84,6 +108,26 @@ impl ConnectorBuilder { .with_no_client_auth(), ) } + + /// Shorthand for using rustls' [safe defaults][with_safe_defaults] + /// with a custom [`CryptoProvider`] and Mozilla roots + /// + /// See [`ConfigBuilderExt::with_webpki_roots`] + /// + /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults + #[cfg(feature = "webpki-roots")] + #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] + pub fn with_provider_and_webpki_roots( + self, + provider: &'static dyn CryptoProvider, + ) -> ConnectorBuilder { + self.with_tls_config( + ClientConfig::builder_with_provider(provider) + .with_safe_defaults() + .with_webpki_roots() + .with_no_client_auth(), + ) + } } impl Default for ConnectorBuilder { diff --git a/src/lib.rs b/src/lib.rs index d2b8ab4..aa26d0a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -33,12 +33,14 @@ //! ```no_run //! # #[cfg(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1", feature = "acceptor"))] //! # fn main() { +//! use std::io; +//! use std::fs::File; +//! //! use hyper::server::conn::AddrIncoming; //! use hyper::service::{make_service_fn, service_fn}; //! use hyper::{Body, Method, Request, Response, Server, StatusCode}; //! use hyper_rustls::TlsAcceptor; -//! use std::io; -//! use std::fs::File; +//! use pki_types::{CertificateDer, PrivateKeyDer}; //! //! let mut rt = tokio::runtime::Runtime::new().unwrap(); //! let addr = "127.0.0.1:1337".parse().unwrap(); @@ -48,16 +50,14 @@ //! let mut reader = io::BufReader::new(certfile); //! //! // Load and return certificate. -//! let certs = rustls_pemfile::certs(&mut reader).unwrap(); -//! let certs = certs.into_iter().map(rustls::Certificate).collect(); +//! let certs = rustls_pemfile::certs(&mut reader).collect::, _>>().unwrap(); //! //! // Load private key. (see `examples/server.rs`) //! let keyfile = File::open("examples/sample.rsa").unwrap(); //! let mut reader = io::BufReader::new(keyfile); //! //! // Load and return a single private key. -//! let keys = rustls_pemfile::rsa_private_keys(&mut reader).unwrap(); -//! let key = rustls::PrivateKey(keys[0].clone()); +//! let key = rustls_pemfile::private_key(&mut reader).unwrap().unwrap(); //! let https = hyper_rustls::HttpsConnectorBuilder::new() //! .with_native_roots() //! .expect("no native root CA certificates found") @@ -67,7 +67,7 @@ //! //! let incoming = AddrIncoming::bind(&addr).unwrap(); //! let acceptor = TlsAcceptor::builder() -//! .with_single_cert(certs, key).unwrap() +//! .with_single_cert(certs, key.into()).unwrap() //! .with_all_versions_alpn() //! .with_incoming(incoming); //! let service = make_service_fn(|_| async { Ok::<_, io::Error>(service_fn(|_req|async {Ok::<_, io::Error>(Response::new(Body::empty()))})) }); @@ -90,14 +90,16 @@ mod stream; #[cfg(feature = "logging")] mod log { - pub(crate) use log::{debug, trace}; + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + pub(crate) use log::debug; } #[cfg(not(feature = "logging"))] mod log { - macro_rules! trace ( ($($tt:tt)*) => {{}} ); + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] macro_rules! debug ( ($($tt:tt)*) => {{}} ); - pub(crate) use {debug, trace}; + #[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] + pub(crate) use debug; } #[cfg(feature = "acceptor")] From 958bef0777442e622878ca89b4a0e218e64eb351 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 24 Nov 2023 10:45:28 +0100 Subject: [PATCH 2/3] Enable feature(doc_auto_cfg) --- src/config.rs | 4 ---- src/connector/builder.rs | 10 ---------- src/lib.rs | 2 +- 3 files changed, 1 insertion(+), 15 deletions(-) diff --git a/src/config.rs b/src/config.rs index 25f0a93..a512433 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,19 +13,16 @@ pub trait ConfigBuilderExt { /// This will return an error if no valid certs were found. In that case, /// it's recommended to use `with_webpki_roots`. #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] fn with_native_roots(self) -> std::io::Result>; /// This configures the webpki roots, which are Mozilla's set of /// trusted roots as packaged by webpki-roots. #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] fn with_webpki_roots(self) -> ConfigBuilder; } impl ConfigBuilderExt for ConfigBuilder { #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] #[cfg_attr(not(feature = "logging"), allow(unused_variables))] fn with_native_roots(self) -> std::io::Result> { let mut roots = rustls::RootCertStore::empty(); @@ -59,7 +56,6 @@ impl ConfigBuilderExt for ConfigBuilder { } #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] fn with_webpki_roots(self) -> ConfigBuilder { let mut roots = rustls::RootCertStore::empty(); roots.extend( diff --git a/src/connector/builder.rs b/src/connector/builder.rs index 76b0998..e3ea1e9 100644 --- a/src/connector/builder.rs +++ b/src/connector/builder.rs @@ -59,10 +59,6 @@ impl ConnectorBuilder { /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults #[cfg(all(feature = "ring", feature = "rustls-native-certs"))] - #[cfg_attr( - docsrs, - doc(cfg(all(feature = "ring", feature = "rustls-native-certs"))) - )] pub fn with_native_roots(self) -> std::io::Result> { Ok(self.with_tls_config( ClientConfig::builder() @@ -79,7 +75,6 @@ impl ConnectorBuilder { /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults #[cfg(feature = "rustls-native-certs")] - #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] pub fn with_provider_and_native_roots( self, provider: &'static dyn CryptoProvider, @@ -99,7 +94,6 @@ impl ConnectorBuilder { /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults #[cfg(all(feature = "ring", feature = "webpki-roots"))] - #[cfg_attr(docsrs, doc(cfg(all(feature = "ring", feature = "webpki-roots"))))] pub fn with_webpki_roots(self) -> ConnectorBuilder { self.with_tls_config( ClientConfig::builder() @@ -116,7 +110,6 @@ impl ConnectorBuilder { /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults #[cfg(feature = "webpki-roots")] - #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] pub fn with_provider_and_webpki_roots( self, provider: &'static dyn CryptoProvider, @@ -209,7 +202,6 @@ impl ConnectorBuilder { /// /// This needs to be called explicitly, no protocol is enabled by default #[cfg(feature = "http2")] - #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn enable_http2(mut self) -> ConnectorBuilder { self.0.tls_config.alpn_protocols = vec![b"h2".to_vec()]; ConnectorBuilder(WantsProtocols3 { @@ -223,7 +215,6 @@ impl ConnectorBuilder { /// For now, this could enable both HTTP 1 and 2, depending on active features. /// In the future, other supported versions will be enabled as well. #[cfg(feature = "http2")] - #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn enable_all_versions(mut self) -> ConnectorBuilder { #[cfg(feature = "http1")] let alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; @@ -267,7 +258,6 @@ impl ConnectorBuilder { /// /// This needs to be called explicitly, no protocol is enabled by default #[cfg(feature = "http2")] - #[cfg_attr(docsrs, doc(cfg(feature = "http2")))] pub fn enable_http2(mut self) -> ConnectorBuilder { self.0.inner.tls_config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; ConnectorBuilder(WantsProtocols3 { diff --git a/src/lib.rs b/src/lib.rs index aa26d0a..a5d3c55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ //! ``` #![warn(missing_docs, unreachable_pub, clippy::use_self)] -#![cfg_attr(docsrs, feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] #[cfg(feature = "acceptor")] /// TLS acceptor implementing hyper's `Accept` trait. From 81c98f9503f37cbc376b4269e99abe65f17b22aa Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 24 Nov 2023 10:10:57 +0100 Subject: [PATCH 3/3] Bump version to 0.25.0-alpha.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a5a8ce5..68f4e01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hyper-rustls" -version = "0.24.2" +version = "0.25.0-alpha.0" edition = "2021" rust-version = "1.63" license = "Apache-2.0 OR ISC OR MIT"