identity_storage/storage/
storage_signer.rs

1// Copyright 2020-2024 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use anyhow::anyhow;
5use async_trait::async_trait;
6use fastcrypto::hash::Blake2b256;
7use fastcrypto::traits::ToFromBytes;
8
9use identity_did::CoreDID;
10use identity_document::document::CoreDocument;
11use identity_verification::jwk::FromJwk as _;
12use identity_verification::jwk::Jwk;
13use identity_verification::MethodData;
14
15use iota_interaction::types::crypto::PublicKey;
16use iota_interaction::types::crypto::Signature;
17use iota_sdk_types::crypto::Intent;
18
19use iota_interaction::types::transaction::TransactionData;
20use iota_interaction::IotaKeySignature;
21use iota_interaction::OptionalSync;
22use secret_storage::Error as SecretStorageError;
23use secret_storage::Signer;
24
25use crate::JwkStorage;
26use crate::KeyId;
27use crate::KeyIdStorage;
28use crate::KeyIdStorageErrorKind;
29use crate::KeyStorageErrorKind;
30use crate::MethodDigest;
31use crate::MethodDigestConstructionError;
32use crate::Storage;
33
34/// Signer that offers signing capabilities for `Signer` trait from `secret_storage`.
35/// `Storage` is used to sign.
36pub struct StorageSigner<'a, K, I> {
37  key_id: KeyId,
38  public_key: Jwk,
39  storage: &'a Storage<K, I>,
40}
41
42impl<K, I> Clone for StorageSigner<'_, K, I> {
43  fn clone(&self) -> Self {
44    StorageSigner {
45      key_id: self.key_id.clone(),
46      public_key: self.public_key.clone(),
47      storage: self.storage,
48    }
49  }
50}
51
52impl<'a, K, I> StorageSigner<'a, K, I> {
53  /// Creates new `StorageSigner` with reference to a `Storage` instance.
54  pub fn new(storage: &'a Storage<K, I>, key_id: KeyId, public_key: Jwk) -> Self {
55    Self {
56      key_id,
57      public_key,
58      storage,
59    }
60  }
61
62  /// Returns a reference to the [`KeyId`] of the key used by this [`Signer`].
63  pub fn key_id(&self) -> &KeyId {
64    &self.key_id
65  }
66
67  /// Returns this [`Signer`]'s public key as [`Jwk`].
68  pub fn public_key_jwk(&self) -> &Jwk {
69    &self.public_key
70  }
71
72  /// Returns a reference to this [`Signer`]'s [`Storage`].
73  pub fn storage(&self) -> &Storage<K, I> {
74    self.storage
75  }
76}
77
78/// Error type that may be returned by [StorageSigner::new_from_vm_fragment].
79#[derive(Debug, thiserror::Error)]
80#[error("failed to create signer for '{did}#{fragment}'")]
81#[non_exhaustive]
82pub struct StorageSignerFromVmError {
83  /// The [DID](CoreDID) of the given [document](CoreDocument).
84  pub did: CoreDID,
85  /// The verification method fragment.
86  pub fragment: Box<str>,
87  /// Specific type of failure for this error.
88  pub kind: StorageSignerFromVmErrorKind,
89}
90
91/// Types of failure for [StorageSignerFromVmError].
92#[derive(Debug, thiserror::Error)]
93#[non_exhaustive]
94pub enum StorageSignerFromVmErrorKind {
95  /// The given DID Document doesn't contain a VM identified by the given fragment.
96  #[error("verification method not found")]
97  VmNotFound,
98  /// Storage doesn't contain VM's private key.
99  #[error("corresponding private key not found in storage")]
100  KeyNotFound,
101  /// Unknown storage-related error.
102  #[error(transparent)]
103  StorageError(#[from] Box<dyn std::error::Error + Send + Sync>),
104  /// Failed to construct VM's digest.
105  #[error(transparent)]
106  MethodDigestConstruction(#[from] MethodDigestConstructionError),
107  /// The type of the resolved verification method is not supported.
108  #[error("unsupported verification method type '{0}'")]
109  UnsupportedVmType(Box<str>),
110}
111
112impl<'a, K, I> StorageSigner<'a, K, I>
113where
114  K: JwkStorage + OptionalSync,
115  I: KeyIdStorage + OptionalSync,
116{
117  /// Creates a new [StorageSigner] from a given DID Document and a verification method fragment.
118  /// ## Notes
119  /// At this time, this function only supports "JsonWebKey2020"-based verification methods.
120  pub async fn new_from_vm_fragment<D>(
121    storage: &'a Storage<K, I>,
122    document: &D,
123    fragment: &str,
124  ) -> Result<Self, StorageSignerFromVmError>
125  where
126    D: AsRef<CoreDocument>,
127  {
128    use StorageSignerFromVmError as Error;
129    use StorageSignerFromVmErrorKind as ErrorKind;
130
131    let document = document.as_ref();
132    let make_err = |kind| Error {
133      did: document.id().clone(),
134      fragment: fragment.into(),
135      kind,
136    };
137
138    // Resolve the given VM from the given DID and fragment.
139    let vm = document
140      .resolve_method(fragment, None)
141      .ok_or_else(|| make_err(ErrorKind::VmNotFound))?;
142    // Ensure the resolved VM has a supported type - AKA its embedded public key can be converted to a JWK.
143    let MethodData::PublicKeyJwk(jwk) = vm.data() else {
144      return Err(make_err(ErrorKind::UnsupportedVmType(vm.type_().as_str().into())));
145    };
146
147    // Find the corresponding private key in storage.
148    let method_digest = MethodDigest::new(vm).map_err(|e| make_err(e.into()))?;
149    let key_id = storage
150      .key_id_storage
151      .get_key_id(&method_digest)
152      .await
153      .map_err(|e| match e.kind() {
154        KeyIdStorageErrorKind::KeyIdNotFound => make_err(ErrorKind::KeyNotFound),
155        _ => make_err(ErrorKind::StorageError(e.into())),
156      })?;
157
158    Ok(Self::new(storage, key_id, jwk.clone()))
159  }
160}
161
162#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
163#[cfg_attr(feature = "send-sync-storage", async_trait)]
164impl<K, I> Signer<IotaKeySignature> for StorageSigner<'_, K, I>
165where
166  K: JwkStorage + OptionalSync,
167  I: KeyIdStorage + OptionalSync,
168{
169  type KeyId = KeyId;
170
171  fn key_id(&self) -> KeyId {
172    self.key_id.clone()
173  }
174
175  async fn public_key(&self) -> Result<PublicKey, SecretStorageError> {
176    PublicKey::from_jwk(&self.public_key)
177      .map_err(|e| SecretStorageError::Other(anyhow!("failed to convert public key: {e}")))
178  }
179  async fn sign(&self, data: &TransactionData) -> Result<Signature, SecretStorageError> {
180    use fastcrypto::hash::HashFunction;
181
182    let tx_data_bcs =
183      bcs::to_bytes(data).map_err(|e| SecretStorageError::Other(anyhow!("bcs deserialization failed: {e}")))?;
184    let intent_bytes = Intent::iota_transaction().to_bytes();
185    let mut hasher = Blake2b256::default();
186    hasher.update(intent_bytes);
187    hasher.update(&tx_data_bcs);
188    let digest = hasher.finalize().digest;
189
190    let signature_bytes = self
191      .storage
192      .key_storage()
193      .sign(&self.key_id, &digest, &self.public_key)
194      .await
195      .map_err(|e| match e.kind() {
196        KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()),
197        KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()),
198        _ => SecretStorageError::Other(anyhow::anyhow!(e)),
199      })?;
200
201    let public_key = Signer::public_key(self).await?;
202
203    let iota_signature_bytes = [[public_key.flag()].as_slice(), &signature_bytes, public_key.as_ref()].concat();
204
205    Signature::from_bytes(&iota_signature_bytes)
206      .map_err(|e| SecretStorageError::Other(anyhow!("failed to create valid IOTA signature: {e}")))
207  }
208}
209
210#[cfg(feature = "sd-jwt-signer")]
211mod sd_jwt_signer_integration {
212  use crate::KeyStorageError;
213
214  use super::*;
215  use identity_verification::jwu::encode_b64;
216  use identity_verification::jwu::encode_b64_json;
217  use sd_jwt::JsonObject;
218  use sd_jwt::JwsSigner;
219
220  #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
221  #[cfg_attr(feature = "send-sync-storage", async_trait)]
222  impl<K, I> JwsSigner for StorageSigner<'_, K, I>
223  where
224    K: JwkStorage + OptionalSync,
225    I: OptionalSync,
226  {
227    type Error = KeyStorageError;
228    async fn sign(&self, header: &JsonObject, payload: &JsonObject) -> Result<Vec<u8>, Self::Error> {
229      let header_json = encode_b64_json(header)
230        .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::SerializationError).with_source(e))?;
231      let payload_json = encode_b64_json(payload)
232        .map_err(|e| KeyStorageError::new(KeyStorageErrorKind::SerializationError).with_source(e))?;
233
234      let mut signing_input = format!("{header_json}.{payload_json}");
235
236      let signature_bytes = self
237        .storage
238        .key_storage()
239        .sign(&self.key_id, signing_input.as_bytes(), &self.public_key)
240        .await?;
241
242      signing_input.push('.');
243      signing_input.push_str(&encode_b64(signature_bytes));
244
245      Ok(signing_input.into_bytes())
246    }
247  }
248}