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