diff --git a/Cargo.toml b/Cargo.toml index 73f1643..8d19f6e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,14 +11,15 @@ repository = "https://github.com/ctz/hyper-rustls" [dependencies] log = "0.4.4" -ct-logs = { version = "^0.8", optional = true } +ct-logs = { version = "^0.9", optional = true } hyper = { version = "0.14", default-features = false, features = ["client", "http1"] } -rustls = "0.19" -rustls-native-certs = { version = "0.5.0", optional = true } +rustls = { git = "https://github.com/ctz/rustls" } +rustls-native-certs = { git = "https://github.com/djc/rustls-native-certs", rev = "6116ef59f5825b0ec74a38807635a70433d68c27", optional = true } +rustls-pemfile = { version = "0.2.1" } tokio = "1.0" -tokio-rustls = "0.22" -webpki = "0.21.0" -webpki-roots = { version = "0.21", optional = true } +tokio-rustls = { version = "0.23", git = "https://github.com/tokio-rs/tls", rev = "b433932bf1025960e5b99f353cf8eee4ce2f08f3" } +webpki = "0.22.0" +webpki-roots = { version = "0.22", optional = true } [dev-dependencies] async-stream = "0.3.0" @@ -45,3 +46,6 @@ required-features = ["tokio-runtime"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[patch."crates-io"] +rustls = { git = "https://github.com/ctz/rustls" } diff --git a/examples/client.rs b/examples/client.rs index 4e97d9d..49e2c0a 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -3,6 +3,8 @@ //! First parameter is the mandatory URL to GET. //! Second parameter is an optional path to CA store. use hyper::{body::to_bytes, client, Body, Uri}; +use rustls::RootCertStore; + use std::str::FromStr; use std::{env, fs, io}; @@ -46,11 +48,16 @@ async fn run_client() -> io::Result<()> { // Build an HTTP connector which supports HTTPS too. let mut http = client::HttpConnector::new(); http.enforce_http(false); - // Build a TLS client, using the custom CA store for lookups. - let mut tls = rustls::ClientConfig::new(); - tls.root_store - .add_pem_file(rd) + // Read trust roots + let certs = rustls_pemfile::certs(rd) .map_err(|_| error("failed to load custom CA store".into()))?; + let mut roots = RootCertStore::empty(); + roots.add_parsable_certificates(&certs); + // Build a TLS client, using the custom CA store for lookups. + let tls = rustls::ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots, &ct_logs::LOGS) + .with_no_client_auth(); // Join the above part into an HTTPS connector. hyper_rustls::HttpsConnector::from((http, tls)) } diff --git a/examples/server.rs b/examples/server.rs index 490dc12..11dfdef 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -4,15 +4,15 @@ //! Certificate and private key are hardcoded to sample files. //! hyper will automatically use HTTP/2 if a client starts talking HTTP/2, //! otherwise HTTP/1.1 will be used. +use std::pin::Pin; +use std::vec::Vec; +use std::{env, fs, io, sync}; + use async_stream::stream; use core::task::{Context, Poll}; use futures_util::{future::TryFutureExt, stream::Stream}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Method, Request, Response, Server, StatusCode}; -use rustls::internal::pemfile; -use std::pin::Pin; -use std::vec::Vec; -use std::{env, fs, io, sync}; use tokio::net::{TcpListener, TcpStream}; use tokio_rustls::server::TlsStream; use tokio_rustls::TlsAcceptor; @@ -45,12 +45,13 @@ async fn run_server() -> Result<(), Box> { // Load private key. let key = load_private_key("examples/sample.rsa")?; // Do not use client certificate authentication. - let mut cfg = rustls::ServerConfig::new(rustls::NoClientAuth::new()); - // Select a certificate to use. - cfg.set_single_cert(certs, key) + let mut cfg = rustls::ServerConfig::builder() + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(certs, key) .map_err(|e| error(format!("{}", e)))?; // Configure ALPN to accept HTTP/2, HTTP/1.1 in that order. - cfg.set_protocols(&[b"h2".to_vec(), b"http/1.1".to_vec()]); + cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; sync::Arc::new(cfg) }; @@ -127,7 +128,9 @@ fn load_certs(filename: &str) -> io::Result> { let mut reader = io::BufReader::new(certfile); // Load and return certificate. - pemfile::certs(&mut reader).map_err(|_| error("failed to load certificate".into())) + let certs = rustls_pemfile::certs(&mut reader) + .map_err(|_| error("failed to load certificate".into()))?; + Ok(certs.into_iter().map(rustls::Certificate).collect()) } // Load private key from file. @@ -138,10 +141,11 @@ fn load_private_key(filename: &str) -> io::Result { let mut reader = io::BufReader::new(keyfile); // Load and return a single private key. - let keys = pemfile::rsa_private_keys(&mut reader) + 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(keys[0].clone()) + + Ok(rustls::PrivateKey(keys[0].clone())) } diff --git a/src/connector.rs b/src/connector.rs index 76dbf52..fbd03a9 100644 --- a/src/connector.rs +++ b/src/connector.rs @@ -1,15 +1,16 @@ -#[cfg(feature = "tokio-runtime")] -use hyper::client::connect::HttpConnector; -use hyper::{client::connect::Connection, service::Service, Uri}; -use rustls::ClientConfig; use std::future::Future; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll}; use std::{fmt, io}; +use std::convert::TryFrom; + +#[cfg(feature = "tokio-runtime")] +use hyper::client::connect::HttpConnector; +use hyper::{client::connect::Connection, service::Service, Uri}; +use rustls::{ClientConfig, RootCertStore}; use tokio::io::{AsyncRead, AsyncWrite}; use tokio_rustls::TlsConnector; -use webpki::DNSNameRef; use crate::stream::MaybeHttpsStream; @@ -31,38 +32,42 @@ impl HttpsConnector { #[cfg(feature = "rustls-native-certs")] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] pub fn with_native_roots() -> Self { - let mut config = ClientConfig::new(); - config.root_store = match rustls_native_certs::load_native_certs() { - Ok(store) => store, - Err((Some(store), err)) => { - log::warn!("Could not load all certificates: {:?}", err); - store - } - Err((None, err)) => Err(err).expect("cannot access native cert store"), + let certs = match rustls_native_certs::load_native_certs() { + Ok(certs) => certs, + Err(err) => Err(err).expect("cannot access native cert store"), }; - if config.root_store.is_empty() { + + if certs.is_empty() { panic!("no CA certificates found"); } - Self::build(config) + + let mut roots = RootCertStore::empty(); + for cert in certs { + roots.add_parsable_certificates(&[cert.0]); + } + + Self::build(roots) } /// Construct a new `HttpsConnector` using the `webpki_roots` #[cfg(feature = "webpki-roots")] #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] pub fn with_webpki_roots() -> Self { - let mut config = ClientConfig::new(); - config - .root_store - .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); - Self::build(config) + let mut roots = rustls::RootCertStore::empty(); + roots.add_server_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.0); + Self::build(roots) } - fn build(mut config: ClientConfig) -> Self { + fn build(roots: rustls::RootCertStore) -> Self { let mut http = HttpConnector::new(); http.enforce_http(false); + let mut config = ClientConfig::builder() + .with_safe_defaults() + .with_root_certificates(roots, &ct_logs::LOGS) + .with_no_client_auth(); + config.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()]; - config.ct_logs = Some(&ct_logs::LOGS); (http, config).into() } } @@ -127,7 +132,7 @@ where let f = async move { let tcp = connecting_future.await.map_err(Into::into)?; let connector = TlsConnector::from(cfg); - let dnsname = DNSNameRef::try_from_ascii_str(&hostname) + let dnsname = rustls::ServerName::try_from(hostname.as_str()) .map_err(|_| io::Error::new(io::ErrorKind::Other, "invalid dnsname"))?; let tls = connector .connect(dnsname, tcp) diff --git a/src/stream.rs b/src/stream.rs index ff5c094..6275f2f 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -6,9 +6,9 @@ use std::task::{Context, Poll}; use hyper::client::connect::{Connected, Connection}; -use rustls::Session; use tokio::io::{AsyncRead, AsyncWrite, ReadBuf}; use tokio_rustls::client::TlsStream; +use tokio_rustls::rustls::{Connection as _}; /// A stream that might be protected with TLS. pub enum MaybeHttpsStream { @@ -24,7 +24,7 @@ impl Connection for MaybeHttpsSt MaybeHttpsStream::Http(s) => s.connected(), MaybeHttpsStream::Https(s) => { let (tcp, tls) = s.get_ref(); - if tls.get_alpn_protocol() == Some(b"h2") { + if tls.alpn_protocol() == Some(b"h2") { tcp.connected().negotiated_h2() } else { tcp.connected()