instant_acme/
account.rs

1#[cfg(feature = "hyper-rustls")]
2use std::path::Path;
3use std::sync::Arc;
4#[cfg(feature = "time")]
5use std::time::{Duration, SystemTime};
6
7use base64::prelude::{BASE64_URL_SAFE_NO_PAD, Engine};
8use http::header::LOCATION;
9#[cfg(feature = "time")]
10use http::header::USER_AGENT;
11#[cfg(feature = "time")]
12use http::{Method, Request};
13#[cfg(feature = "hyper-rustls")]
14use rustls::RootCertStore;
15#[cfg(feature = "hyper-rustls")]
16use rustls_pki_types::CertificateDer;
17#[cfg(feature = "hyper-rustls")]
18use rustls_pki_types::pem::PemObject;
19use rustls_pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer};
20use serde::de::DeserializeOwned;
21use serde::{Deserialize, Serialize};
22
23#[cfg(feature = "hyper-rustls")]
24use crate::DefaultClient;
25use crate::order::Order;
26use crate::types::{
27    AccountCredentials, AuthorizationStatus, Empty, Header, JoseJson, Jwk, KeyOrKeyId, NewAccount,
28    NewAccountPayload, NewOrder, OrderState, Problem, ProfileMeta, RevocationRequest, Signer,
29    SigningAlgorithm,
30};
31#[cfg(feature = "time")]
32use crate::types::{CertificateIdentifier, RenewalInfo};
33#[cfg(feature = "time")]
34use crate::{BodyWrapper, CRATE_USER_AGENT, retry_after};
35use crate::{BytesResponse, Client, Error, HttpClient, crypto, nonce_from_response};
36
37/// An ACME account as described in RFC 8555 (section 7.1.2)
38///
39/// Create an [`Account`] via [`Account::builder()`] or [`Account::builder_with_http()`],
40/// then use [`AccountBuilder::create()`] to create a new account or restore one from
41/// serialized data by passing deserialized [`AccountCredentials`] to
42/// [`AccountBuilder::from_credentials()`].
43///
44/// The [`Account`] type is cheap to clone.
45///
46/// <https://datatracker.ietf.org/doc/html/rfc8555#section-7.1.2>
47#[derive(Clone)]
48pub struct Account {
49    inner: Arc<AccountInner>,
50}
51
52impl Account {
53    /// Create an account builder with the default HTTP client
54    #[cfg(feature = "hyper-rustls")]
55    pub fn builder() -> Result<AccountBuilder, Error> {
56        Ok(AccountBuilder {
57            http: Box::new(DefaultClient::try_new()?),
58        })
59    }
60
61    /// Create an account builder with an HTTP client configured using a custom root CA
62    ///
63    /// This is useful if your ACME server uses a testing PKI and not a certificate
64    /// chain issued by a publicly trusted CA.
65    #[cfg(feature = "hyper-rustls")]
66    pub fn builder_with_root(pem_path: impl AsRef<Path>) -> Result<AccountBuilder, Error> {
67        let root_der = match CertificateDer::from_pem_file(pem_path) {
68            Ok(root_der) => root_der,
69            Err(err) => return Err(Error::Other(err.into())),
70        };
71
72        let mut roots = RootCertStore::empty();
73        match roots.add(root_der) {
74            Ok(()) => Ok(AccountBuilder {
75                http: Box::new(DefaultClient::with_roots(roots)?),
76            }),
77            Err(err) => Err(Error::Other(err.into())),
78        }
79    }
80
81    /// Create an account builder with the given HTTP client
82    pub fn builder_with_http(http: Box<dyn HttpClient>) -> AccountBuilder {
83        AccountBuilder { http }
84    }
85
86    /// Create a new order based on the given [`NewOrder`]
87    ///
88    /// Returns an [`Order`] instance. Use the [`Order::state()`] method to inspect its state.
89    pub async fn new_order(&self, order: &NewOrder<'_>) -> Result<Order, Error> {
90        if order.replaces.is_some() && self.inner.client.directory.renewal_info.is_none() {
91            return Err(Error::Unsupported("ACME renewal information (ARI)"));
92        }
93
94        let rsp = self
95            .inner
96            .post(Some(order), None, &self.inner.client.directory.new_order)
97            .await?;
98
99        let nonce = nonce_from_response(&rsp);
100        let order_url = rsp
101            .parts
102            .headers
103            .get(LOCATION)
104            .and_then(|hv| hv.to_str().ok())
105            .map(|s| s.to_owned());
106
107        // We return errors from Problem::check before emitting an error for any further
108        // issues (e.g. no order URL, missing replacement field).
109        let state = Problem::check::<OrderState>(rsp).await?;
110
111        // Per the ARI spec:
112        // "If the Server accepts a new-order request with a "replaces" field, it MUST reflect
113        // that field in the response and in subsequent requests for the corresponding Order
114        // object."
115        if order.replaces.is_some() && order.replaces != state.replaces {
116            return Err(Error::Other(
117                format!(
118                    "replaces field mismatch: expected {expected:?}, found {found:?}",
119                    expected = order.replaces,
120                    found = state.replaces,
121                )
122                .into(),
123            ));
124        }
125
126        Ok(Order {
127            account: self.inner.clone(),
128            nonce,
129            retry_after: None,
130            state,
131            url: order_url.ok_or("no order URL found")?,
132        })
133    }
134
135    /// Fetch the order state for an existing order based on the given `url`
136    ///
137    /// This might fail if the given URL's order belongs to a different account.
138    ///
139    /// Returns an [`Order`] instance. Use the [`Order::state`] method to inspect its state.
140    pub async fn order(&self, url: String) -> Result<Order, Error> {
141        let rsp = self.inner.post(None::<&Empty>, None, &url).await?;
142        Ok(Order {
143            account: self.inner.clone(),
144            nonce: nonce_from_response(&rsp),
145            retry_after: None,
146            // Order of fields matters! We return errors from Problem::check
147            // before emitting an error if there is no order url. Or the
148            // simple no url error hides the causing error in `Problem::check`.
149            state: Problem::check::<OrderState>(rsp).await?,
150            url,
151        })
152    }
153
154    /// Revokes a previously issued certificate
155    pub async fn revoke<'a>(&'a self, payload: &RevocationRequest<'a>) -> Result<(), Error> {
156        let Some(revoke_url) = self.inner.client.directory.revoke_cert.as_deref() else {
157            // This happens because the current account credentials were deserialized from an
158            // older version which only serialized a subset of the directory URLs. You should
159            // make sure the account credentials include a `directory` field containing a
160            // string with the server's directory URL.
161            return Err("no revokeCert URL found".into());
162        };
163
164        let rsp = self.inner.post(Some(payload), None, revoke_url).await?;
165        // The body is empty if the request was successful
166        let _ = Problem::from_response(rsp).await?;
167        Ok(())
168    }
169
170    /// Fetch `RenewalInfo` with a suggested window for renewing an identified certificate
171    ///
172    /// Clients may use this information to determine when to renew a certificate. If the renewal
173    /// window starts in the past, then renewal should be attempted immediately. Otherwise, a
174    /// uniformly random point between the window start/end should be selected and used to
175    /// schedule a renewal in the future.
176    ///
177    /// The `Duration` is a hint from the ACME server when to again fetch the renewal window.
178    /// See <https://www.rfc-editor.org/rfc/rfc9773.html#section-4.3.2> for details.
179    ///
180    /// This is only supported by some ACME servers. If the server does not support this feature,
181    /// this method will return `Error::Unsupported`.
182    ///
183    /// See <https://www.rfc-editor.org/rfc/rfc9773.html#section-4.2-4> for more information.
184    #[cfg(feature = "time")]
185    pub async fn renewal_info(
186        &self,
187        certificate_id: &CertificateIdentifier<'_>,
188    ) -> Result<(RenewalInfo, Duration), Error> {
189        let Some(renewal_info_url) = self.inner.client.directory.renewal_info.as_deref() else {
190            return Err(Error::Unsupported("ACME renewal information (ARI)"));
191        };
192
193        // Note: unlike other ACME endpoints, the renewal info endpoint does not require a nonce
194        // or any JWS authentication. It's just a Plain-Old-HTTP-GET.
195        let request = Request::builder()
196            .method(Method::GET)
197            .uri(format!("{renewal_info_url}/{certificate_id}"))
198            .header(USER_AGENT, CRATE_USER_AGENT)
199            .body(BodyWrapper::default())?;
200
201        let rsp = self.inner.client.http.request(request).await?;
202        let Some(retry_after) = retry_after(&rsp) else {
203            return Err(Error::Str("missing Retry-After header"));
204        };
205
206        let delay = retry_after
207            .duration_since(SystemTime::now())
208            .unwrap_or(Duration::ZERO);
209
210        Ok((Problem::check::<RenewalInfo>(rsp).await?, delay))
211    }
212
213    /// Update the account's authentication key
214    ///
215    /// This is useful if you want to change the ACME account key of an existing account, e.g.
216    /// to mitigate the risk of a key compromise. This method creates a new client key and changes
217    /// the key associated with the existing account. `self` will be updated with the new key,
218    /// and a fresh set of [`AccountCredentials`] will be returned to update stored credentials.
219    ///
220    /// See <https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.5> for more information.
221    pub async fn update_key(&mut self) -> Result<AccountCredentials, Error> {
222        let Some(new_key_url) = self.inner.client.directory.key_change.as_deref() else {
223            return Err("Account key rollover not supported by ACME CA".into());
224        };
225
226        #[derive(Debug, Serialize)]
227        struct NewKey<'a> {
228            account: &'a str,
229            #[serde(rename = "oldKey")]
230            old_key: Jwk,
231        }
232
233        let (new_key, new_key_pkcs8) = Key::generate_pkcs8()?;
234        let mut header = new_key.header(Some("nonce"), new_key_url);
235        header.nonce = None;
236        let payload = NewKey {
237            account: &self.inner.id,
238            old_key: Jwk::new(&self.inner.key.inner),
239        };
240
241        let body = JoseJson::new(Some(&payload), header, &new_key)?;
242        let rsp = self.inner.post(Some(&body), None, new_key_url).await?;
243        let _ = Problem::from_response(rsp).await?;
244
245        self.inner = Arc::new(AccountInner {
246            client: self.inner.client.clone(),
247            key: new_key,
248            id: self.inner.id.clone(),
249        });
250
251        let (directory, urls) = match &self.inner.client.directory_url {
252            Some(directory_url) => (Some(directory_url.clone()), None),
253            None => (None, Some(self.inner.client.directory.clone())),
254        };
255
256        Ok(AccountCredentials {
257            id: self.inner.id.clone(),
258            key_pkcs8: new_key_pkcs8,
259            directory,
260            urls,
261        })
262    }
263
264    /// Updates the account contacts
265    ///
266    /// This is useful if you want to update the contact information of an existing account
267    /// on the ACME server. The contacts argument replaces existing contacts on
268    /// the server. By providing an empty array the contacts are removed from the server.
269    ///
270    /// See <https://datatracker.ietf.org/doc/html/rfc8555#section-7.3.2> for more information.
271    pub async fn update_contacts<'a>(&self, contacts: &'a [&'a str]) -> Result<(), Error> {
272        #[derive(Debug, Serialize)]
273        struct Contacts<'a> {
274            contact: &'a [&'a str],
275        }
276
277        let payload = Contacts { contact: contacts };
278        let rsp = self
279            .inner
280            .post(Some(&payload), None, &self.inner.id)
281            .await?;
282
283        #[derive(Debug, Deserialize)]
284        struct Account {
285            status: AuthorizationStatus,
286        }
287
288        let response = Problem::check::<Account>(rsp).await?;
289        match response.status {
290            AuthorizationStatus::Valid => Ok(()),
291            _ => Err("Unexpected account status after updating contact information".into()),
292        }
293    }
294
295    /// Deactivate the account with the ACME server
296    ///
297    /// This is useful when you want to cancel an account with the ACME server
298    /// because you don't intend to use it further, or because the account key was
299    /// compromised.
300    ///
301    /// After this point no further operations can be performed with the account.
302    /// Any existing orders or authorizations created with the ACME server will be
303    /// invalidated.
304    pub async fn deactivate(self) -> Result<(), Error> {
305        #[derive(Serialize)]
306        struct DeactivateRequest<'a> {
307            status: &'a str,
308        }
309
310        let _ = self
311            .inner
312            .post(
313                Some(&DeactivateRequest {
314                    status: "deactivated",
315                }),
316                None,
317                self.id(),
318            )
319            .await?;
320
321        Ok(())
322    }
323
324    /// Yield the profiles supported according to the account's server directory
325    pub fn profiles(&self) -> impl Iterator<Item = ProfileMeta<'_>> {
326        self.inner
327            .client
328            .directory
329            .meta
330            .profiles
331            .iter()
332            .map(|(name, description)| ProfileMeta { name, description })
333    }
334
335    /// Get the account ID
336    pub fn id(&self) -> &str {
337        &self.inner.id
338    }
339
340    /// Get the [RFC 7638](https://www.rfc-editor.org/rfc/rfc7638) account key thumbprint
341    pub fn key_thumbprint(&self) -> &str {
342        &self.inner.key.thumb
343    }
344}
345
346pub(crate) struct AccountInner {
347    client: Arc<Client>,
348    pub(crate) key: Key,
349    id: String,
350}
351
352impl AccountInner {
353    async fn from_credentials(
354        credentials: AccountCredentials,
355        http: Box<dyn HttpClient>,
356    ) -> Result<Self, Error> {
357        Ok(Self {
358            id: credentials.id,
359            key: Key::from_pkcs8_der(credentials.key_pkcs8)?,
360            client: Arc::new(match (credentials.directory, credentials.urls) {
361                (Some(directory_url), _) => Client::new(directory_url, http).await?,
362                (None, Some(directory)) => Client {
363                    http,
364                    directory,
365                    directory_url: None,
366                },
367                (None, None) => return Err("no server URLs found".into()),
368            }),
369        })
370    }
371
372    pub(crate) async fn get<T: DeserializeOwned>(
373        &self,
374        nonce: &mut Option<String>,
375        url: &str,
376    ) -> Result<T, Error> {
377        let rsp = self.post(None::<&Empty>, nonce.take(), url).await?;
378        *nonce = nonce_from_response(&rsp);
379        Problem::check(rsp).await
380    }
381
382    pub(crate) async fn post(
383        &self,
384        payload: Option<&impl Serialize>,
385        nonce: Option<String>,
386        url: &str,
387    ) -> Result<BytesResponse, Error> {
388        self.client.post(payload, nonce, self, url).await
389    }
390}
391
392impl Signer for AccountInner {
393    type Signature = <Key as Signer>::Signature;
394
395    fn header<'n, 'u: 'n, 's: 'u>(&'s self, nonce: Option<&'n str>, url: &'u str) -> Header<'n> {
396        debug_assert!(nonce.is_some());
397        Header {
398            alg: self.key.signing_algorithm,
399            key: KeyOrKeyId::KeyId(&self.id),
400            nonce,
401            url,
402        }
403    }
404
405    fn sign(&self, payload: &[u8]) -> Result<Self::Signature, Error> {
406        self.key.sign(payload)
407    }
408}
409
410/// Builder for `Account` values
411///
412/// Create one via [`Account::builder()`] or [`Account::builder_with_http()`].
413pub struct AccountBuilder {
414    http: Box<dyn HttpClient>,
415}
416
417impl AccountBuilder {
418    /// Restore an existing account from the given credentials
419    ///
420    /// The [`AccountCredentials`] type is opaque, but supports deserialization.
421    #[allow(clippy::wrong_self_convention)]
422    pub async fn from_credentials(self, credentials: AccountCredentials) -> Result<Account, Error> {
423        Ok(Account {
424            inner: Arc::new(AccountInner::from_credentials(credentials, self.http).await?),
425        })
426    }
427
428    /// Create a new account on the `directory_url` with the information in [`NewAccount`]
429    ///
430    /// The returned [`AccountCredentials`] can be serialized and stored for later use.
431    /// Use [`AccountBuilder::from_credentials()`] to restore the account from the credentials.
432    pub async fn create(
433        self,
434        account: &NewAccount<'_>,
435        directory_url: String,
436        external_account: Option<&ExternalAccountKey>,
437    ) -> Result<(Account, AccountCredentials), Error> {
438        let (key, key_pkcs8) = Key::generate_pkcs8()?;
439        Self::create_inner(
440            account,
441            (key, key_pkcs8),
442            external_account,
443            Client::new(directory_url, self.http).await?,
444        )
445        .await
446    }
447
448    /// Load an existing account for the given private key
449    ///
450    /// The returned [`AccountCredentials`] can be serialized and stored for later use.
451    /// Use [`AccountBuilder::from_credentials()`] to restore the account from the credentials.
452    ///
453    /// Yields an error if no account matching the given key exists on the server.
454    pub async fn from_key(
455        self,
456        key: (Key, PrivateKeyDer<'static>),
457        directory_url: String,
458    ) -> Result<(Account, AccountCredentials), Error> {
459        self.from_key_inner(key, directory_url, true).await
460    }
461
462    /// Create a new account with the given private key
463    ///
464    /// The returned [`AccountCredentials`] can be serialized and stored for later use.
465    /// Use [`AccountBuilder::from_credentials()`] to restore the account from the credentials.
466    ///
467    /// Unlike [`AccountBuilder::from_key()`] which only loads existing accounts, this method
468    /// will create a new account if one doesn't already exist for the given key.
469    pub async fn create_from_key(
470        self,
471        key: (Key, PrivateKeyDer<'static>),
472        directory_url: String,
473    ) -> Result<(Account, AccountCredentials), Error> {
474        self.from_key_inner(key, directory_url, false).await
475    }
476
477    /// Shared implementation for `from_key` and `create_from_key`
478    #[allow(clippy::wrong_self_convention)]
479    async fn from_key_inner(
480        self,
481        key: (Key, PrivateKeyDer<'static>),
482        directory_url: String,
483        only_return_existing: bool,
484    ) -> Result<(Account, AccountCredentials), Error> {
485        Self::create_inner(
486            &NewAccount {
487                contact: &[],
488                terms_of_service_agreed: true,
489                only_return_existing,
490            },
491            match key {
492                (key, PrivateKeyDer::Pkcs8(pkcs8)) => (key, pkcs8),
493                _ => return Err("unsupported key format, expected PKCS#8".into()),
494            },
495            None,
496            Client::new(directory_url, self.http).await?,
497        )
498        .await
499    }
500
501    /// Restore an existing account from the given ID, private key, server URL and HTTP client
502    ///
503    /// The key must be provided in DER-encoded PKCS#8. This is usually how ECDSA keys are
504    /// encoded in PEM files. Use a crate like rustls-pemfile to decode from PEM to DER.
505    #[allow(clippy::wrong_self_convention)]
506    pub async fn from_parts(
507        self,
508        id: String,
509        key_pkcs8_der: PrivatePkcs8KeyDer<'_>,
510        directory_url: String,
511    ) -> Result<Account, Error> {
512        Ok(Account {
513            inner: Arc::new(AccountInner {
514                id,
515                key: Key::from_pkcs8_der(key_pkcs8_der)?,
516                client: Arc::new(Client::new(directory_url, self.http).await?),
517            }),
518        })
519    }
520
521    async fn create_inner(
522        account: &NewAccount<'_>,
523        (key, key_pkcs8): (Key, PrivatePkcs8KeyDer<'static>),
524        external_account: Option<&ExternalAccountKey>,
525        client: Client,
526    ) -> Result<(Account, AccountCredentials), Error> {
527        let payload = NewAccountPayload {
528            new_account: account,
529            external_account_binding: external_account
530                .map(|eak| {
531                    JoseJson::new(
532                        Some(&Jwk::new(&key.inner)),
533                        eak.header(None, &client.directory.new_account),
534                        eak,
535                    )
536                })
537                .transpose()?,
538        };
539
540        let rsp = client
541            .post(Some(&payload), None, &key, &client.directory.new_account)
542            .await?;
543
544        let account_url = rsp
545            .parts
546            .headers
547            .get(LOCATION)
548            .and_then(|hv| hv.to_str().ok())
549            .map(|s| s.to_owned());
550
551        // The response redirects, we don't need the body
552        let _ = Problem::from_response(rsp).await?;
553        let id = account_url.ok_or("failed to get account URL")?;
554        let credentials = AccountCredentials {
555            id: id.clone(),
556            key_pkcs8,
557            directory: Some(client.directory_url.clone().unwrap()), // New clients always have `directory_url`
558            // We support deserializing URLs for compatibility with versions pre 0.4,
559            // but we prefer to get fresh URLs from the `directory_url` for newer credentials.
560            urls: None,
561        };
562
563        let account = AccountInner {
564            client: Arc::new(client),
565            key,
566            id: id.clone(),
567        };
568
569        Ok((
570            Account {
571                inner: Arc::new(account),
572            },
573            credentials,
574        ))
575    }
576}
577
578/// Private account key used to sign requests
579pub struct Key {
580    rng: crypto::SystemRandom,
581    signing_algorithm: SigningAlgorithm,
582    inner: crypto::EcdsaKeyPair,
583    pub(crate) thumb: String,
584}
585
586impl Key {
587    /// Generate a new ECDSA P-256 key pair
588    #[deprecated(since = "0.8.3", note = "use `generate_pkcs8()` instead")]
589    pub fn generate() -> Result<(Self, PrivateKeyDer<'static>), Error> {
590        let (key, pkcs8) = Self::generate_pkcs8()?;
591        Ok((key, PrivateKeyDer::Pkcs8(pkcs8)))
592    }
593
594    /// Generate a new ECDSA P-256 key pair
595    pub fn generate_pkcs8() -> Result<(Self, PrivatePkcs8KeyDer<'static>), Error> {
596        let rng = crypto::SystemRandom::new();
597        let pkcs8 =
598            crypto::EcdsaKeyPair::generate_pkcs8(&crypto::ECDSA_P256_SHA256_FIXED_SIGNING, &rng)
599                .map_err(|_| Error::Crypto)?;
600        Ok((
601            Self::new(pkcs8.as_ref(), rng)?,
602            PrivatePkcs8KeyDer::from(pkcs8.as_ref().to_vec()),
603        ))
604    }
605
606    /// Create a new key from the given PKCS#8 DER-encoded private key
607    ///
608    /// Currently, only ECDSA P-256 keys are supported.
609    pub fn from_pkcs8_der(pkcs8_der: PrivatePkcs8KeyDer<'_>) -> Result<Self, Error> {
610        Self::new(pkcs8_der.secret_pkcs8_der(), crypto::SystemRandom::new())
611    }
612
613    fn new(pkcs8_der: &[u8], rng: crypto::SystemRandom) -> Result<Self, Error> {
614        let inner = crypto::p256_key_pair_from_pkcs8(pkcs8_der, &rng)?;
615        let thumb = BASE64_URL_SAFE_NO_PAD.encode(Jwk::thumb_sha256(&inner)?);
616        Ok(Self {
617            rng,
618            signing_algorithm: SigningAlgorithm::Es256,
619            inner,
620            thumb,
621        })
622    }
623}
624
625impl Signer for Key {
626    type Signature = crypto::Signature;
627
628    fn header<'n, 'u: 'n, 's: 'u>(&'s self, nonce: Option<&'n str>, url: &'u str) -> Header<'n> {
629        debug_assert!(nonce.is_some());
630        Header {
631            alg: self.signing_algorithm,
632            key: KeyOrKeyId::from_key(&self.inner),
633            nonce,
634            url,
635        }
636    }
637
638    fn sign(&self, payload: &[u8]) -> Result<Self::Signature, Error> {
639        self.inner
640            .sign(&self.rng, payload)
641            .map_err(|_| Error::Crypto)
642    }
643}
644
645/// A HMAC key used to link account creation requests to an external account
646///
647/// See RFC 8555 section 7.3.4 for more information.
648pub struct ExternalAccountKey {
649    id: String,
650    key: crypto::hmac::Key,
651}
652
653impl ExternalAccountKey {
654    /// Create a new external account key
655    ///
656    /// Note that the `key_value` argument represents the raw key value, so if the caller holds
657    /// an encoded key value (for example, using base64), decode it before passing it in.
658    pub fn new(id: String, key_value: &[u8]) -> Self {
659        Self {
660            id,
661            key: crypto::hmac::Key::new(crypto::hmac::HMAC_SHA256, key_value),
662        }
663    }
664}
665
666impl Signer for ExternalAccountKey {
667    type Signature = crypto::hmac::Tag;
668
669    fn header<'n, 'u: 'n, 's: 'u>(&'s self, nonce: Option<&'n str>, url: &'u str) -> Header<'n> {
670        debug_assert_eq!(nonce, None);
671        Header {
672            alg: SigningAlgorithm::Hs256,
673            key: KeyOrKeyId::KeyId(&self.id),
674            nonce,
675            url,
676        }
677    }
678
679    fn sign(&self, payload: &[u8]) -> Result<Self::Signature, Error> {
680        Ok(crypto::hmac::sign(&self.key, payload))
681    }
682}