acme_lite/order/
mod.rs

1//! Order life cycle.
2//!
3//! An order goes through a life cycle of different states that require various actions by
4//! the user. To ensure the user only use appropriate actions, this library have simple façade
5//! structs that wraps the actual [`ApiOrder`].
6//!
7//! 1. First prove ownership:
8//!    * [`NewOrder`] -> [`Auth`]* -> [`Challenge`]
9//! 2. Then submit CSR and download the cert.
10//!    * [`NewOrder`] -> [`CsrOrder`] -> [`CertOrder`]
11//!
12//! \* Possibly multiple auths.
13//!
14//! [`ApiOrder`]: ../api/struct.ApiOrder.html
15//! [`NewOrder`]: struct.NewOrder.html
16//! [`Auth`]: struct.Auth.html
17//! [`Challenge`]: struct.Challenge.html
18//! [`CsrOrder`]: struct.CsrOrder.html
19//! [`CertOrder`]: struct.CertOrder.html
20
21use std::{sync::Arc, time::Duration};
22
23use base64::prelude::*;
24use der::Encode as _;
25use ecdsa::SigningKey;
26use eyre::Context as _;
27use pkcs8::{DecodePrivateKey as _, EncodePrivateKey as _};
28
29use crate::{
30    acc::AccountInner,
31    api::{ApiAuth, ApiEmptyString, ApiFinalize, ApiOrder},
32    cert::{create_csr, Certificate},
33};
34
35mod auth;
36pub use self::auth::{Auth, Challenge};
37
38/// The order wrapped with an outer façade.
39pub(crate) struct Order {
40    inner: Arc<AccountInner>,
41    api_order: ApiOrder,
42    url: String,
43}
44
45impl Order {
46    pub(crate) fn new(inner: &Arc<AccountInner>, api_order: ApiOrder, url: String) -> Self {
47        Order {
48            inner: inner.clone(),
49            api_order,
50            url,
51        }
52    }
53}
54
55/// Helper to refresh an order status (POST-as-GET).
56pub(crate) async fn refresh_order(
57    inner: &Arc<AccountInner>,
58    url: String,
59    want_status: &'static str,
60) -> eyre::Result<Order> {
61    let res = inner.transport.call(&url, &ApiEmptyString).await?;
62
63    // our test rig requires the order to be in `want_status`.
64    // api_order_of is different for test compilation
65    let api_order = api_order_of(res, want_status).await?;
66
67    Ok(Order {
68        inner: inner.clone(),
69        api_order,
70        url,
71    })
72}
73
74#[cfg(not(test))]
75async fn api_order_of(res: reqwest::Response, _want_status: &str) -> eyre::Result<ApiOrder> {
76    Ok(res.json().await?)
77}
78
79#[cfg(test)]
80// our test rig requires the order to be in `want_status`
81async fn api_order_of(res: reqwest::Response, want_status: &str) -> eyre::Result<ApiOrder> {
82    let s = res.text().await?;
83    #[allow(clippy::trivial_regex)]
84    let re = regex::Regex::new("<STATUS>").unwrap();
85    let b = re.replace_all(&s, want_status).into_owned();
86    let api_order: ApiOrder = serde_json::from_str(&b)?;
87    Ok(api_order)
88}
89
90/// A new order created by [`Account::new_order`].
91///
92/// An order is created using one or many domains (a primary `CN` and possible multiple
93/// alt names). All domains in the order must have authorizations ([confirmed ownership])
94/// before the order can progress to submitting a [CSR].
95///
96/// This order façade provides calls to provide such authorizations and to progress the order
97/// when ready.
98///
99/// The ACME API provider might "remember" for a time that you already own a domain, which
100/// means you might not need to prove the ownership every time. Use appropriate methods to
101/// first check whether you really need to handle authorizations.
102///
103/// [`Account::new_order`]: ../struct.Account.html#method.new_order
104/// [confirmed ownership]: ../index.html#domain-ownership
105/// [CSR]: https://en.wikipedia.org/wiki/Certificate_signing_request
106pub struct NewOrder {
107    pub(crate) order: Order,
108}
109
110impl NewOrder {
111    /// Tell if the domains in this order have been authorized.
112    ///
113    /// This doesn't do any calls against the API. You must manually call [`refresh`].
114    ///
115    /// In ACME API terms, the order can either be `ready` or `valid`, which both would
116    /// mean we have passed the authorization stage.
117    ///
118    /// [`refresh`]: struct.NewOrder.html#method.refresh
119    pub fn is_validated(&self) -> bool {
120        self.order.api_order.is_status_ready() || self.order.api_order.is_status_valid()
121    }
122
123    /// If the order [`is_validated`] progress it to a [`CsrOrder`].
124    ///
125    /// This doesn't do any calls against the API. You must manually call [`refresh`].
126    ///
127    /// [`is_validated`]: struct.NewOrder.html#method.is_validated
128    /// [`CsrOrder`]: struct.CsrOrder.html
129    pub fn confirm_validations(&self) -> Option<CsrOrder> {
130        if self.is_validated() {
131            Some(CsrOrder {
132                order: Order::new(
133                    &self.order.inner,
134                    self.order.api_order.clone(),
135                    self.order.url.clone(),
136                ),
137            })
138        } else {
139            None
140        }
141    }
142
143    /// Refresh the order state against the ACME API.
144    ///
145    /// The specification calls this a "POST-as-GET" against the order URL.
146    pub async fn refresh(&mut self) -> eyre::Result<()> {
147        let order = refresh_order(&self.order.inner, self.order.url.clone(), "ready").await?;
148        self.order = order;
149        Ok(())
150    }
151
152    /// Provide the authorizations. The number of authorizations will be the same as
153    /// the number of domains requests, i.e. at least one (the primary CN), but possibly
154    /// more (for alt names).
155    ///
156    /// If the order includes new domain names that have not been authorized before, this
157    /// list might contain a mix of already valid and not yet valid auths.
158    pub async fn authorizations(&self) -> eyre::Result<Vec<Auth>> {
159        let mut result = vec![];
160        if let Some(authorizations) = &self.order.api_order.authorizations {
161            for auth_url in authorizations {
162                let res = self
163                    .order
164                    .inner
165                    .transport
166                    .call(auth_url, &ApiEmptyString)
167                    .await?;
168                let api_auth = res.json::<ApiAuth>().await?;
169                result.push(Auth::new(&self.order.inner, api_auth, auth_url));
170            }
171        }
172        Ok(result)
173    }
174
175    /// Access the underlying JSON object for debugging.
176    pub fn api_order(&self) -> &ApiOrder {
177        &self.order.api_order
178    }
179}
180
181/// An order that is ready for a [CSR] submission.
182///
183/// To submit the CSR is called "finalizing" the order.
184///
185/// To finalize, the user supplies a private key (from which a public key is derived). This
186/// library provides [functions to create private keys], but the user can opt for creating them
187/// in some other way.
188///
189/// This library makes no attempt at validating which key algorithms are used. Unsupported
190/// algorithms will show as an error when finalizing the order. It is up to the ACME API
191/// provider to decide which key algorithms to support.
192///
193/// Right now Let's Encrypt [supports]:
194///
195/// * RSA keys from 2048 to 4096 bits in length
196/// * P-256 and P-384 ECDSA keys
197///
198/// [CSR]: https://en.wikipedia.org/wiki/Certificate_signing_request
199/// [functions to create key pairs]: ../index.html#functions
200/// [supports]: https://letsencrypt.org/docs/integration-guide/#supported-key-algorithms
201pub struct CsrOrder {
202    pub(crate) order: Order,
203}
204
205impl CsrOrder {
206    /// Finalize the order by providing a private key as PEM.
207    ///
208    /// Once the CSR has been submitted, the order goes into a `processing` status,
209    /// where we must poll until the status changes. The `delay` is the
210    /// amount of time to wait between each poll attempt.
211    ///
212    /// This is a convenience wrapper that in turn calls the lower level [`finalize_signing_key`].
213    ///
214    /// [`finalize_signing_key`]: struct.CsrOrder.html#method.finalize_signing_key
215    pub async fn finalize(self, private_key_pem: &str, delay: Duration) -> eyre::Result<CertOrder> {
216        let signing_key =
217            SigningKey::from_pkcs8_pem(private_key_pem).context("Error reading private key PEM")?;
218        self.finalize_signing_key(signing_key, delay).await
219    }
220
221    /// Lower level finalize call that works directly with the openssl crate structures.
222    ///
223    /// Creates the CSR for the domains in the order and submit it to the ACME API.
224    ///
225    /// Once the CSR has been submitted, the order goes into a `processing` status,
226    /// where we must poll until the status changes. The `delay` is the
227    /// amount of time to wait between each poll attempt.
228    pub async fn finalize_signing_key(
229        self,
230        signing_key: p256::ecdsa::SigningKey,
231        delay: Duration,
232    ) -> eyre::Result<CertOrder> {
233        // the domains that we have authorized
234        let domains = self.order.api_order.domains();
235
236        // csr from private key and authorized domains.
237        let csr = create_csr(&signing_key, &domains)?;
238
239        // this is not the same as PEM.
240        let csr_der = csr.to_der()?;
241        let csr_enc = BASE64_URL_SAFE_NO_PAD.encode(&csr_der);
242        let finalize = ApiFinalize { csr: csr_enc };
243
244        let inner = self.order.inner;
245        let order_url = self.order.url;
246        let finalize_url = &self.order.api_order.finalize;
247
248        // if the CSR is invalid, we will get a 4xx code back that
249        // bombs out from this retry_call.
250        inner.transport.call(finalize_url, &finalize).await?;
251
252        // wait for the status to not be processing.
253        // valid -> cert is issued
254        // invalid -> the whole thing is off
255        let order = wait_for_order_status(&inner, &order_url, delay).await?;
256
257        if !order.api_order.is_status_valid() {
258            return Err(eyre::eyre!(
259                "Order is in status: {:?}",
260                order.api_order.status
261            ));
262        }
263
264        Ok(CertOrder { signing_key, order })
265    }
266
267    /// Access the underlying JSON object for debugging.
268    pub fn api_order(&self) -> &ApiOrder {
269        &self.order.api_order
270    }
271}
272
273async fn wait_for_order_status(
274    inner: &Arc<AccountInner>,
275    url: &str,
276    delay: Duration,
277) -> eyre::Result<Order> {
278    loop {
279        let order = refresh_order(inner, url.to_owned(), "valid").await?;
280
281        if !order.api_order.is_status_processing() {
282            return Ok(order);
283        }
284
285        tokio::time::sleep(delay).await;
286    }
287}
288
289/// Order for an issued certificate that is ready to download.
290pub struct CertOrder {
291    signing_key: p256::ecdsa::SigningKey,
292    order: Order,
293}
294
295impl CertOrder {
296    /// Request download of the issued certificate.
297    pub async fn download_cert(self) -> eyre::Result<Certificate> {
298        let url = self
299            .order
300            .api_order
301            .certificate
302            .ok_or_else(|| eyre::eyre!("certificate url"))?;
303
304        let inner = self.order.inner;
305
306        let res = inner.transport.call(&url, &ApiEmptyString).await?;
307
308        let signing_key_pem = self.signing_key.to_pkcs8_pem(der::pem::LineEnding::LF)?;
309
310        let certificate = res.text().await?;
311
312        Ok(Certificate::new(signing_key_pem, certificate))
313    }
314
315    /// Access the underlying JSON object for debugging.
316    pub fn api_order(&self) -> &ApiOrder {
317        &self.order.api_order
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::*;
324    use crate::{cert, Directory, DirectoryUrl};
325
326    #[tokio::test]
327    async fn test_get_authorizations() {
328        let server = crate::test::with_directory_server();
329        let url = DirectoryUrl::Other(&server.dir_url);
330        let dir = Directory::from_url(url).await.unwrap();
331        let acc = dir
332            .register_account(Some(vec!["mailto:foo@bar.com".to_owned()]))
333            .await
334            .unwrap();
335        let ord = acc.new_order("acmetest.example.com", &[]).await.unwrap();
336        let _authorizations = ord.authorizations().await.unwrap();
337    }
338
339    #[tokio::test]
340    async fn test_finalize() {
341        let server = crate::test::with_directory_server();
342        let url = DirectoryUrl::Other(&server.dir_url);
343        let dir = Directory::from_url(url).await.unwrap();
344        let acc = dir
345            .register_account(Some(vec!["mailto:foo@bar.com".to_owned()]))
346            .await
347            .unwrap();
348        let ord = acc.new_order("acmetest.example.com", &[]).await.unwrap();
349        // shortcut auth
350        let ord = CsrOrder { order: ord.order };
351        let signing_key = cert::create_p256_key();
352        let _ord = ord
353            .finalize_signing_key(signing_key, Duration::from_millis(1))
354            .await
355            .unwrap();
356    }
357
358    #[tokio::test]
359    async fn test_download_and_save_cert() {
360        let server = crate::test::with_directory_server();
361        let url = DirectoryUrl::Other(&server.dir_url);
362        let dir = Directory::from_url(url).await.unwrap();
363        let acc = dir
364            .register_account(Some(vec!["mailto:foo@bar.com".to_owned()]))
365            .await
366            .unwrap();
367        let ord = acc.new_order("acmetest.example.com", &[]).await.unwrap();
368
369        // shortcut auth
370        let ord = CsrOrder { order: ord.order };
371        let signing_key = cert::create_p256_key();
372        let ord = ord
373            .finalize_signing_key(signing_key, Duration::from_millis(1))
374            .await
375            .unwrap();
376
377        let cert = ord.download_cert().await.unwrap();
378        assert_eq!("CERT HERE", cert.certificate());
379        assert!(!cert.private_key().is_empty());
380        assert_eq!(cert.valid_days_left().unwrap(), 89);
381    }
382}