identity_storage/storage/
jwk_document_ext.rs

1// Copyright 2020-2023 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use std::borrow::Cow;
5
6use super::JwkStorageDocumentError as Error;
7use super::JwsSignatureOptions;
8use super::Storage;
9
10use crate::key_id_storage::KeyIdStorage;
11use crate::key_id_storage::KeyIdStorageResult;
12use crate::key_id_storage::MethodDigest;
13use crate::key_storage::JwkGenOutput;
14use crate::key_storage::JwkStorage;
15use crate::key_storage::KeyId;
16use crate::key_storage::KeyStorageResult;
17use crate::key_storage::KeyType;
18
19use async_trait::async_trait;
20use identity_core::common::Object;
21use identity_credential::credential::Credential;
22use identity_credential::credential::CredentialV2;
23use identity_credential::credential::Jws;
24use identity_credential::credential::Jwt;
25use identity_credential::credential::JwtVcV2;
26use identity_credential::presentation::JwtPresentationOptions;
27use identity_credential::presentation::Presentation;
28use identity_did::DIDUrl;
29use identity_document::document::CoreDocument;
30use identity_verification::jose::jws::CompactJwsEncoder;
31use identity_verification::jose::jws::CompactJwsEncodingOptions;
32use identity_verification::jose::jws::JwsAlgorithm;
33use identity_verification::jose::jws::JwsHeader;
34use identity_verification::jws::CharSet;
35use identity_verification::MethodData;
36use identity_verification::MethodScope;
37use identity_verification::VerificationMethod;
38use serde::de::DeserializeOwned;
39use serde::Serialize;
40
41/// Alias for a `Result` with the error type [`Error`].
42pub type StorageResult<T> = Result<T, Error>;
43
44/// Extension trait for JWK-based operations on DID documents.
45///
46/// This trait is deliberately sealed and cannot be implemented by external crates.
47/// The trait only exists as an extension of existing DID documents implemented in
48/// dependent crates. Because those crates cannot also depend on this crate,
49/// the extension trait is necessary. External crates however should simply wrap the methods
50/// on the trait if they wish to reexport them on their DID document type.
51/// This also allows them to use their own error type on those methods.
52#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
53#[cfg_attr(feature = "send-sync-storage", async_trait)]
54pub trait JwkDocumentExt: private::Sealed {
55  /// Generate new key material in the given `storage` and insert a new verification method with the corresponding
56  /// public key material into the DID document.
57  ///
58  /// - If no fragment is given the `kid` of the generated JWK is used, if it is set, otherwise an error is returned.
59  /// - The `key_type` must be compatible with the given `storage`. [`Storage`]s are expected to export key type
60  ///   constants for that use case.
61  ///
62  /// The fragment of the generated method is returned.
63  async fn generate_method<K, I>(
64    &mut self,
65    storage: &Storage<K, I>,
66    key_type: KeyType,
67    alg: JwsAlgorithm,
68    fragment: Option<&str>,
69    scope: MethodScope,
70  ) -> StorageResult<String>
71  where
72    K: JwkStorage,
73    I: KeyIdStorage;
74
75  /// Remove the method identified by the given `id` from the document and delete the corresponding key material in
76  /// the given `storage`.
77  ///
78  /// ## Warning
79  ///
80  /// This will delete the key material permanently and irrecoverably.
81  async fn purge_method<K, I>(&mut self, storage: &Storage<K, I>, id: &DIDUrl) -> StorageResult<()>
82  where
83    K: JwkStorage,
84    I: KeyIdStorage;
85
86  /// Sign the arbitrary `payload` according to `options` with the storage backed private key corresponding to the
87  /// public key material in the verification method identified by the given `fragment.
88  ///
89  /// Upon success a string representing a JWS encoded according to the Compact JWS Serialization format is returned.
90  /// See [RFC7515 section 3.1](https://www.rfc-editor.org/rfc/rfc7515#section-3.1).
91  async fn create_jws<K, I>(
92    &self,
93    storage: &Storage<K, I>,
94    fragment: &str,
95    payload: &[u8],
96    options: &JwsSignatureOptions,
97  ) -> StorageResult<Jws>
98  where
99    K: JwkStorage,
100    I: KeyIdStorage;
101
102  /// Produces a JWT where the payload is produced from the given `credential`
103  /// in accordance with either [VC Data Model v1.1](https://www.w3.org/TR/vc-data-model/#json-web-token)
104  /// or [VC Data Model v2.0](https://www.w3.org/TR/vc-data-model-2.0/).
105  ///
106  /// Unless the `kid` is explicitly set in the options, the `kid` in the protected header is the `id`
107  /// of the method identified by `fragment` and the JWS signature will be produced by the corresponding
108  /// private key backed by the `storage` in accordance with the passed `options`.
109  ///
110  /// The `custom_claims` can be used to set additional claims on the resulting JWT.
111  async fn create_credential_jwt<K, I, T>(
112    &self,
113    credential: &Credential<T>,
114    storage: &Storage<K, I>,
115    fragment: &str,
116    options: &JwsSignatureOptions,
117    custom_claims: Option<Object>,
118  ) -> StorageResult<Jwt>
119  where
120    K: JwkStorage,
121    I: KeyIdStorage,
122    T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync;
123
124  /// Returns a JWT containing the given VC Data Model 2.0 `credential` in accordance with the mediatype
125  /// `application/vc+jwt` defined in [Securing Verifiable Credentials using JOSE and COSE](https://www.w3.org/TR/vc-jose-cose/#securing-with-jose).
126  ///
127  /// Unless the `kid` is explicitly set in the options, the `kid` in the protected header is the `id`
128  /// of the method identified by `fragment` and the JWS signature will be produced by the corresponding
129  /// private key backed by the `storage` in accordance with the passed `options`.
130  async fn create_credential_v2_jwt<K, I, T>(
131    &self,
132    credential: &CredentialV2<T>,
133    storage: &Storage<K, I>,
134    fragment: &str,
135    options: &JwsSignatureOptions,
136  ) -> StorageResult<JwtVcV2>
137  where
138    K: JwkStorage,
139    I: KeyIdStorage,
140    T: Clone + Serialize + Sync;
141
142  /// Produces a JWT where the payload is produced from the given `presentation`
143  /// in accordance with [VC Data Model v1.1](https://www.w3.org/TR/vc-data-model/#json-web-token)
144  /// or with [VC Data Model v2.0](https://www.w3.org/TR/vc-jose-cose/#securing-vps-with-jose) depending
145  /// on the presentation's context.
146  ///
147  /// Unless the `kid` is explicitly set in the options, the `kid` in the protected header is the `id`
148  /// of the method identified by `fragment` and the JWS signature will be produced by the corresponding
149  /// private key backed by the `storage` in accordance with the passed `options`.
150  async fn create_presentation_jwt<K, I, CRED, T>(
151    &self,
152    presentation: &Presentation<CRED, T>,
153    storage: &Storage<K, I>,
154    fragment: &str,
155    signature_options: &JwsSignatureOptions,
156    presentation_options: &JwtPresentationOptions,
157  ) -> StorageResult<Jwt>
158  where
159    K: JwkStorage,
160    I: KeyIdStorage,
161    T: Clone + Serialize + DeserializeOwned + Sync,
162    CRED: ToOwned<Owned = CRED> + Serialize + DeserializeOwned + Clone + Sync;
163}
164
165mod private {
166  pub trait Sealed {}
167  impl Sealed for identity_document::document::CoreDocument {}
168  #[cfg(feature = "iota-document")]
169  impl Sealed for identity_iota_core::IotaDocument {}
170}
171
172// ====================================================================================================================
173// Implementation
174// ====================================================================================================================
175
176// We want to implement this trait both for CoreDocument and IotaDocument, but the methods that take `&mut self` cannot
177// be implemented in terms of &mut CoreDocument for IotaDocument. To work around this limitation we use macros to avoid
178// copious amounts of repetition.
179// NOTE: If such use of macros becomes very common it is probably better to use the duplicate crate: https://docs.rs/duplicate/latest/duplicate/
180macro_rules! generate_method_for_document_type {
181  ($t:ty, $a:ty, $k:path, $f:path, $name:ident) => {
182    async fn $name<K, I>(
183      document: &mut $t,
184      storage: &Storage<K, I>,
185      key_type: KeyType,
186      alg: $a,
187      fragment: Option<&str>,
188      scope: MethodScope,
189    ) -> StorageResult<String>
190    where
191      K: $k,
192      I: KeyIdStorage,
193    {
194      let JwkGenOutput { key_id, jwk } = $f(storage.key_storage(), key_type, alg)
195        .await
196        .map_err(Error::KeyStorageError)?;
197
198      // Produce a new verification method containing the generated JWK. If this operation fails we handle the error
199      // by attempting to revert key generation before returning an error.
200      let method: VerificationMethod = {
201        match VerificationMethod::new_from_jwk(document.id().clone(), jwk, fragment)
202          .map_err(Error::VerificationMethodConstructionError)
203        {
204          Ok(method) => method,
205          Err(source) => {
206            return Err(try_undo_key_generation(storage, &key_id, source).await);
207          }
208        }
209      };
210
211      // Extract data from method before inserting it into the DID document.
212      let method_digest: MethodDigest = MethodDigest::new(&method).map_err(Error::MethodDigestConstructionError)?;
213      let method_id: DIDUrl = method.id().clone();
214
215      // The fragment is always set on a method, so this error will never occur.
216      let fragment: String = method_id
217        .fragment()
218        .ok_or(identity_verification::Error::MissingIdFragment)
219        .map_err(Error::VerificationMethodConstructionError)?
220        .to_owned();
221
222      // Insert method into document and handle error upon failure.
223      if let Err(error) = document
224        .insert_method(method, scope)
225        .map_err(|_| Error::FragmentAlreadyExists)
226      {
227        return Err(try_undo_key_generation(storage, &key_id, error).await);
228      };
229
230      // Insert the generated `KeyId` into storage under the computed method digest and handle the error if the
231      // operation fails.
232      if let Err(error) = <I as KeyIdStorage>::insert_key_id(&storage.key_id_storage(), method_digest, key_id.clone())
233        .await
234        .map_err(Error::KeyIdStorageError)
235      {
236        // Remove the method from the document as it can no longer be used.
237        let _ = document.remove_method(&method_id);
238        return Err(try_undo_key_generation(storage, &key_id, error).await);
239      }
240
241      Ok(fragment)
242    }
243  };
244}
245
246macro_rules! purge_method_for_document_type {
247  ($t:ty, $name:ident) => {
248    async fn $name<K, I>(document: &mut $t, storage: &Storage<K, I>, id: &DIDUrl) -> StorageResult<()>
249    where
250      K: JwkStorage,
251      I: KeyIdStorage,
252    {
253      let (method, scope) = document.remove_method_and_scope(id).ok_or(Error::MethodNotFound)?;
254
255      // Obtain method digest and handle error if this operation fails.
256      let method_digest: MethodDigest = match MethodDigest::new(&method).map_err(Error::MethodDigestConstructionError) {
257        Ok(digest) => digest,
258        Err(error) => {
259          // Revert state by reinserting the method before returning the error.
260          let _ = document.insert_method(method, scope);
261          return Err(error);
262        }
263      };
264
265      // Obtain key id and handle error upon failure.
266      let key_id: KeyId = match <I as KeyIdStorage>::get_key_id(&storage.key_id_storage(), &method_digest)
267        .await
268        .map_err(Error::KeyIdStorageError)
269      {
270        Ok(key_id) => key_id,
271        Err(error) => {
272          // Reinsert method before returning.
273          let _ = document.insert_method(method, scope);
274          return Err(error);
275        }
276      };
277
278      // Delete key and key id concurrently.
279      let key_deletion_fut = <K as JwkStorage>::delete(&storage.key_storage(), &key_id);
280      let key_id_deletion_fut = <I as KeyIdStorage>::delete_key_id(&storage.key_id_storage(), &method_digest);
281      let (key_deletion_result, key_id_deletion_result): (KeyStorageResult<()>, KeyIdStorageResult<()>) =
282        futures::join!(key_deletion_fut, key_id_deletion_fut);
283
284      // Check for any errors that may have occurred. Unfortunately this is somewhat involved.
285      match (key_deletion_result, key_id_deletion_result) {
286        (Ok(_), Ok(_)) => Ok(()),
287        (Ok(_), Err(key_id_deletion_error)) => {
288          // Cannot attempt to revert this operation as the JwkStorage may not return the same KeyId when
289          // JwkStorage::insert is called.
290          Err(Error::UndoOperationFailed {
291            message: format!(
292              "cannot undo key deletion: this results in a stray key id stored under packed method digest: {:?}",
293              &method_digest.pack()
294            ),
295            source: Box::new(Error::KeyIdStorageError(key_id_deletion_error)),
296            undo_error: None,
297          })
298        }
299        (Err(key_deletion_error), Ok(_)) => {
300          // Attempt to revert: Reinsert key id and method if possible.
301          if let Err(key_id_insertion_error) =
302            <I as KeyIdStorage>::insert_key_id(&storage.key_id_storage(), (&method_digest).clone(), key_id.clone())
303              .await
304              .map_err(Error::KeyIdStorageError)
305          {
306            Err(Error::UndoOperationFailed {
307              message: format!("cannot revert key id deletion: this results in stray key with key id: {key_id}"),
308              source: Box::new(Error::KeyStorageError(key_deletion_error)),
309              undo_error: Some(Box::new(key_id_insertion_error)),
310            })
311          } else {
312            // KeyId reinsertion succeeded. Now reinsert method.
313            let _ = document.insert_method(method, scope);
314            Err(Error::KeyStorageError(key_deletion_error))
315          }
316        }
317        (Err(_key_deletion_error), Err(key_id_deletion_error)) => {
318          // We assume this means nothing got deleted. Reinsert the method and return one of the errors (perhaps
319          // key_id_deletion_error as we really expect the key id storage to work as expected at this point).
320          let _ = document.insert_method(method, scope);
321          Err(Error::KeyIdStorageError(key_id_deletion_error))
322        }
323      }
324    }
325  };
326}
327
328// ====================================================================================================================
329// CoreDocument
330// ====================================================================================================================
331
332generate_method_for_document_type!(
333  CoreDocument,
334  JwsAlgorithm,
335  JwkStorage,
336  JwkStorage::generate,
337  generate_method_core_document
338);
339purge_method_for_document_type!(CoreDocument, purge_method_core_document);
340
341#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
342#[cfg_attr(feature = "send-sync-storage", async_trait)]
343impl JwkDocumentExt for CoreDocument {
344  async fn generate_method<K, I>(
345    &mut self,
346    storage: &Storage<K, I>,
347    key_type: KeyType,
348    alg: JwsAlgorithm,
349    fragment: Option<&str>,
350    scope: MethodScope,
351  ) -> StorageResult<String>
352  where
353    K: JwkStorage,
354    I: KeyIdStorage,
355  {
356    generate_method_core_document(self, storage, key_type, alg, fragment, scope).await
357  }
358
359  async fn purge_method<K, I>(&mut self, storage: &Storage<K, I>, id: &DIDUrl) -> StorageResult<()>
360  where
361    K: JwkStorage,
362    I: KeyIdStorage,
363  {
364    purge_method_core_document(self, storage, id).await
365  }
366
367  async fn create_jws<K, I>(
368    &self,
369    storage: &Storage<K, I>,
370    fragment: &str,
371    payload: &[u8],
372    options: &JwsSignatureOptions,
373  ) -> StorageResult<Jws>
374  where
375    K: JwkStorage,
376    I: KeyIdStorage,
377  {
378    // Obtain the method corresponding to the given fragment.
379    let method: &VerificationMethod = self.resolve_method(fragment, None).ok_or(Error::MethodNotFound)?;
380    let MethodData::PublicKeyJwk(ref jwk) = method.data() else {
381      return Err(Error::NotPublicKeyJwk);
382    };
383
384    // Extract JwsAlgorithm.
385    let alg: JwsAlgorithm = jwk
386      .alg()
387      .unwrap_or("")
388      .parse()
389      .map_err(|_| Error::InvalidJwsAlgorithm)?;
390
391    // Create JWS header in accordance with options.
392    let header: JwsHeader = {
393      let mut header = JwsHeader::new();
394
395      header.set_alg(alg);
396      if let Some(custom) = &options.custom_header_parameters {
397        header.set_custom(custom.clone())
398      }
399
400      if let Some(ref kid) = options.kid {
401        header.set_kid(kid.clone());
402      } else {
403        header.set_kid(method.id().to_string());
404      }
405
406      if options.attach_jwk {
407        header.set_jwk(jwk.clone())
408      };
409
410      if let Some(b64) = options.b64 {
411        // Follow recommendation in https://datatracker.ietf.org/doc/html/rfc7797#section-7.
412        if !b64 {
413          header.set_b64(b64);
414          header.set_crit(["b64"]);
415        }
416      };
417
418      if let Some(typ) = &options.typ {
419        header.set_typ(typ.clone())
420      } else {
421        // https://www.w3.org/TR/vc-data-model/#jwt-encoding
422        header.set_typ("JWT")
423      }
424
425      if let Some(cty) = &options.cty {
426        header.set_cty(cty.clone())
427      };
428
429      if let Some(url) = &options.url {
430        header.set_url(url.clone())
431      };
432
433      if let Some(nonce) = &options.nonce {
434        header.set_nonce(nonce.clone())
435      };
436
437      header
438    };
439
440    // Get the key identifier corresponding to the given method from the KeyId storage.
441    let method_digest: MethodDigest = MethodDigest::new(method).map_err(Error::MethodDigestConstructionError)?;
442    let key_id = <I as KeyIdStorage>::get_key_id(storage.key_id_storage(), &method_digest)
443      .await
444      .map_err(Error::KeyIdStorageError)?;
445
446    // Extract Compact JWS encoding options.
447    let encoding_options: CompactJwsEncodingOptions = if !options.detached_payload {
448      // We use this as a default and don't provide the extra UrlSafe check for now.
449      // Applications that require such checks can easily do so after JWS creation.
450      CompactJwsEncodingOptions::NonDetached {
451        charset_requirements: CharSet::Default,
452      }
453    } else {
454      CompactJwsEncodingOptions::Detached
455    };
456
457    let jws_encoder: CompactJwsEncoder<'_> = CompactJwsEncoder::new_with_options(payload, &header, encoding_options)
458      .map_err(|err| Error::EncodingError(err.into()))?;
459    let signature = <K as JwkStorage>::sign(storage.key_storage(), &key_id, jws_encoder.signing_input(), jwk)
460      .await
461      .map_err(Error::KeyStorageError)?;
462    Ok(Jws::new(jws_encoder.into_jws(&signature)))
463  }
464
465  async fn create_credential_jwt<K, I, T>(
466    &self,
467    credential: &Credential<T>,
468    storage: &Storage<K, I>,
469    fragment: &str,
470    options: &JwsSignatureOptions,
471    custom_claims: Option<Object>,
472  ) -> StorageResult<Jwt>
473  where
474    K: JwkStorage,
475    I: KeyIdStorage,
476    T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync,
477  {
478    if options.detached_payload {
479      return Err(Error::EncodingError(Box::<dyn std::error::Error + Send + Sync>::from(
480        "cannot use detached payload for credential signing",
481      )));
482    }
483
484    if !options.b64.unwrap_or(true) {
485      // JWTs should not have `b64` set per https://datatracker.ietf.org/doc/html/rfc7797#section-7.
486      return Err(Error::EncodingError(Box::<dyn std::error::Error + Send + Sync>::from(
487        "cannot use `b64 = false` with JWTs",
488      )));
489    }
490
491    let payload = credential
492      .serialize_jwt(custom_claims)
493      .map_err(Error::ClaimsSerializationError)?;
494    self
495      .create_jws(storage, fragment, payload.as_bytes(), options)
496      .await
497      .map(|jws| Jwt::new(jws.into()))
498  }
499
500  async fn create_credential_v2_jwt<K, I, T>(
501    &self,
502    credential: &CredentialV2<T>,
503    storage: &Storage<K, I>,
504    fragment: &str,
505    options: &JwsSignatureOptions,
506  ) -> StorageResult<JwtVcV2>
507  where
508    K: JwkStorage,
509    I: KeyIdStorage,
510    T: Clone + Serialize + Sync,
511  {
512    if options.detached_payload {
513      return Err(Error::EncodingError(
514        "cannot use detached payload for credential signing".into(),
515      ));
516    }
517
518    if !options.b64.unwrap_or(true) {
519      // JWTs should not have `b64` set per https://datatracker.ietf.org/doc/html/rfc7797#section-7.
520      return Err(Error::EncodingError("cannot use `b64 = false` with JWTs".into()));
521    }
522
523    let payload = credential
524      .serialize_jwt(None)
525      .map_err(Error::ClaimsSerializationError)?;
526
527    // Ensure the correct `typ` header for VC Data Model 2.0 JWTs.
528    let mut options = Cow::Borrowed(options);
529    if options.typ.as_deref() != Some("vc+jwt") {
530      options.to_mut().typ = Some("vc+jwt".to_owned());
531    }
532
533    self
534      .create_jws(storage, fragment, payload.as_bytes(), &options)
535      .await
536      .map(|jws| JwtVcV2::parse(jws.as_str()).expect("valid JWT string containing a VC v2.0"))
537  }
538
539  async fn create_presentation_jwt<K, I, CRED, T>(
540    &self,
541    presentation: &Presentation<CRED, T>,
542    storage: &Storage<K, I>,
543    fragment: &str,
544    jws_options: &JwsSignatureOptions,
545    jwt_options: &JwtPresentationOptions,
546  ) -> StorageResult<Jwt>
547  where
548    K: JwkStorage,
549    I: KeyIdStorage,
550    T: Clone + Serialize + DeserializeOwned + Sync,
551    CRED: ToOwned<Owned = CRED> + Serialize + DeserializeOwned + Clone + Sync,
552  {
553    if jws_options.detached_payload {
554      return Err(Error::EncodingError(
555        "cannot use detached payload for presentation signing".into(),
556      ));
557    }
558
559    if !jws_options.b64.unwrap_or(true) {
560      // JWTs should not have `b64` set per https://datatracker.ietf.org/doc/html/rfc7797#section-7.
561      return Err(Error::EncodingError("cannot use `b64 = false` with JWTs".into()));
562    }
563    let payload = presentation
564      .serialize_jwt(jwt_options)
565      .map_err(Error::ClaimsSerializationError)?;
566
567    let mut jws_options = Cow::Borrowed(jws_options);
568    // Set JWS headers in accordance with the JOSE specification for VPs that use VC Data Model 2.0.
569    // See https://www.w3.org/TR/vc-jose-cose/#securing-vps-with-jose.
570    if presentation.is_v2() {
571      jws_options.to_mut().typ = Some("vp+jwt".to_owned());
572    }
573
574    self
575      .create_jws(storage, fragment, payload.as_bytes(), &jws_options)
576      .await
577      .map(|jws| Jwt::new(jws.into()))
578  }
579}
580
581/// Attempt to revert key generation. If this succeeds the original `source_error` is returned,
582/// otherwise [`JwkStorageDocumentError::UndoOperationFailed`] is returned with the `source_error` attached as
583/// `source`.
584pub(crate) async fn try_undo_key_generation<K, I>(storage: &Storage<K, I>, key_id: &KeyId, source_error: Error) -> Error
585where
586  K: JwkStorage,
587  I: KeyIdStorage,
588{
589  // Undo key generation
590  if let Err(err) = <K as JwkStorage>::delete(storage.key_storage(), key_id).await {
591    Error::UndoOperationFailed {
592      message: format!("unable to delete stray key with id: {}", &key_id),
593      source: Box::new(source_error),
594      undo_error: Some(Box::new(Error::KeyStorageError(err))),
595    }
596  } else {
597    source_error
598  }
599}
600
601// ====================================================================================================================
602// IotaDocument
603// ====================================================================================================================
604#[cfg(feature = "iota-document")]
605mod iota_document {
606  use super::*;
607  use identity_credential::credential::Credential;
608  use identity_credential::credential::Jwt;
609  use identity_iota_core::IotaDocument;
610
611  generate_method_for_document_type!(
612    IotaDocument,
613    JwsAlgorithm,
614    JwkStorage,
615    JwkStorage::generate,
616    generate_method_iota_document
617  );
618  purge_method_for_document_type!(IotaDocument, purge_method_iota_document);
619
620  #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
621  #[cfg_attr(feature = "send-sync-storage", async_trait)]
622  impl JwkDocumentExt for IotaDocument {
623    async fn generate_method<K, I>(
624      &mut self,
625      storage: &Storage<K, I>,
626      key_type: KeyType,
627      alg: JwsAlgorithm,
628      fragment: Option<&str>,
629      scope: MethodScope,
630    ) -> StorageResult<String>
631    where
632      K: JwkStorage,
633      I: KeyIdStorage,
634    {
635      generate_method_iota_document(self, storage, key_type, alg, fragment, scope).await
636    }
637
638    async fn purge_method<K, I>(&mut self, storage: &Storage<K, I>, id: &DIDUrl) -> StorageResult<()>
639    where
640      K: JwkStorage,
641      I: KeyIdStorage,
642    {
643      purge_method_iota_document(self, storage, id).await
644    }
645
646    async fn create_jws<K, I>(
647      &self,
648      storage: &Storage<K, I>,
649      fragment: &str,
650      payload: &[u8],
651      options: &JwsSignatureOptions,
652    ) -> StorageResult<Jws>
653    where
654      K: JwkStorage,
655      I: KeyIdStorage,
656    {
657      self
658        .core_document()
659        .create_jws(storage, fragment, payload, options)
660        .await
661    }
662
663    async fn create_credential_jwt<K, I, T>(
664      &self,
665      credential: &Credential<T>,
666      storage: &Storage<K, I>,
667      fragment: &str,
668      options: &JwsSignatureOptions,
669      custom_claims: Option<Object>,
670    ) -> StorageResult<Jwt>
671    where
672      K: JwkStorage,
673      I: KeyIdStorage,
674      T: ToOwned<Owned = T> + Serialize + DeserializeOwned + Sync,
675    {
676      self
677        .core_document()
678        .create_credential_jwt(credential, storage, fragment, options, custom_claims)
679        .await
680    }
681    async fn create_presentation_jwt<K, I, CRED, T>(
682      &self,
683      presentation: &Presentation<CRED, T>,
684      storage: &Storage<K, I>,
685      fragment: &str,
686      options: &JwsSignatureOptions,
687      jwt_options: &JwtPresentationOptions,
688    ) -> StorageResult<Jwt>
689    where
690      K: JwkStorage,
691      I: KeyIdStorage,
692      T: Clone + Serialize + DeserializeOwned + Sync,
693      CRED: ToOwned<Owned = CRED> + Serialize + DeserializeOwned + Clone + Sync,
694    {
695      self
696        .core_document()
697        .create_presentation_jwt(presentation, storage, fragment, options, jwt_options)
698        .await
699    }
700
701    async fn create_credential_v2_jwt<K, I, T>(
702      &self,
703      credential: &CredentialV2<T>,
704      storage: &Storage<K, I>,
705      fragment: &str,
706      options: &JwsSignatureOptions,
707    ) -> StorageResult<JwtVcV2>
708    where
709      K: JwkStorage,
710      I: KeyIdStorage,
711      T: Clone + Serialize + Sync,
712    {
713      self
714        .core_document()
715        .create_credential_v2_jwt(credential, storage, fragment, options)
716        .await
717    }
718  }
719}