1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use core::fmt::Debug;

use async_trait::async_trait;

use identity_core::crypto::KeyType;
use identity_core::crypto::PrivateKey;
use identity_core::crypto::PublicKey;
use identity_iota_core::did::IotaDID;
use identity_iota_core::document::IotaDocument;
use identity_iota_core::tangle::NetworkName;

use crate::error::Result;
use crate::identity::ChainState;
use crate::types::CekAlgorithm;
use crate::types::EncryptedData;
use crate::types::EncryptionAlgorithm;
use crate::types::KeyLocation;
use crate::types::Signature;

#[cfg(not(feature = "send-sync-storage"))]
mod storage_sub_trait {
  pub trait StorageSendSyncMaybe {}
  impl<S: super::Storage> StorageSendSyncMaybe for S {}
}

#[cfg(feature = "send-sync-storage")]
mod storage_sub_trait {
  pub trait StorageSendSyncMaybe: Send + Sync {}
  impl<S: Send + Sync + super::Storage> StorageSendSyncMaybe for S {}
}

/// An interface for Account storage implementations.
///
/// The [`Storage`] interface is used for secure key operations, such as key generation and signing,
/// as well as key-value like storage of data structures, such as DID documents.
///
/// # Identifiers
///
/// Implementations of this interface are expected to uniquely identify keys through the
/// combination of DID _and_ `KeyLocation`.
///
/// An implementation recommendation is to use the DID as a partition key. Everything related to a DID
/// can be stored in a partition identified by that DID. Keys belonging to a DID can then be identified
/// by [`KeyLocation`]s in that partition.
///
/// # DID List
///
/// The storage is expected to maintain a list of stored DIDs. DIDs created with `did_create` should be
/// inserted into the list, and removed when calling `did_purge`.
/// Other operations on the list are `did_exists` and `did_list`.
///
/// # Thread-Safety
///
/// Note: This only applies if the `send-sync-storage` feature is enabled.
///
/// Since the DID list is a global data structure per storage instance, modifications to that list
/// need to be carefully synchronized.
/// Other operations can be executed concurrently and don't need to be synchronized globally,
/// as it is a user error to create more than one `Account` for the same identity.
/// Regardless of that, a storage implementation still needs to be thread-safe as defined by
/// the `Send` and `Sync` traits.
///
/// # Implementation example
///
/// See [`MemStore`][crate::storage::MemStore] for a test/example implementation.
#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
#[cfg_attr(feature = "send-sync-storage", async_trait)]
pub trait Storage: storage_sub_trait::StorageSendSyncMaybe + Debug {
  /// Creates a new identity for the given `network`.
  ///
  /// - Uses the given Ed25519 `private_key` or generates a new key if it's `None`.
  /// - Returns an error if the DID already exists.
  /// - Adds the newly created DID to a list which can be accessed via [`Storage::did_list`].
  ///
  /// Returns the generated DID and the location at which the key was stored.
  async fn did_create(
    &self,
    network: NetworkName,
    fragment: &str,
    private_key: Option<PrivateKey>,
  ) -> Result<(IotaDID, KeyLocation)>;

  /// Removes the keys and any other state for the given `did`.
  ///
  /// This operation is idempotent: it does not fail if the given `did` does not (or no longer) exist.
  ///
  /// Returns `true` if the did and its associated data was removed, `false` if nothing was done.
  async fn did_purge(&self, did: &IotaDID) -> Result<bool>;

  /// Returns `true` if `did` exists in the list of stored DIDs.
  async fn did_exists(&self, did: &IotaDID) -> Result<bool>;

  /// Returns the list of stored DIDs.
  async fn did_list(&self) -> Result<Vec<IotaDID>>;

  /// Generates a new key for the given `did` with the given `key_type` and `fragment` identifier
  /// and returns the location of the newly generated key.
  async fn key_generate(&self, did: &IotaDID, key_type: KeyType, fragment: &str) -> Result<KeyLocation>;

  /// Inserts a private key at the specified `location`.
  ///
  /// If a key at `location` exists, it is overwritten.
  async fn key_insert(&self, did: &IotaDID, location: &KeyLocation, private_key: PrivateKey) -> Result<()>;

  /// Retrieves the public key from `location`.
  async fn key_public(&self, did: &IotaDID, location: &KeyLocation) -> Result<PublicKey>;

  /// Deletes the key at `location`.
  ///
  /// This operation is idempotent: it does not fail if the key does not exist.
  ///
  /// Returns `true` if it removed the key, `false` if nothing was done.
  async fn key_delete(&self, did: &IotaDID, location: &KeyLocation) -> Result<bool>;

  /// Signs `data` with the private key at the specified `location`.
  async fn key_sign(&self, did: &IotaDID, location: &KeyLocation, data: Vec<u8>) -> Result<Signature>;

  /// Returns `true` if a key exists at the specified `location`.
  async fn key_exists(&self, did: &IotaDID, location: &KeyLocation) -> Result<bool>;

  /// Encrypts the given `plaintext` with the specified `encryption_algorithm` and `cek_algorithm`.
  ///
  /// Returns an [`EncryptedData`] instance.
  #[cfg(feature = "encryption")]
  async fn data_encrypt(
    &self,
    did: &IotaDID,
    plaintext: Vec<u8>,
    associated_data: Vec<u8>,
    encryption_algorithm: &EncryptionAlgorithm,
    cek_algorithm: &CekAlgorithm,
    public_key: PublicKey,
  ) -> Result<EncryptedData>;

  /// Decrypts the given `data` with the specified `encryption_algorithm` and `cek_algorithm`.
  ///
  /// Returns the decrypted text.
  #[cfg(feature = "encryption")]
  async fn data_decrypt(
    &self,
    did: &IotaDID,
    data: EncryptedData,
    encryption_algorithm: &EncryptionAlgorithm,
    cek_algorithm: &CekAlgorithm,
    private_key: &KeyLocation,
  ) -> Result<Vec<u8>>;

  /// Returns the chain state of the identity specified by `did`.
  async fn chain_state_get(&self, did: &IotaDID) -> Result<Option<ChainState>>;

  /// Set the chain state of the identity specified by `did`.
  async fn chain_state_set(&self, did: &IotaDID, chain_state: &ChainState) -> Result<()>;

  /// Returns the [`IotaDocument`] of the identity specified by `did`.
  async fn document_get(&self, did: &IotaDID) -> Result<Option<IotaDocument>>;

  /// Sets a new state for the identity specified by `did`.
  async fn document_set(&self, did: &IotaDID, state: &IotaDocument) -> Result<()>;

  /// Persists any unsaved changes.
  async fn flush_changes(&self) -> Result<()>;
}