1use crate::account::Account;
2use crate::error::*;
3use crate::helpers::*;
4use openssl::hash::MessageDigest;
5use openssl::pkey::PKey;
6use openssl::pkey::Private;
7use openssl::stack::Stack;
8use openssl::x509::extension::SubjectAlternativeName;
9use openssl::x509::X509Name;
10use openssl::x509::X509Req;
11use openssl::x509::X509;
12use serde::Deserialize;
13use serde_json::json;
14use std::sync::Arc;
15use std::time::Duration;
16use tracing::debug;
17use tracing::field;
18use tracing::instrument;
19use tracing::Level;
20use tracing::Span;
21
22#[derive(Deserialize, Debug, Eq, PartialEq)]
26#[serde(rename_all = "camelCase")]
27pub enum OrderStatus {
28 Pending,
29 Ready,
30 Processing,
31 Valid,
32 Invalid,
33}
34
35#[derive(Deserialize, Debug)]
41#[serde(rename_all = "camelCase")]
42pub struct Order {
43 #[serde(skip)]
44 pub(crate) account: Option<Arc<Account>>,
45 #[serde(skip)]
46 pub(crate) url: String,
47
48 pub status: OrderStatus,
50 pub expires: Option<String>,
53 pub identifiers: Vec<Identifier>,
55 pub not_before: Option<String>,
57 pub not_after: Option<String>,
59
60 pub error: Option<ServerError>,
62
63 #[serde(rename = "authorizations")]
64 pub(crate) authorization_urls: Vec<String>,
69 #[serde(rename = "finalize")]
70 pub(crate) finalize_url: String,
73 #[serde(rename = "certificate")]
74 pub(crate) certificate_url: Option<String>,
77}
78
79#[derive(Debug)]
81pub struct OrderBuilder {
82 account: Arc<Account>,
83
84 identifiers: Vec<Identifier>,
85 }
87
88impl OrderBuilder {
89 pub fn new(account: Arc<Account>) -> Self {
90 OrderBuilder {
91 account,
92 identifiers: vec![],
93 }
94 }
95
96 pub fn set_identifiers(&mut self, identifiers: Vec<Identifier>) -> &mut Self {
100 self.identifiers = identifiers;
101 self
102 }
103
104 pub fn add_dns_identifier(&mut self, fqdn: String) -> &mut Self {
107 self.identifiers.push(Identifier {
108 r#type: "dns".to_string(),
109 value: fqdn,
110 });
111 self
112 }
113
114 #[instrument(level = Level::INFO, name = "acme2::OrderBuilder::build", err, skip(self), fields(identifiers = ?self.identifiers, order_url = field::Empty))]
116 pub async fn build(&mut self) -> Result<Order, Error> {
117 let dir = self.account.directory.clone().unwrap();
118
119 let (res, headers) = dir
120 .authenticated_request::<_, Order>(
121 &dir.new_order_url,
122 json!({
123 "identifiers": self.identifiers,
124 }),
125 self.account.private_key.clone().unwrap(),
126 Some(self.account.id.clone()),
127 )
128 .await?;
129
130 let res: Result<Order, Error> = res.into();
131 let mut order = res?;
132
133 let order_url = map_transport_err(
134 headers
135 .get(reqwest::header::LOCATION)
136 .ok_or_else(|| {
137 transport_err("mandatory location header in newOrder response not present")
138 })?
139 .to_str(),
140 )?
141 .to_string();
142 Span::current().record("order_url", &field::display(&order_url));
143
144 order.account = Some(self.account.clone());
145 order.url = order_url;
146
147 Ok(order)
148 }
149}
150
151pub enum Csr {
153 Automatic(PKey<Private>),
157 Custom(X509Req),
160}
161
162fn gen_csr(pkey: &PKey<openssl::pkey::Private>, domains: Vec<String>) -> Result<X509Req, Error> {
163 if domains.is_empty() {
164 return Err(Error::Validation(
165 "at least one domain name needs to be supplied",
166 ));
167 }
168
169 let mut builder = X509Req::builder()?;
170 let name = {
171 let mut name = X509Name::builder()?;
172 name.append_entry_by_text("CN", &domains[0])?;
173 name.build()
174 };
175 builder.set_subject_name(&name)?;
176
177 let san_extension = {
179 let mut san = SubjectAlternativeName::new();
180 for domain in domains.iter() {
181 san.dns(domain);
182 }
183 san.build(&builder.x509v3_context(None))?
184 };
185 let mut stack = Stack::new()?;
186 stack.push(san_extension)?;
187 builder.add_extensions(&stack)?;
188
189 builder.set_pubkey(pkey)?;
190 builder.sign(pkey, MessageDigest::sha256())?;
191
192 Ok(builder.build())
193}
194
195impl Order {
196 #[instrument(level = Level::INFO, name = "acme2::Order::finalize", err, skip(self, csr), fields(order_url = %self.url, status = field::Empty))]
207 pub async fn finalize(&self, csr: Csr) -> Result<Order, Error> {
208 let csr = match csr {
209 Csr::Automatic(pkey) => gen_csr(
210 &pkey,
211 self.identifiers
212 .iter()
213 .map(|f| f.value.clone())
214 .collect::<Vec<_>>(),
215 )?,
216 Csr::Custom(csr) => csr,
217 };
218
219 let csr_b64 = b64(&csr.to_der()?);
220
221 let account = self.account.clone().unwrap();
222 let directory = account.directory.clone().unwrap();
223
224 let (res, _) = directory
225 .authenticated_request::<_, Order>(
226 &self.finalize_url,
227 json!({ "csr": csr_b64 }),
228 account.private_key.clone().unwrap(),
229 Some(account.id.clone()),
230 )
231 .await?;
232 let res: Result<Order, Error> = res.into();
233 let mut order = res?;
234 Span::current().record("status", &field::debug(&order.status));
235 order.account = Some(account.clone());
236 order.url = self.url.clone();
237 Ok(order)
238 }
239
240 #[instrument(level = Level::INFO, name = "acme2::Order::certificate", err, skip(self), fields(order_url = %self.url, has_certificate = field::Empty))]
243 pub async fn certificate(&self) -> Result<Option<Vec<X509>>, Error> {
244 Span::current().record("has_certificate", self.certificate_url.is_some());
245 let certificate_url = match self.certificate_url.clone() {
246 Some(certificate_url) => certificate_url,
247 None => return Ok(None),
248 };
249
250 let account = self.account.clone().unwrap();
251 let directory = account.directory.clone().unwrap();
252
253 let bytes = directory
254 .authenticated_request_bytes(
255 &certificate_url,
256 "",
257 &account.private_key.clone().unwrap(),
258 &Some(account.id.clone()),
259 )
260 .await?
261 .0?;
262
263 Ok(Some(X509::stack_from_pem(&bytes)?))
264 }
265
266 #[instrument(level = Level::DEBUG, name = "acme2::Order::poll", err, skip(self), fields(order_url = %self.url, status = field::Empty))]
270 pub async fn poll(&self) -> Result<Order, Error> {
271 let account = self.account.clone().unwrap();
272 let directory = account.directory.clone().unwrap();
273
274 let (res, _) = directory
275 .authenticated_request::<_, Order>(
276 &self.url,
277 json!(""),
278 account.private_key.clone().unwrap(),
279 Some(account.id.clone()),
280 )
281 .await?;
282 let res: Result<Order, Error> = res.into();
283 let mut order = res?;
284 Span::current().record("status", &field::debug(&order.status));
285 order.account = Some(account.clone());
286 order.url = self.url.clone();
287 Ok(order)
288 }
289
290 #[instrument(level = Level::INFO, name = "acme2::Order::wait_ready", err, skip(self), fields(order_url = %self.url))]
302 pub async fn wait_ready(
303 self,
304 poll_interval: Duration,
305 attempts: usize,
306 ) -> Result<Order, Error> {
307 let mut order = self;
308
309 let mut i: usize = 0;
310
311 while order.status == OrderStatus::Pending {
312 if i >= attempts {
313 return Err(Error::MaxAttemptsExceeded);
314 }
315 debug!(
316 { delay = ?poll_interval },
317 "Order still pending. Waiting to poll."
318 );
319 tokio::time::sleep(poll_interval).await;
320 order = order.poll().await?;
321 i += 1;
322 }
323
324 Ok(order)
325 }
326
327 #[instrument(level = Level::INFO, name = "acme2::Order::wait_ready", err, skip(self), fields(order_url = %self.url))]
340 pub async fn wait_done(self, poll_interval: Duration, attempts: usize) -> Result<Order, Error> {
341 let mut order = self;
342
343 let mut i: usize = 0;
344
345 while order.status == OrderStatus::Pending
346 || order.status == OrderStatus::Ready
347 || order.status == OrderStatus::Processing
348 {
349 if i >= attempts {
350 return Err(Error::MaxAttemptsExceeded);
351 }
352 debug!(
353 { delay = ?poll_interval, status = ?order.status },
354 "Order not done. Waiting to poll."
355 );
356 tokio::time::sleep(poll_interval).await;
357 order = order.poll().await?;
358 i += 1;
359 }
360
361 Ok(order)
362 }
363}