Skip to content
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ allowed_external_types = [
"http::*",
"http_body::*",
"hyper::*",
"rustls::crypto::CryptoProvider",
"rustls_pki_types::*",
"serde_core::*",
"serde_json::*",
Expand Down
14 changes: 13 additions & 1 deletion examples/provision.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#![cfg(any(feature = "ring", feature = "aws-lc-rs"))]
use std::io;

use clap::Parser;
use rustls::crypto::CryptoProvider;
use tracing::info;

use instant_acme::{
Expand All @@ -17,7 +19,7 @@ async fn main() -> anyhow::Result<()> {
// Alternatively, restore an account from serialized credentials by
// using `Account::from_credentials()`.

let (account, credentials) = Account::builder()?
let (account, credentials) = Account::builder(rustls_crypto_provider())?
.create(
&NewAccount {
contact: &[],
Expand Down Expand Up @@ -100,3 +102,13 @@ struct Options {
#[clap(long)]
names: Vec<String>,
}

#[cfg(feature = "aws-lc-rs")]
fn rustls_crypto_provider() -> CryptoProvider {
rustls::crypto::aws_lc_rs::default_provider()
}

#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
fn rustls_crypto_provider() -> CryptoProvider {
rustls::crypto::ring::default_provider()
}
13 changes: 9 additions & 4 deletions src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ use http::{Method, Request};
#[cfg(feature = "hyper-rustls")]
use rustls::RootCertStore;
#[cfg(feature = "hyper-rustls")]
use rustls::crypto::CryptoProvider;
#[cfg(feature = "hyper-rustls")]
use rustls_pki_types::CertificateDer;
#[cfg(feature = "hyper-rustls")]
use rustls_pki_types::pem::PemObject;
Expand Down Expand Up @@ -52,9 +54,9 @@ pub struct Account {
impl Account {
/// Create an account builder with the default HTTP client
#[cfg(feature = "hyper-rustls")]
pub fn builder() -> Result<AccountBuilder, Error> {
pub fn builder(rustls_crypto_provider: CryptoProvider) -> Result<AccountBuilder, Error> {
Ok(AccountBuilder {
http: Box::new(DefaultClient::try_new()?),
http: Box::new(DefaultClient::try_new(rustls_crypto_provider)?),
})
}

Expand All @@ -63,7 +65,10 @@ impl Account {
/// This is useful if your ACME server uses a testing PKI and not a certificate
/// chain issued by a publicly trusted CA.
#[cfg(feature = "hyper-rustls")]
pub fn builder_with_root(pem_path: impl AsRef<Path>) -> Result<AccountBuilder, Error> {
pub fn builder_with_root(
pem_path: impl AsRef<Path>,
rustls_crypto_provider: CryptoProvider,
) -> Result<AccountBuilder, Error> {
let root_der = match CertificateDer::from_pem_file(pem_path) {
Ok(root_der) => root_der,
Err(err) => return Err(Error::Other(err.into())),
Expand All @@ -72,7 +77,7 @@ impl Account {
let mut roots = RootCertStore::empty();
match roots.add(root_der) {
Ok(()) => Ok(AccountBuilder {
http: Box::new(DefaultClient::with_roots(roots)?),
http: Box::new(DefaultClient::with_roots(roots, rustls_crypto_provider)?),
}),
Err(err) => Err(Error::Other(err.into())),
}
Expand Down
39 changes: 32 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::str::FromStr;
#[cfg(feature = "hyper-rustls")]
use std::sync::Arc;
use std::task::{Context, Poll};
use std::time::{Duration, SystemTime};

Expand All @@ -31,6 +33,8 @@ use hyper_util::client::legacy::Client as HyperClient;
use hyper_util::client::legacy::connect::{Connect, HttpConnector};
#[cfg(feature = "hyper-rustls")]
use hyper_util::rt::TokioExecutor;
#[cfg(feature = "hyper-rustls")]
use rustls::crypto::CryptoProvider;
use serde::Serialize;

mod account;
Expand Down Expand Up @@ -200,18 +204,23 @@ struct DefaultClient(HyperClient<hyper_rustls::HttpsConnector<HttpConnector>, Bo

#[cfg(feature = "hyper-rustls")]
impl DefaultClient {
fn try_new() -> Result<Self, Error> {
fn try_new(rustls_crypto_provider: CryptoProvider) -> Result<Self, Error> {
Ok(Self::new(
HttpsConnectorBuilder::new()
.try_with_platform_verifier()
.with_provider_and_platform_verifier(rustls_crypto_provider)
.map_err(|e| Error::Other(Box::new(e)))?,
))
}

fn with_roots(roots: rustls::RootCertStore) -> Result<Self, Error> {
fn with_roots(
roots: rustls::RootCertStore,
rustls_crypto_provider: CryptoProvider,
) -> Result<Self, Error> {
Ok(Self::new(
HttpsConnectorBuilder::new().with_tls_config(
rustls::ClientConfig::builder()
rustls::ClientConfig::builder_with_provider(Arc::new(rustls_crypto_provider))
.with_safe_default_protocol_versions()
.map_err(|e| Error::Other(Box::new(e)))?
.with_root_certificates(roots)
.with_no_client_auth(),
),
Expand Down Expand Up @@ -406,14 +415,20 @@ const CRATE_USER_AGENT: &str = concat!("instant-acme/", env!("CARGO_PKG_VERSION"
const JOSE_JSON: &str = "application/jose+json";
const REPLAY_NONCE: &str = "Replay-Nonce";

#[cfg(all(test, feature = "hyper-rustls"))]
#[cfg(all(
test,
feature = "hyper-rustls",
any(feature = "aws-lc-rs", feature = "ring")
))]
mod tests {
use rustls::crypto::CryptoProvider;

use super::*;

#[tokio::test]
async fn deserialize_old_credentials() -> Result<(), Error> {
const CREDENTIALS: &str = r#"{"id":"id","key_pkcs8":"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJVWC_QzOTCS5vtsJp2IG-UDc8cdDfeoKtxSZxaznM-mhRANCAAQenCPoGgPFTdPJ7VLLKt56RxPlYT1wNXnHc54PEyBg3LxKaH0-sJkX0mL8LyPEdsfL_Oz4TxHkWLJGrXVtNhfH","urls":{"newNonce":"new-nonce","newAccount":"new-acct","newOrder":"new-order", "revokeCert": "revoke-cert"}}"#;
Account::builder()?
Account::builder(rustls_crypto_provider())?
.from_credentials(serde_json::from_str::<AccountCredentials>(CREDENTIALS)?)
.await?;
Ok(())
Expand All @@ -422,9 +437,19 @@ mod tests {
#[tokio::test]
async fn deserialize_new_credentials() -> Result<(), Error> {
const CREDENTIALS: &str = r#"{"id":"id","key_pkcs8":"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgJVWC_QzOTCS5vtsJp2IG-UDc8cdDfeoKtxSZxaznM-mhRANCAAQenCPoGgPFTdPJ7VLLKt56RxPlYT1wNXnHc54PEyBg3LxKaH0-sJkX0mL8LyPEdsfL_Oz4TxHkWLJGrXVtNhfH","directory":"https://acme-staging-v02.api.letsencrypt.org/directory"}"#;
Account::builder()?
Account::builder(rustls_crypto_provider())?
.from_credentials(serde_json::from_str::<AccountCredentials>(CREDENTIALS)?)
.await?;
Ok(())
}

#[cfg(feature = "aws-lc-rs")]
fn rustls_crypto_provider() -> CryptoProvider {
rustls::crypto::aws_lc_rs::default_provider()
}

#[cfg(all(feature = "ring", not(feature = "aws-lc-rs")))]
fn rustls_crypto_provider() -> CryptoProvider {
rustls::crypto::ring::default_provider()
}
}