diff --git a/Cargo.toml b/Cargo.toml index 9214af3..ee5d200 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hyper-rustls" -version = "0.24.2" +version = "0.25.0-alpha.1" edition = "2021" rust-version = "1.63" license = "Apache-2.0 OR ISC OR MIT" @@ -14,17 +14,18 @@ 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 } +rustls-native-certs = { version = "=0.7.0-alpha.1", optional = true } +rustls = { version = "=0.22.0-alpha.4", 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.2", default-features = false } +webpki-roots = { version = "=0.26.0-alpha.1", optional = true } futures-util = { version = "0.3", default-features = false } +pki-types = { package = "rustls-pki-types", version = "0.2.1" } [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.4", default-features = false, features = ["tls12"] } +rustls-pemfile = "=2.0.0-alpha.1" tokio = { version = "1.0", features = ["io-std", "macros", "net", "rt-multi-thread"] } [features] @@ -37,17 +38,23 @@ native-tokio = ["tokio-runtime", "rustls-native-certs"] tokio-runtime = ["hyper/runtime"] tls12 = ["tokio-rustls/tls12", "rustls/tls12"] logging = ["log", "tokio-rustls/logging", "rustls/logging"] +ring = ["rustls/ring"] [[example]] name = "client" path = "examples/client.rs" -required-features = ["native-tokio", "http1"] +required-features = ["native-tokio", "http1", "ring"] [[example]] name = "server" path = "examples/server.rs" -required-features = ["tokio-runtime", "acceptor"] +required-features = ["tokio-runtime", "acceptor", "ring"] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] + +[patch.crates-io] +rustls-native-certs = { git = 'https://github.com/rustls/rustls-native-certs' } +tokio-rustls = { git = 'https://github.com/rustls/tokio-rustls' } +rustls-pemfile = { git = 'https://github.com/rustls/pemfile' } 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..1fd18fd 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,27 @@ 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()) + let certs: Vec> = + rustls_pemfile::certs(&mut reader).collect::, _>>()?; + Ok(certs) } // 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)?.ok_or(error( + "expected a valid private key from the key file".into(), + )) } diff --git a/src/acceptor/builder.rs b/src/acceptor/builder.rs index 9b95a82..8179c5d 100644 --- a/src/acceptor/builder.rs +++ b/src/acceptor/builder.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use hyper::server::conn::AddrIncoming; -use rustls::ServerConfig; +use rustls::{crypto::CryptoProvider, ServerConfig}; use super::TlsAcceptor; /// Builder for [`TlsAcceptor`] @@ -21,14 +21,16 @@ impl AcceptorBuilder { AcceptorBuilder(WantsAlpn(config)) } - /// Use rustls [defaults][with_safe_defaults] without [client authentication][with_no_client_auth] + #[cfg(feature = "ring")] + /// Use rustls [defaults][with_safe_defaults] without [client authentication][with_no_client_auth], + /// with ring as the provided crypto suites /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults /// [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: pki_types::PrivateKeyDer<'static>, ) -> Result, rustls::Error> { Ok(AcceptorBuilder(WantsAlpn( ServerConfig::builder() @@ -37,6 +39,25 @@ impl AcceptorBuilder { .with_single_cert(cert_chain, key_der)?, ))) } + + /// Use rustls [defaults][with_safe_defaults] without [client authentication][with_no_client_auth], + /// and the user has to provide a crypto suite implemention + /// + /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults + /// [with_no_client_auth]: rustls::ConfigBuilder::with_no_client_auth + pub fn with_provider_and_single_cert( + self, + provider: &'static dyn CryptoProvider, + cert_chain: Vec>, + key_der: pki_types::PrivateKeyDer<'static>, + ) -> Result, rustls::Error> { + Ok(AcceptorBuilder(WantsAlpn( + ServerConfig::builder_with_provider(provider) + .with_safe_defaults() + .with_no_client_auth() + .with_single_cert(cert_chain, key_der)?, + ))) + } } impl Default for AcceptorBuilder { diff --git a/src/config.rs b/src/config.rs index 1c74c45..b7a598f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,8 @@ -use rustls::client::WantsTransparencyPolicyOrClientCert; use rustls::{ClientConfig, ConfigBuilder, WantsVerifier}; +#[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] +use rustls::client::WantsClientCert; + /// Methods for configuring roots /// /// This adds methods (gated by crate features) for easily configuring @@ -13,35 +15,30 @@ 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.as_ref().into()) { Ok(_) => valid_count += 1, Err(err) => { - crate::log::trace!("invalid cert der {:?}", cert.0); + crate::log::trace!("invalid cert der {:?}", cert.as_ref()); crate::log::debug!("certificate parsing failed: {:?}", err); invalid_count += 1 } @@ -65,18 +62,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/builder.rs b/src/connector/builder.rs index e0f06d0..66d122c 100644 --- a/src/connector/builder.rs +++ b/src/connector/builder.rs @@ -7,6 +7,9 @@ use crate::config::ConfigBuilderExt; #[cfg(feature = "tokio-runtime")] use hyper::client::HttpConnector; +#[cfg(any(feature = "rustls-native-certs", feature = "webpki-roots"))] +use rustls::crypto::CryptoProvider; + /// A builder for an [`HttpsConnector`] /// /// This makes configuration flexible and explicit and ensures connector @@ -52,12 +55,12 @@ impl ConnectorBuilder { } /// Shorthand for using rustls' [safe defaults][with_safe_defaults] - /// and native roots + /// and native roots, with ring as the provided crypto suites /// /// See [`ConfigBuilderExt::with_native_roots`] /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults - #[cfg(feature = "rustls-native-certs")] + #[cfg(all(feature = "rustls-native-certs", feature = "ring"))] #[cfg_attr(docsrs, doc(cfg(feature = "rustls-native-certs")))] pub fn with_native_roots(self) -> std::io::Result> { Ok(self.with_tls_config( @@ -69,12 +72,12 @@ impl ConnectorBuilder { } /// Shorthand for using rustls' [safe defaults][with_safe_defaults] - /// and Mozilla roots + /// and Mozilla roots, with ring as the provided crypto suites /// /// See [`ConfigBuilderExt::with_webpki_roots`] /// /// [with_safe_defaults]: rustls::ConfigBuilder::with_safe_defaults - #[cfg(feature = "webpki-roots")] + #[cfg(all(feature = "webpki-roots", feature = "ring"))] #[cfg_attr(docsrs, doc(cfg(feature = "webpki-roots")))] pub fn with_webpki_roots(self) -> ConnectorBuilder { self.with_tls_config( @@ -84,6 +87,46 @@ impl ConnectorBuilder { .with_no_client_auth(), ) } + + /// Shorthand for using rustls' [safe defaults][with_safe_defaults] + /// and native roots, with user's provided crypto suites + /// + /// 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, with user's provided crypto suites + /// + /// 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 { @@ -292,7 +335,7 @@ mod tests { } #[test] - #[cfg(feature = "http1")] + #[cfg(all(feature = "http1", feature = "ring"))] #[should_panic(expected = "ALPN protocols should not be pre-defined")] fn test_reject_predefined_alpn() { let roots = rustls::RootCertStore::empty(); @@ -309,7 +352,7 @@ mod tests { } #[test] - #[cfg(all(feature = "http1", feature = "http2"))] + #[cfg(all(feature = "http1", feature = "http2", feature = "ring"))] fn test_alpn() { let roots = rustls::RootCertStore::empty(); let tls_config = rustls::ClientConfig::builder() @@ -353,7 +396,7 @@ mod tests { } #[test] - #[cfg(all(not(feature = "http1"), feature = "http2"))] + #[cfg(all(not(feature = "http1"), feature = "http2", feature = "ring"))] fn test_alpn_http2() { let roots = rustls::RootCertStore::empty(); let tls_config = rustls::ClientConfig::builder() diff --git a/src/lib.rs b/src/lib.rs index d2b8ab4..4cbb54f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ //! ## Example client //! //! ```no_run -//! # #[cfg(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1"))] +//! # #[cfg(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1", feature = "ring"))] //! # fn main() { //! use hyper::{Body, Client, StatusCode, Uri}; //! @@ -24,14 +24,14 @@ //! let res = rt.block_on(client.get(url)).unwrap(); //! assert_eq!(res.status(), StatusCode::OK); //! # } -//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1")))] +//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1", feature = "ring")))] //! # fn main() {} //! ``` //! //! ## Example server //! //! ```no_run -//! # #[cfg(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1", feature = "acceptor"))] +//! # #[cfg(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1", feature = "acceptor", feature = "ring"))] //! # fn main() { //! use hyper::server::conn::AddrIncoming; //! use hyper::service::{make_service_fn, service_fn}; @@ -48,16 +48,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") @@ -74,7 +72,7 @@ //! let server = Server::builder(acceptor).serve(service); //! // server.await.unwrap(); //! # } -//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1")))] +//! # #[cfg(not(all(feature = "rustls-native-certs", feature = "tokio-runtime", feature = "http1", feature = "ring")))] //! # fn main() {} //! ```