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_verification::jwk::FromJwk as _;
10use identity_verification::jwk::Jwk;
11
12use iota_interaction::shared_crypto::intent::Intent;
13use iota_interaction::types::crypto::PublicKey;
14use iota_interaction::types::crypto::Signature;
15
16use iota_interaction::types::transaction::TransactionData;
17use iota_interaction::IotaKeySignature;
18use iota_interaction::OptionalSync;
19use secret_storage::Error as SecretStorageError;
20use secret_storage::Signer;
21
22use crate::JwkStorage;
23use crate::KeyId;
24use crate::KeyIdStorage;
25use crate::KeyStorageErrorKind;
26use crate::Storage;
27
28/// Signer that offers signing capabilities for `Signer` trait from `secret_storage`.
29/// `Storage` is used to sign.
30pub struct StorageSigner<'a, K, I> {
31  key_id: KeyId,
32  public_key: Jwk,
33  storage: &'a Storage<K, I>,
34}
35
36impl<K, I> Clone for StorageSigner<'_, K, I> {
37  fn clone(&self) -> Self {
38    StorageSigner {
39      key_id: self.key_id.clone(),
40      public_key: self.public_key.clone(),
41      storage: self.storage,
42    }
43  }
44}
45
46impl<'a, K, I> StorageSigner<'a, K, I> {
47  /// Creates new `StorageSigner` with reference to a `Storage` instance.
48  pub fn new(storage: &'a Storage<K, I>, key_id: KeyId, public_key: Jwk) -> Self {
49    Self {
50      key_id,
51      public_key,
52      storage,
53    }
54  }
55
56  /// Returns a reference to the [`KeyId`] of the key used by this [`Signer`].
57  pub fn key_id(&self) -> &KeyId {
58    &self.key_id
59  }
60
61  /// Returns this [`Signer`]'s public key as [`Jwk`].
62  pub fn public_key_jwk(&self) -> &Jwk {
63    &self.public_key
64  }
65
66  /// Returns a reference to this [`Signer`]'s [`Storage`].
67  pub fn storage(&self) -> &Storage<K, I> {
68    self.storage
69  }
70}
71
72#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
73#[cfg_attr(feature = "send-sync-storage", async_trait)]
74impl<K, I> Signer<IotaKeySignature> for StorageSigner<'_, K, I>
75where
76  K: JwkStorage + OptionalSync,
77  I: KeyIdStorage + OptionalSync,
78{
79  type KeyId = KeyId;
80
81  fn key_id(&self) -> KeyId {
82    self.key_id.clone()
83  }
84
85  async fn public_key(&self) -> Result<PublicKey, SecretStorageError> {
86    PublicKey::from_jwk(&self.public_key)
87      .map_err(|e| SecretStorageError::Other(anyhow!("failed to convert public key: {e}")))
88  }
89  async fn sign(&self, data: &TransactionData) -> Result<Signature, SecretStorageError> {
90    use fastcrypto::hash::HashFunction;
91
92    let tx_data_bcs =
93      bcs::to_bytes(data).map_err(|e| SecretStorageError::Other(anyhow!("bcs deserialization failed: {e}")))?;
94    let intent_bytes = Intent::iota_transaction().to_bytes();
95    let mut hasher = Blake2b256::default();
96    hasher.update(intent_bytes);
97    hasher.update(&tx_data_bcs);
98    let digest = hasher.finalize().digest;
99
100    let signature_bytes = self
101      .storage
102      .key_storage()
103      .sign(&self.key_id, &digest, &self.public_key)
104      .await
105      .map_err(|e| match e.kind() {
106        KeyStorageErrorKind::KeyNotFound => SecretStorageError::KeyNotFound(e.to_string()),
107        KeyStorageErrorKind::RetryableIOFailure => SecretStorageError::StoreDisconnected(e.to_string()),
108        _ => SecretStorageError::Other(anyhow::anyhow!(e)),
109      })?;
110
111    let public_key = Signer::public_key(self).await?;
112
113    let iota_signature_bytes = [[public_key.flag()].as_slice(), &signature_bytes, public_key.as_ref()].concat();
114
115    Signature::from_bytes(&iota_signature_bytes)
116      .map_err(|e| SecretStorageError::Other(anyhow!("failed to create valid IOTA signature: {e}")))
117  }
118}