diff --git a/Cargo.toml b/Cargo.toml index 3d37e50..d6fe9fe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,33 +12,33 @@ categories = ["asynchronous", "cryptography", "network-programming"] [dependencies] futures = "0.3.21" -rcgen = "0.11" +rcgen = "0.12" serde_json = "1.0.81" serde = { version = "1.0.137", features = ["derive"] } -ring = { version = "0.16.20", features = ["std"] } +ring = { version = "0.17", features = ["std"] } base64 = "0.21.0" log = "0.4.17" -webpki-roots = "0.25.2" +webpki-roots = "0.26" pem = "2.0" thiserror = "1.0.31" x509-parser = "0.15" chrono = { version = "0.4.24", default-features = false, features = ["clock"] } url = "2.2.2" async-trait = "0.1.53" -rustls = "0.21" +rustls = "0.23" tokio = { version = "1.20.1", default-features = false } -tokio-rustls = { version = "0.24" } -reqwest = { version = "0.11.19", default-features = false, features = ["rustls-tls"] } +tokio-rustls = { version = "0.26" } +reqwest = { version = "0.12.5", default-features = false, features = ["rustls-tls"] } # Axum -axum-server = { version = "0.5", features = ["tls-rustls"], optional = true } +axum-server = { version = "0.7", features = ["tls-rustls"], optional = true } [dev-dependencies] simple_logger = "4.1" structopt = "0.3.26" clap = { version = "4", features = ["derive"] } -axum = "0.6" +axum = "0.7" tokio = { version="1.19.2", features = ["full"] } tokio-stream = { version="0.1.9", features = ["net"] } tokio-util = { version="0.7.3", features = ["compat"] } diff --git a/examples/high_level.rs b/examples/high_level.rs index 12b4603..8e52966 100644 --- a/examples/high_level.rs +++ b/examples/high_level.rs @@ -1,4 +1,5 @@ use clap::Parser; +use rustls::crypto; use std::net::Ipv6Addr; use std::path::PathBuf; use tokio::io::AsyncWriteExt; @@ -33,6 +34,9 @@ struct Args { #[tokio::main] async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); + + crypto::ring::default_provider().install_default().unwrap(); + let args = Args::parse(); let tcp_listener = tokio::net::TcpListener::bind((Ipv6Addr::UNSPECIFIED, args.port)) diff --git a/examples/high_level_warp.rs b/examples/high_level_warp.rs index c2f3924..a44e803 100644 --- a/examples/high_level_warp.rs +++ b/examples/high_level_warp.rs @@ -1,4 +1,5 @@ use clap::Parser; +use rustls::crypto; use std::net::Ipv6Addr; use std::path::PathBuf; use tokio_rustls_acme::caches::DirCache; @@ -32,6 +33,11 @@ struct Args { #[tokio::main] async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); + + crypto::aws_lc_rs::default_provider() + .install_default() + .unwrap(); + let args = Args::parse(); let tcp_listener = tokio::net::TcpListener::bind((Ipv6Addr::UNSPECIFIED, args.port)) diff --git a/examples/low_level.rs b/examples/low_level.rs index 7202e2e..39a7965 100644 --- a/examples/low_level.rs +++ b/examples/low_level.rs @@ -1,5 +1,5 @@ use clap::Parser; -use rustls::ServerConfig; +use rustls::{crypto, ServerConfig}; use std::net::Ipv6Addr; use std::path::PathBuf; use std::sync::Arc; @@ -34,6 +34,9 @@ struct Args { #[tokio::main] async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); + + crypto::ring::default_provider().install_default().unwrap(); + let args = Args::parse(); let mut state = AcmeConfig::new(args.domains) @@ -42,7 +45,6 @@ async fn main() { .directory_lets_encrypt(args.prod) .state(); let rustls_config = ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_cert_resolver(state.resolver()); let acceptor = state.acceptor(); diff --git a/examples/low_level_axum.rs b/examples/low_level_axum.rs index 12c991d..389052c 100644 --- a/examples/low_level_axum.rs +++ b/examples/low_level_axum.rs @@ -1,6 +1,6 @@ use axum::{routing::get, Router}; use clap::Parser; -use rustls::ServerConfig; +use rustls::{crypto, ServerConfig}; use std::net::{Ipv6Addr, SocketAddr}; use std::path::PathBuf; use std::sync::Arc; @@ -34,6 +34,9 @@ struct Args { #[tokio::main] async fn main() { simple_logger::init_with_level(log::Level::Info).unwrap(); + + crypto::ring::default_provider().install_default().unwrap(); + let args = Args::parse(); let mut state = AcmeConfig::new(args.domains) @@ -42,7 +45,6 @@ async fn main() { .directory_lets_encrypt(args.prod) .state(); let rustls_config = ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_cert_resolver(state.resolver()); let acceptor = state.axum_acceptor(Arc::new(rustls_config)); diff --git a/src/acceptor.rs b/src/acceptor.rs index c2162dc..ceb0f1b 100644 --- a/src/acceptor.rs +++ b/src/acceptor.rs @@ -18,7 +18,6 @@ pub struct AcmeAcceptor { impl AcmeAcceptor { pub(crate) fn new(resolver: Arc) -> Self { let mut config = ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_cert_resolver(resolver); config.alpn_protocols.push(ACME_TLS_ALPN_NAME.to_vec()); diff --git a/src/acme.rs b/src/acme.rs index 92d7f7a..74086ef 100644 --- a/src/acme.rs +++ b/src/acme.rs @@ -4,12 +4,15 @@ use crate::https_helper::{https, HttpsRequestError, Method, Response}; use crate::jose::{key_authorization_sha256, sign, JoseError}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; -use rcgen::{Certificate, CustomExtension, RcgenError, PKCS_ECDSA_P256_SHA256}; +use rcgen::{Certificate, CustomExtension, PKCS_ECDSA_P256_SHA256}; use ring::error::{KeyRejected, Unspecified}; use ring::rand::SystemRandom; use ring::signature::{EcdsaKeyPair, EcdsaSigningAlgorithm, ECDSA_P256_SHA256_FIXED_SIGNING}; -use rustls::sign::{any_ecdsa_type, CertifiedKey}; -use rustls::{ClientConfig, PrivateKey}; +use rustls::{crypto::ring::sign::any_ecdsa_type, sign::CertifiedKey}; +use rustls::{ + pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}, + ClientConfig, +}; use serde::{Deserialize, Serialize}; use serde_json::json; use thiserror::Error; @@ -57,7 +60,8 @@ impl Account { S: AsRef + 'a, I: IntoIterator, { - let key_pair = EcdsaKeyPair::from_pkcs8(ALG, key_pair)?; + let rng = SystemRandom::new(); + let key_pair = EcdsaKeyPair::from_pkcs8(ALG, key_pair, &rng)?; let contact: Vec<&'a str> = contact.into_iter().map(AsRef::::as_ref).collect(); let payload = json!({ "termsOfServiceAgreed": true, @@ -178,8 +182,12 @@ impl Account { params.alg = &PKCS_ECDSA_P256_SHA256; params.custom_extensions = vec![CustomExtension::new_acme_identifier(key_auth.as_ref())]; let cert = Certificate::from_params(params)?; - let pk = any_ecdsa_type(&PrivateKey(cert.serialize_private_key_der())).unwrap(); - let certified_key = CertifiedKey::new(vec![rustls::Certificate(cert.serialize_der()?)], pk); + let pk_bytes = cert.serialize_private_key_der(); + let pk_der: PrivatePkcs8KeyDer = pk_bytes.into(); + let pk_der: PrivateKeyDer = pk_der.into(); + let pk = any_ecdsa_type(&pk_der).unwrap(); + let cert_bytes = cert.serialize_der()?; + let certified_key = CertifiedKey::new(vec![cert_bytes.into()], pk); Ok((challenge, certified_key)) } } @@ -285,7 +293,7 @@ pub enum AcmeError { #[error("io error: {0}")] Io(#[from] std::io::Error), #[error("certificate generation error: {0}")] - Rcgen(#[from] RcgenError), + Rcgen(#[from] rcgen::Error), #[error("JOSE error: {0}")] Jose(#[from] JoseError), #[error("JSON error: {0}")] diff --git a/src/caches/test.rs b/src/caches/test.rs index 4bcc134..fa5a26c 100644 --- a/src/caches/test.rs +++ b/src/caches/test.rs @@ -14,6 +14,7 @@ use std::sync::Arc; /// # use tokio_rustls_acme::{AcmeConfig}; /// # use tokio_rustls_acme::caches::{DirCache, TestCache}; /// # let test_environment = true; +/// rustls::crypto::ring::default_provider().install_default().unwrap(); /// let mut config = AcmeConfig::new(["example.com"]) /// .cache(DirCache::new("./cache")); /// if test_environment { diff --git a/src/config.rs b/src/config.rs index 625f468..48fa67d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -31,6 +31,7 @@ impl AcmeConfig { /// ```rust /// # use tokio_rustls_acme::AcmeConfig; /// use tokio_rustls_acme::caches::DirCache; + /// rustls::crypto::ring::default_provider().install_default().unwrap(); /// let config = AcmeConfig::new(["example.com"]).cache(DirCache::new("./rustls_acme_cache")); /// ``` /// @@ -43,6 +44,7 @@ impl AcmeConfig { /// ```rust /// # use tokio_rustls_acme::AcmeConfig; /// use tokio_rustls_acme::caches::NoCache; + /// rustls::crypto::ring::default_provider().install_default().unwrap(); /// # type EC = std::io::Error; /// # type EA = EC; /// let config: AcmeConfig = AcmeConfig::new(["example.com"]).cache(NoCache::new()); @@ -50,16 +52,17 @@ impl AcmeConfig { /// pub fn new(domains: impl IntoIterator>) -> Self { let mut root_store = RootCertStore::empty(); - root_store.add_trust_anchors(TLS_SERVER_ROOTS.iter().map(|ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - })); + root_store.extend( + TLS_SERVER_ROOTS + .iter() + .map(|ta| rustls::pki_types::TrustAnchor { + subject: ta.subject.clone(), + subject_public_key_info: ta.subject_public_key_info.clone(), + name_constraints: ta.name_constraints.clone(), + }), + ); let client_config = Arc::new( ClientConfig::builder() - .with_safe_defaults() .with_root_certificates(root_store) .with_no_client_auth(), ); diff --git a/src/https_helper.rs b/src/https_helper.rs index ccf1b4b..38e523e 100644 --- a/src/https_helper.rs +++ b/src/https_helper.rs @@ -1,7 +1,6 @@ -use rustls::client::InvalidDnsNameError; -use rustls::ClientConfig; -use std::sync::Arc; +use rustls::{pki_types::InvalidDnsNameError, ClientConfig}; use thiserror::Error; +use std::sync::Arc; pub use reqwest::{Request, Response}; @@ -29,9 +28,8 @@ pub(crate) async fn https( body: Option, ) -> Result { let method: reqwest::Method = method.into(); - let client_config: ClientConfig = client_config.as_ref().clone(); let client = reqwest::ClientBuilder::new() - .use_preconfigured_tls(client_config) + .use_preconfigured_tls(Arc::clone(client_config)) .build()?; let mut request = client.request(method, url.as_ref()); if let Some(body) = body { @@ -70,3 +68,4 @@ pub enum HttpsRequestError { #[error("could not determine host from url")] UndefinedHost, } + diff --git a/src/incoming.rs b/src/incoming.rs index 5062f24..94070e9 100644 --- a/src/incoming.rs +++ b/src/incoming.rs @@ -50,7 +50,6 @@ impl< alpn_protocols: Vec>, ) -> Self { let mut config = ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_cert_resolver(state.resolver()); config.alpn_protocols = alpn_protocols; diff --git a/src/lib.rs b/src/lib.rs index 242d400..36bf9d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,3 +131,25 @@ pub use config::*; pub use incoming::*; pub use resolver::*; pub use state::*; + +#[cfg(test)] +mod test { + use rustls::{crypto, ClientConfig, RootCertStore}; + use std::any::Any; + + #[test] + fn downcast_client_config() { + crypto::ring::default_provider().install_default().unwrap(); + + let mut root_store = RootCertStore::empty(); + let mut tls = Some( + ClientConfig::builder() + .with_root_certificates(root_store) + .with_no_client_auth(), + ); + + if let None = (&mut tls as &mut dyn Any).downcast_mut::>() { + panic!("could not downcast client config"); + } + } +} diff --git a/src/resolver.rs b/src/resolver.rs index 89acdf2..ae57156 100644 --- a/src/resolver.rs +++ b/src/resolver.rs @@ -5,10 +5,12 @@ use std::collections::BTreeMap; use std::sync::Arc; use std::sync::Mutex; +#[derive(Debug)] pub struct ResolvesServerCertAcme { inner: Mutex, } +#[derive(Debug)] struct Inner { cert: Option>, auth_keys: BTreeMap>, diff --git a/src/state.rs b/src/state.rs index 9dfdff3..cd49e41 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,10 +6,10 @@ use crate::{AcmeConfig, Incoming, ResolvesServerCertAcme}; use chrono::{DateTime, TimeZone, Utc}; use futures::future::try_join_all; use futures::{ready, FutureExt, Stream}; -use rcgen::{CertificateParams, DistinguishedName, RcgenError, PKCS_ECDSA_P256_SHA256}; -use rustls::sign::{any_ecdsa_type, CertifiedKey}; -use rustls::Certificate as RustlsCertificate; -use rustls::PrivateKey; +use rcgen::{CertificateParams, DistinguishedName, PKCS_ECDSA_P256_SHA256}; +use rustls::pki_types::CertificateDer as RustlsCertificate; +use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; +use rustls::{crypto::ring::sign::any_ecdsa_type, sign::CertifiedKey}; use std::convert::Infallible; use std::fmt::Debug; use std::future::Future; @@ -74,7 +74,7 @@ pub enum OrderError { #[error("acme error: {0}")] Acme(#[from] AcmeError), #[error("certificate generation error: {0}")] - Rcgen(#[from] RcgenError), + Rcgen(#[from] rcgen::Error), #[error("bad order object: {0:?}")] BadOrder(Order), #[error("bad auth object: {0:?}")] @@ -159,15 +159,16 @@ impl AcmeState { if pems.len() < 2 { return Err(CertParseError::TooFewPem(pems.len())); } - let pk = match any_ecdsa_type(&PrivateKey(pems.remove(0).into_contents())) { + let pk_bytes = pems.remove(0).into_contents(); + let pk_der: PrivatePkcs8KeyDer = pk_bytes.into(); + let pk: PrivateKeyDer = pk_der.into(); + let pk = match any_ecdsa_type(&pk) { Ok(pk) => pk, Err(_) => return Err(CertParseError::InvalidPrivateKey), }; - let cert_chain: Vec = pems - .into_iter() - .map(|p| RustlsCertificate(p.into_contents())) - .collect(); - let validity = match parse_x509_certificate(cert_chain[0].0.as_slice()) { + let cert_chain: Vec = + pems.into_iter().map(|p| p.into_contents().into()).collect(); + let validity = match parse_x509_certificate(cert_chain[0].as_ref()) { Ok((_, cert)) => { let validity = cert.validity(); [validity.not_before, validity.not_after]