async_acme/
rustls_helper.rs1use futures_util::future::try_join_all;
25use rustls::sign::CertifiedKey;
26use std::time::Duration;
27use thiserror::Error;
28
29use crate::{
30 acme::{Account, AcmeError, Auth, Directory, Identifier, Order},
31 cache::AcmeCache,
32 crypto::{gen_acme_cert, get_cert_duration_left, CertBuilder},
33};
34
35#[cfg(feature = "use_async_std")]
36use async_std::task::sleep;
37#[cfg(feature = "use_tokio")]
38use tokio::time::sleep;
39
40pub async fn order<C, F>(
48 set_auth_key: F,
49 directory_url: &str,
50 domains: &[String],
51 cache: Option<&C>,
52 contact: &[String],
53) -> Result<CertifiedKey, OrderError>
54where
55 C: AcmeCache,
56 F: Fn(String, CertifiedKey) -> Result<(), AcmeError>,
57{
58 let directory = Directory::discover(directory_url).await?;
59 let account = Account::load_or_create(directory, cache, contact).await?;
60
61 let (c, key_pem, cert_pem) = drive_order(set_auth_key, domains.to_vec(), account).await?;
62
63 if let Some(dir) = cache {
64 dir.write_certificate(domains, directory_url, &key_pem, &cert_pem)
65 .await
66 .map_err(AcmeError::cache)?;
67 };
68 Ok(c)
69}
70
71pub async fn drive_order<F>(
78 set_auth_key: F,
79 domains: Vec<String>,
80 account: Account,
81) -> Result<(CertifiedKey, String, String), OrderError>
82where
83 F: Fn(String, CertifiedKey) -> Result<(), AcmeError>,
84{
85 let cert = CertBuilder::gen_new(domains.clone())?;
86 let mut order = account.new_order(domains).await?;
87 loop {
88 order = match order {
89 Order::Pending {
90 authorizations,
91 finalize,
92 } => {
93 let auth_futures = authorizations
94 .iter()
95 .map(|url| authorize(&set_auth_key, &account, url));
96 try_join_all(auth_futures).await?;
97 log::info!("completed all authorizations");
98 Order::Ready { finalize }
99 }
100 Order::Ready { finalize } => {
101 log::info!("sending csr");
102 let csr = cert.get_csr()?;
103 account.send_csr(finalize, csr).await?
104 }
105 Order::Valid { certificate } => {
106 log::info!("download certificate");
107 let acme_cert_pem = account.obtain_certificate(certificate).await?;
108 let rd = acme_cert_pem.as_bytes();
109 let pkey_pem = cert.private_key_as_pem_pkcs8();
110 let cert_key = cert.sign(rd).map_err(|_| {
111 AcmeError::Io(std::io::Error::new(
112 std::io::ErrorKind::InvalidData,
113 "could not parse certificate",
114 ))
115 })?;
116 return Ok((cert_key, pkey_pem, acme_cert_pem));
117 }
118 Order::Invalid => return Err(OrderError::BadOrder(order)),
119 }
120 }
121}
122async fn authorize<F>(set_auth_key: &F, account: &Account, url: &str) -> Result<(), OrderError>
123where
124 F: Fn(String, CertifiedKey) -> Result<(), AcmeError>,
125{
126 let (domain, challenge_url) = match account.check_auth(url).await? {
127 Auth::Pending {
128 identifier,
129 challenges,
130 } => {
131 let Identifier::Dns(domain) = identifier;
132 log::info!("trigger challenge for {}", &domain);
133 let (challenge, key_auth) = account.tls_alpn_01(&challenges)?;
134 let auth_key = gen_acme_cert(vec![domain.clone()], key_auth.as_ref())?;
135 set_auth_key(domain.clone(), auth_key)?;
136 account.trigger_challenge(&challenge.url).await?;
137 (domain, challenge.url.clone())
138 }
139 Auth::Valid => return Ok(()),
140 auth => return Err(OrderError::BadAuth(auth)),
141 };
142 for i in 0u8..5 {
143 sleep(Duration::from_secs(1u64 << i)).await;
144 match account.check_auth(url).await? {
145 Auth::Pending { .. } => {
146 log::info!("authorization for {} still pending", &domain);
147 account.trigger_challenge(&challenge_url).await?
148 }
149 Auth::Valid => return Ok(()),
150 auth => return Err(OrderError::BadAuth(auth)),
151 }
152 }
153 Err(OrderError::TooManyAttemptsAuth(domain))
154}
155
156pub fn duration_until_renewal_attempt(cert_key: Option<&CertifiedKey>, err_cnt: usize) -> Duration {
158 let valid_until = cert_key
159 .and_then(|cert_key| cert_key.cert.first())
160 .and_then(|cert| get_cert_duration_left(cert).ok())
161 .unwrap_or_default();
162
163 let wait_secs = valid_until / 2;
164 match err_cnt {
165 0 => wait_secs,
166 err_cnt => wait_secs.max(Duration::from_secs(1 << err_cnt)),
167 }
168}
169
170#[derive(Error, Debug)]
171pub enum OrderError {
172 #[error("acme error: {0}")]
173 Acme(#[from] AcmeError),
174 #[error("certificate generation error: {0}")]
175 Rcgen(#[from] rcgen::Error),
176 #[error("bad order object: {0:?}")]
177 BadOrder(Order),
178 #[error("bad auth object: {0:?}")]
179 BadAuth(Auth),
180 #[error("authorization for {0} failed too many times")]
181 TooManyAttemptsAuth(String),
182}