acme2_eab/
authorization.rs

1use crate::account::Account;
2use crate::error::*;
3use crate::helpers::Identifier;
4use crate::helpers::*;
5use crate::jws::Jwk;
6use crate::order::Order;
7use openssl::hash::hash;
8use openssl::hash::MessageDigest;
9use serde::Deserialize;
10use serde_json::json;
11use std::sync::Arc;
12use std::time::Duration;
13use tracing::debug;
14use tracing::field;
15use tracing::instrument;
16use tracing::Level;
17use tracing::Span;
18
19#[derive(Deserialize, Debug, Eq, PartialEq)]
20#[serde(rename_all = "camelCase")]
21/// The status of this authorization.
22///
23/// Possible values are "pending", "valid", "invalid", "deactivated",
24/// "expired", and "revoked".
25pub enum AuthorizationStatus {
26    Pending,
27    Valid,
28    Invalid,
29    Deactivated,
30    Expired,
31    Revoked,
32}
33
34/// An autorization represents the server's authorization of a certain
35/// domain being represented by an account.
36#[derive(Deserialize, Debug)]
37#[serde(rename_all = "camelCase")]
38pub struct Authorization {
39    #[serde(skip)]
40    pub(crate) account: Option<Arc<Account>>,
41    #[serde(skip)]
42    pub(crate) url: String,
43
44    /// The identifier (domain) that the account is authorized to represent.
45    pub identifier: Identifier,
46    /// The status of this authorization.
47    pub status: AuthorizationStatus,
48    /// The timestamp after which the server will consider this
49    /// authorization invalid.
50    pub expires: Option<String>,
51    /// For pending authorizations, the challenges that the client can
52    /// fulfill in order to prove possession of the identifier. For
53    /// valid authorizations, the challenge that was validated. For
54    /// invalid authorizations, the challenge that was attempted and
55    /// failed.
56    pub challenges: Vec<Challenge>,
57    /// Whether this authorization was created for a wildcard identifier
58    /// (domain).
59    pub wildcard: Option<bool>,
60}
61
62/// The status of this challenge.
63///
64/// Possible values are "pending", "processing", "valid", and "invalid".
65#[derive(Deserialize, Debug, Eq, PartialEq, Clone)]
66#[serde(rename_all = "camelCase")]
67pub enum ChallengeStatus {
68    Pending,
69    Processing,
70    Valid,
71    Invalid,
72}
73
74/// A challenge represents a means for the server to validate
75/// that an account has control over an identifier (domain).
76///
77/// A challenge can only be acquired through an [`Authorization`].
78#[derive(Deserialize, Debug, Clone)]
79#[serde(rename_all = "camelCase")]
80pub struct Challenge {
81    #[serde(skip)]
82    pub(crate) account: Option<Arc<Account>>,
83
84    /// The type of challenge encoded in the object.
85    pub r#type: String,
86    /// The URL to which a response can be posted.
87    pub(crate) url: String,
88    /// The status of this challenge.
89    pub status: ChallengeStatus,
90    /// The time at which the server validated this challenge.
91    pub validated: Option<String>,
92
93    /// Error that occurred while the server was validating the
94    /// challenge, if any.
95    pub error: Option<ServerError>,
96
97    /// A random value that uniquely identifies the challenge.
98    pub token: Option<String>,
99}
100
101impl Order {
102    /// Retrieve all of the [`Authorization`]s needed for this order.
103    ///
104    /// The authorization may already be in a `Valid` state, if an
105    /// authorization for this identifier was already completed through
106    /// a seperate order.
107    #[instrument(level = Level::INFO, name = "acme2::Order::authorizations", err, skip(self), fields(order = %self.url, authorization_urls = ?self.authorization_urls))]
108    pub async fn authorizations(&self) -> Result<Vec<Authorization>, Error> {
109        let account = self.account.clone().unwrap();
110        let directory = account.directory.clone().unwrap();
111
112        let mut authorizations = vec![];
113
114        for authorization_url in self.authorization_urls.clone() {
115            let (res, _) = directory
116                .authenticated_request::<_, Authorization>(
117                    &authorization_url,
118                    "",
119                    account.private_key.clone().unwrap(),
120                    Some(account.id.clone()),
121                )
122                .await?;
123
124            let res: Result<Authorization, Error> = res.into();
125
126            let mut authorization = res?;
127            authorization.account = Some(account.clone());
128            authorization.url = authorization_url;
129            for challenge in &mut authorization.challenges {
130                challenge.account = Some(account.clone())
131            }
132            authorizations.push(authorization)
133        }
134
135        Ok(authorizations)
136    }
137}
138
139impl Authorization {
140    /// Get a certain type of challenge to complete.
141    ///
142    /// Example: `http-01`, or `dns-01`
143    pub fn get_challenge(&self, r#type: &str) -> Option<Challenge> {
144        for challenge in &self.challenges {
145            if challenge.r#type == r#type {
146                return Some(challenge.clone());
147            }
148        }
149        None
150    }
151
152    /// Update the authorization to match the current server state.
153    ///
154    /// Most users should use [`Authorization::wait_done`].
155    #[instrument(level = Level::DEBUG, name = "acme2::Authorization::poll", err, skip(self), fields(url = ?self.url, status = field::Empty))]
156    pub async fn poll(self) -> Result<Authorization, Error> {
157        let account = self.account.clone().unwrap();
158        let directory = account.directory.clone().unwrap();
159
160        let (res, _) = directory
161            .authenticated_request::<_, Authorization>(
162                &self.url,
163                json!(""),
164                account.private_key.clone().unwrap(),
165                Some(account.id.clone()),
166            )
167            .await?;
168        let res: Result<Authorization, Error> = res.into();
169        let mut authorization = res?;
170        authorization.url = self.url.clone();
171        authorization.account = Some(account.clone());
172        Span::current().record("status", &field::debug(&authorization.status));
173        Ok(authorization)
174    }
175
176    /// Wait for the authorization to go into a state other than
177    /// [`AuthorizationStatus::Pending`].
178    ///
179    /// This will only happen once one of the challenges in an authorization
180    /// is completed. You can use [`Challenge::wait_done`] to wait until
181    /// this is the case.
182    ///
183    /// Will complete immediately if the authorization is already in a
184    /// state other than [`AuthorizationStatus::Pending`].
185    ///
186    /// Specify the interval at which to poll the acme server, and how often to
187    /// attempt polling before timing out. Polling should not happen faster than
188    /// about every 5 seconds to avoid rate limits in the acme server.
189    #[instrument(level = Level::INFO, name = "acme2::Authorization::wait_done", err, skip(self), fields(url = ?self.url))]
190    pub async fn wait_done(
191        self,
192        poll_interval: Duration,
193        attempts: usize,
194    ) -> Result<Authorization, Error> {
195        let mut authorization = self;
196
197        let mut i: usize = 0;
198
199        while authorization.status == AuthorizationStatus::Pending {
200            if i >= attempts {
201                return Err(Error::MaxAttemptsExceeded);
202            }
203            debug!(
204              { delay = ?poll_interval },
205              "Authorization still pending. Waiting to poll."
206            );
207            tokio::time::sleep(poll_interval).await;
208            authorization = authorization.poll().await?;
209            i += 1;
210        }
211
212        Ok(authorization)
213    }
214}
215
216impl Challenge {
217    /// The key authorization is the token that the HTTP01 challenge
218    /// should be serving for the ACME server to inspect.
219    pub fn key_authorization(&self) -> Result<Option<String>, Error> {
220        if let Some(token) = self.token.clone() {
221            let account = self.account.clone().unwrap();
222
223            let key_authorization = format!(
224                "{}.{}",
225                token,
226                b64(&hash(
227                    MessageDigest::sha256(),
228                    &serde_json::to_string(&Jwk::new(&account.private_key.clone().unwrap()))?
229                        .into_bytes()
230                )?)
231            );
232
233            Ok(Some(key_authorization))
234        } else {
235            Ok(None)
236        }
237    }
238
239    /// The encoded key authorization is the token that the DNS01
240    /// challenge should be serving for the ACME server to inspect.
241    pub fn key_authorization_encoded(&self) -> Result<Option<String>, Error> {
242        let key_authorization = self.key_authorization()?;
243
244        if let Some(key_authorization) = key_authorization {
245            Ok(Some(b64(&hash(
246                MessageDigest::sha256(),
247                &key_authorization.into_bytes(),
248            )?)))
249        } else {
250            Ok(None)
251        }
252    }
253
254    /// Initiate validation of the challenge by the ACME server.
255    ///
256    /// Before calling this method, you should have set up your challenge token
257    /// so it is available for the ACME server to check.
258    ///
259    /// In most cases this will not complete immediately. You should always
260    /// call [`Challenge::wait_done`] after this operation to wait until the
261    /// ACME server has finished validation.
262    #[instrument(level = Level::INFO, name = "acme2::Challenge::validate", err, skip(self), fields(url = ?self.url, status = field::Empty))]
263    pub async fn validate(&self) -> Result<Challenge, Error> {
264        let account = self.account.clone().unwrap();
265        let directory = account.directory.clone().unwrap();
266
267        let (res, _) = directory
268            .authenticated_request::<_, Challenge>(
269                &self.url,
270                json!({}),
271                account.private_key.clone().unwrap(),
272                Some(account.id.clone()),
273            )
274            .await?;
275        let res: Result<Challenge, Error> = res.into();
276        let mut challenge = res?;
277        challenge.account = Some(account.clone());
278        Span::current().record("status", &field::debug(&challenge.status));
279
280        Ok(challenge)
281    }
282
283    /// Update the challenge to match the current server state.
284    ///
285    /// Most users should use [`Challenge::wait_done`].
286    #[instrument(level = Level::DEBUG, name = "acme2::Challenge::poll", err, skip(self), fields(url = ?self.url, status = field::Empty))]
287    pub async fn poll(&self) -> Result<Challenge, Error> {
288        let account = self.account.clone().unwrap();
289        let directory = account.directory.clone().unwrap();
290
291        let (res, _) = directory
292            .authenticated_request::<_, Challenge>(
293                &self.url,
294                json!(""),
295                account.private_key.clone().unwrap(),
296                Some(account.id.clone()),
297            )
298            .await?;
299        let res: Result<Challenge, Error> = res.into();
300        let mut challenge = res?;
301        challenge.account = Some(account.clone());
302        Span::current().record("status", &field::debug(&challenge.status));
303        Ok(challenge)
304    }
305
306    /// Wait for the authorization to go into the [`AuthorizationStatus::Valid`]
307    /// or [`AuthorizationStatus::Invalid`] state.
308    ///
309    /// Will complete immediately if the authorization is already
310    /// in one of these states.
311    ///
312    /// Specify the interval at which to poll the acme server, and how often to
313    /// attempt polling before timing out. Polling should not happen faster than
314    /// about every 5 seconds to avoid rate limits in the acme server.
315    #[instrument(level = Level::INFO, name = "acme2::Challenge::wait_done", err, skip(self), fields(url = ?self.url))]
316    pub async fn wait_done(
317        self,
318        poll_interval: Duration,
319        attempts: usize,
320    ) -> Result<Challenge, Error> {
321        let mut challenge = self;
322
323        let mut i: usize = 0;
324
325        while challenge.status == ChallengeStatus::Pending
326            || challenge.status == ChallengeStatus::Processing
327        {
328            if i >= attempts {
329                return Err(Error::MaxAttemptsExceeded);
330            }
331            debug!(
332              { delay = ?poll_interval, status = ?challenge.status },
333              "Challenge not done. Waiting to poll."
334            );
335            tokio::time::sleep(poll_interval).await;
336            challenge = challenge.poll().await?;
337            i += 1;
338        }
339
340        Ok(challenge)
341    }
342}