identity_storage/key_storage/
memstore.rs

1// Copyright 2020-2023 IOTA Stiftung, Fondazione Links
2// SPDX-License-Identifier: Apache-2.0
3
4use core::fmt::Debug;
5use std::collections::HashMap;
6use std::fmt::Display;
7use std::str::FromStr;
8
9use async_trait::async_trait;
10use fastcrypto::ed25519::Ed25519KeyPair;
11use fastcrypto::ed25519::Ed25519Signature;
12use fastcrypto::traits::KeyPair as _;
13use fastcrypto::traits::Signer;
14use identity_verification::jose::jwk::EdCurve;
15use identity_verification::jose::jwk::Jwk;
16use identity_verification::jose::jwk::JwkType;
17use identity_verification::jose::jws::JwsAlgorithm;
18use identity_verification::jwk::BlsCurve;
19use identity_verification::jwk::FromJwk as _;
20use identity_verification::jwk::ToJwk as _;
21use rand::distributions::DistString;
22use shared::Shared;
23use tokio::sync::RwLockReadGuard;
24use tokio::sync::RwLockWriteGuard;
25
26use super::jwk_gen_output::JwkGenOutput;
27use super::KeyId;
28use super::KeyStorageError;
29use super::KeyStorageErrorKind;
30use super::KeyStorageResult;
31use super::KeyType;
32use crate::key_storage::JwkStorage;
33
34/// The map from key ids to JWKs.
35type JwkKeyStore = HashMap<KeyId, Jwk>;
36
37/// An insecure, in-memory [`JwkStorage`] implementation that serves as an example and may be used in tests.
38#[derive(Debug)]
39pub struct JwkMemStore {
40  jwk_store: Shared<JwkKeyStore>,
41}
42
43impl JwkMemStore {
44  /// Creates a new, empty `JwkMemStore` instance.
45  pub fn new() -> Self {
46    Self {
47      jwk_store: Shared::new(HashMap::new()),
48    }
49  }
50
51  /// Returns the number of items contained in the [`JwkMemStore`].
52  pub async fn count(&self) -> usize {
53    self.jwk_store.read().await.keys().count()
54  }
55}
56
57// Refer to the `JwkStorage` interface docs for high-level documentation of the individual methods.
58#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
59#[cfg_attr(feature = "send-sync-storage", async_trait)]
60impl JwkStorage for JwkMemStore {
61  async fn generate(&self, key_type: KeyType, alg: JwsAlgorithm) -> KeyStorageResult<JwkGenOutput> {
62    let key_type: MemStoreKeyType = MemStoreKeyType::try_from(&key_type)?;
63
64    check_key_alg_compatibility(key_type, &alg)?;
65
66    let keypair = match key_type {
67      MemStoreKeyType::Ed25519 => Ed25519KeyPair::generate(&mut rand::thread_rng()),
68      other => {
69        return Err(
70          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
71            .with_custom_message(format!("{other} is not supported")),
72        );
73      }
74    };
75
76    let kid: KeyId = random_key_id();
77
78    let mut jwk: Jwk = keypair.to_jwk().map_err(|err| {
79      KeyStorageError::new(KeyStorageErrorKind::Unspecified)
80        .with_custom_message("could not convert `Ed25519KeyPair` to `Jwk`")
81        .with_source(err)
82    })?;
83    jwk.set_alg(alg.name());
84    jwk.set_kid(jwk.thumbprint_sha256_b64());
85    let public_jwk: Jwk = jwk.to_public().expect("should only panic if kty == oct");
86
87    let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
88    jwk_store.insert(kid.clone(), jwk);
89
90    Ok(JwkGenOutput::new(kid, public_jwk))
91  }
92
93  async fn insert(&self, jwk: Jwk) -> KeyStorageResult<KeyId> {
94    let key_type = MemStoreKeyType::try_from(&jwk)?;
95
96    if !jwk.is_private() {
97      return Err(
98        KeyStorageError::new(KeyStorageErrorKind::Unspecified)
99          .with_custom_message("expected a Jwk with all private key components set"),
100      );
101    }
102
103    match jwk.alg() {
104      Some(alg) => {
105        let alg: JwsAlgorithm = JwsAlgorithm::from_str(alg)
106          .map_err(|err| KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm).with_source(err))?;
107        check_key_alg_compatibility(key_type, &alg)?;
108      }
109      None => {
110        return Err(
111          KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
112            .with_custom_message("expected a Jwk with an `alg` parameter"),
113        );
114      }
115    }
116
117    if jwk.alg().is_none() {
118      return Err(
119        KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
120          .with_custom_message("expected a Jwk with an `alg` parameter"),
121      );
122    }
123
124    let key_id: KeyId = random_key_id();
125
126    let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
127
128    jwk_store.insert(key_id.clone(), jwk);
129
130    Ok(key_id)
131  }
132
133  async fn sign(&self, key_id: &KeyId, data: &[u8], public_key: &Jwk) -> KeyStorageResult<Vec<u8>> {
134    let jwk_store: RwLockReadGuard<'_, JwkKeyStore> = self.jwk_store.read().await;
135
136    // Extract the required alg from the given public key
137    let alg = public_key
138      .alg()
139      .ok_or(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
140      .and_then(|alg_str| {
141        JwsAlgorithm::from_str(alg_str).map_err(|_| KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
142      })?;
143
144    // Check that `kty` is `Okp` and `crv = Ed25519`.
145    match alg {
146      JwsAlgorithm::EdDSA => {
147        let okp_params = public_key.try_okp_params().map_err(|err| {
148          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
149            .with_custom_message(format!("expected a Jwk with Okp params in order to sign with {alg}"))
150            .with_source(err)
151        })?;
152        if okp_params.crv != EdCurve::Ed25519.name() {
153          return Err(
154            KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_custom_message(format!(
155              "expected Jwk with Okp {} crv in order to sign with {alg}",
156              EdCurve::Ed25519
157            )),
158          );
159        }
160      }
161      other => {
162        return Err(
163          KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
164            .with_custom_message(format!("{other} is not supported")),
165        );
166      }
167    };
168
169    // Obtain the corresponding private key and sign `data`.
170    let jwk: &Jwk = jwk_store
171      .get(key_id)
172      .ok_or_else(|| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound))?;
173    let secret_key = Ed25519KeyPair::from_jwk(jwk).map_err(|err| {
174      KeyStorageError::new(KeyStorageErrorKind::Unspecified)
175        .with_custom_message("could not convert `Jwk` to `Ed25519KeyPair`")
176        .with_source(err)
177    })?;
178    Ok(Signer::<Ed25519Signature>::sign(&secret_key, data).as_ref().to_vec())
179  }
180
181  async fn delete(&self, key_id: &KeyId) -> KeyStorageResult<()> {
182    let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
183
184    jwk_store
185      .remove(key_id)
186      .map(|_| ())
187      .ok_or_else(|| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound))
188  }
189
190  async fn exists(&self, key_id: &KeyId) -> KeyStorageResult<bool> {
191    let jwk_store: RwLockReadGuard<'_, JwkKeyStore> = self.jwk_store.read().await;
192    Ok(jwk_store.contains_key(key_id))
193  }
194}
195
196#[derive(Debug, Copy, Clone)]
197enum MemStoreKeyType {
198  Ed25519,
199  BLS12381G2,
200}
201
202impl JwkMemStore {
203  const ED25519_KEY_TYPE_STR: &'static str = "Ed25519";
204  /// The Ed25519 key type.
205  pub const ED25519_KEY_TYPE: KeyType = KeyType::from_static_str(Self::ED25519_KEY_TYPE_STR);
206
207  const BLS12381G2_KEY_TYPE_STR: &'static str = "BLS12381G2";
208  /// The BLS12381G2 key type
209  pub const BLS12381G2_KEY_TYPE: KeyType = KeyType::from_static_str(Self::BLS12381G2_KEY_TYPE_STR);
210}
211
212impl MemStoreKeyType {
213  const fn name(&self) -> &'static str {
214    match self {
215      MemStoreKeyType::Ed25519 => JwkMemStore::ED25519_KEY_TYPE_STR,
216      MemStoreKeyType::BLS12381G2 => JwkMemStore::BLS12381G2_KEY_TYPE_STR,
217    }
218  }
219}
220
221impl Display for MemStoreKeyType {
222  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
223    f.write_str(self.name())
224  }
225}
226
227impl TryFrom<&KeyType> for MemStoreKeyType {
228  type Error = KeyStorageError;
229
230  fn try_from(value: &KeyType) -> Result<Self, Self::Error> {
231    match value.as_str() {
232      JwkMemStore::ED25519_KEY_TYPE_STR => Ok(MemStoreKeyType::Ed25519),
233      JwkMemStore::BLS12381G2_KEY_TYPE_STR => Ok(MemStoreKeyType::BLS12381G2),
234      _ => Err(KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)),
235    }
236  }
237}
238
239impl TryFrom<&Jwk> for MemStoreKeyType {
240  type Error = KeyStorageError;
241
242  fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
243    match jwk.kty() {
244      JwkType::Okp => {
245        let okp_params = jwk.try_okp_params().map_err(|err| {
246          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
247            .with_custom_message("expected Okp parameters for a JWK with `kty` Okp")
248            .with_source(err)
249        })?;
250        match okp_params.try_ed_curve().map_err(|err| {
251          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
252            .with_custom_message("only Ed curves are supported for signing")
253            .with_source(err)
254        })? {
255          EdCurve::Ed25519 => Ok(MemStoreKeyType::Ed25519),
256          curve => Err(
257            KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
258              .with_custom_message(format!("{curve} not supported")),
259          ),
260        }
261      }
262      JwkType::Ec => {
263        let ec_params = jwk.try_ec_params().map_err(|err| {
264          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
265            .with_custom_message("expected EC parameters for a JWK with `kty` Ec")
266            .with_source(err)
267        })?;
268        match ec_params.try_bls_curve().map_err(|err| {
269          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
270            .with_custom_message("only Ed curves are supported for signing")
271            .with_source(err)
272        })? {
273          BlsCurve::BLS12381G2 => Ok(MemStoreKeyType::BLS12381G2),
274          curve => Err(
275            KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
276              .with_custom_message(format!("{curve} not supported")),
277          ),
278        }
279      }
280      other => Err(
281        KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
282          .with_custom_message(format!("Jwk `kty` {other} not supported")),
283      ),
284    }
285  }
286}
287
288impl Default for JwkMemStore {
289  fn default() -> Self {
290    Self::new()
291  }
292}
293
294/// Generate a random alphanumeric string of len 32.
295fn random_key_id() -> KeyId {
296  KeyId::new(rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 32))
297}
298
299/// Check that the key type can be used with the algorithm.
300fn check_key_alg_compatibility(key_type: MemStoreKeyType, alg: &JwsAlgorithm) -> KeyStorageResult<()> {
301  match (key_type, alg) {
302    (MemStoreKeyType::Ed25519, JwsAlgorithm::EdDSA) => Ok(()),
303    (key_type, alg) => Err(
304      KeyStorageError::new(crate::key_storage::KeyStorageErrorKind::KeyAlgorithmMismatch)
305        .with_custom_message(format!("`cannot use key type `{key_type}` with algorithm `{alg}`")),
306    ),
307  }
308}
309
310#[cfg(feature = "jpt-bbs-plus")]
311mod bbs_plus_impl {
312  use std::str::FromStr as _;
313
314  use crate::key_storage::bls::encode_bls_jwk;
315  use crate::key_storage::bls::expand_bls_jwk;
316  use crate::key_storage::bls::generate_bbs_keypair;
317  use crate::key_storage::bls::sign_bbs;
318  use crate::key_storage::bls::update_bbs_signature;
319  use crate::JwkGenOutput;
320  use crate::JwkMemStore;
321  use crate::JwkStorageBbsPlusExt;
322  use crate::KeyId;
323  use crate::KeyStorageError;
324  use crate::KeyStorageErrorKind;
325  use crate::KeyStorageResult;
326  use crate::KeyType;
327  use crate::ProofUpdateCtx;
328  use async_trait::async_trait;
329  use identity_verification::jwk::BlsCurve;
330  use identity_verification::jwk::Jwk;
331  use jsonprooftoken::jpa::algs::ProofAlgorithm;
332
333  use super::random_key_id;
334
335  /// JwkStorageBbsPlusExt implementation for JwkMemStore
336  #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
337  #[cfg_attr(feature = "send-sync-storage", async_trait)]
338  impl JwkStorageBbsPlusExt for JwkMemStore {
339    async fn generate_bbs(&self, key_type: KeyType, alg: ProofAlgorithm) -> KeyStorageResult<JwkGenOutput> {
340      if key_type != JwkMemStore::BLS12381G2_KEY_TYPE {
341        return Err(
342          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
343            .with_custom_message(format!("unsupported key type {key_type}")),
344        );
345      }
346
347      let (private_key, public_key) = generate_bbs_keypair(alg)?;
348      let (jwk, public_jwk) = encode_bls_jwk(&private_key, &public_key, alg);
349
350      let kid: KeyId = random_key_id();
351      let mut jwk_store = self.jwk_store.write().await;
352      jwk_store.insert(kid.clone(), jwk);
353
354      Ok(JwkGenOutput::new(kid, public_jwk))
355    }
356
357    async fn sign_bbs(
358      &self,
359      key_id: &KeyId,
360      data: &[Vec<u8>],
361      header: &[u8],
362      public_key: &Jwk,
363    ) -> KeyStorageResult<Vec<u8>> {
364      let jwk_store = self.jwk_store.read().await;
365
366      // Extract the required alg from the given public key
367      let alg = public_key
368        .alg()
369        .and_then(|alg_str| ProofAlgorithm::from_str(alg_str).ok())
370        .ok_or(KeyStorageErrorKind::UnsupportedProofAlgorithm)?;
371
372      // Check the provided JWK represents a BLS12381G2 key.
373      if !public_key
374        .try_ec_params()
375        .map(|ec| ec.crv == BlsCurve::BLS12381G2.to_string())
376        .unwrap_or(false)
377      {
378        return Err(
379          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
380            .with_custom_message(format!("expected a key from the {} curve", BlsCurve::BLS12381G2)),
381        );
382      }
383
384      // Obtain the corresponding private key.
385      let jwk: &Jwk = jwk_store.get(key_id).ok_or(KeyStorageErrorKind::KeyNotFound)?;
386      let (sk, pk) = expand_bls_jwk(jwk)?;
387
388      sign_bbs(alg, data, &sk.expect("jwk is private"), &pk, header)
389    }
390
391    async fn update_signature(
392      &self,
393      key_id: &KeyId,
394      public_key: &Jwk,
395      signature: &[u8],
396      ctx: ProofUpdateCtx,
397    ) -> KeyStorageResult<Vec<u8>> {
398      let jwk_store = self.jwk_store.read().await;
399
400      // Extract the required alg from the given public key
401      let alg = public_key
402        .alg()
403        .ok_or(KeyStorageErrorKind::UnsupportedProofAlgorithm)
404        .and_then(|alg_str| {
405          ProofAlgorithm::from_str(alg_str).map_err(|_| KeyStorageErrorKind::UnsupportedProofAlgorithm)
406        })?;
407
408      // Check the provided JWK represents a BLS12381G2 key.
409      if !public_key
410        .try_ec_params()
411        .map(|ec| ec.crv == BlsCurve::BLS12381G2.to_string())
412        .unwrap_or(false)
413      {
414        return Err(
415          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
416            .with_custom_message(format!("expected a key from the {} curve", BlsCurve::BLS12381G2)),
417        );
418      }
419
420      // Obtain the corresponding private key.
421      let jwk = jwk_store.get(key_id).ok_or(KeyStorageErrorKind::KeyNotFound)?;
422      let sk = expand_bls_jwk(jwk)?.0.expect("jwk is private");
423
424      // Update the signature.
425      update_bbs_signature(alg, signature, &sk, &ctx)
426    }
427  }
428}
429pub(crate) mod shared {
430  use core::fmt::Debug;
431  use core::fmt::Formatter;
432  use tokio::sync::RwLock;
433  use tokio::sync::RwLockReadGuard;
434  use tokio::sync::RwLockWriteGuard;
435
436  #[derive(Default)]
437  pub(crate) struct Shared<T>(RwLock<T>);
438
439  impl<T> Shared<T> {
440    pub(crate) fn new(data: T) -> Self {
441      Self(RwLock::new(data))
442    }
443
444    pub(crate) async fn read(&self) -> RwLockReadGuard<'_, T> {
445      self.0.read().await
446    }
447
448    pub(crate) async fn write(&self) -> RwLockWriteGuard<'_, T> {
449      self.0.write().await
450    }
451  }
452
453  impl<T: Debug> Debug for Shared<T> {
454    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
455      Debug::fmt(&self.0, f)
456    }
457  }
458}
459
460#[cfg(test)]
461mod tests {
462  use crate::key_storage::tests::utils::generate_ed25519;
463  use fastcrypto::ed25519::Ed25519PublicKey;
464  use fastcrypto::traits::ToFromBytes as _;
465  use fastcrypto::traits::VerifyingKey as _;
466  use identity_verification::jose::jwk::EcCurve;
467  use identity_verification::jose::jwk::JwkParamsEc;
468  use identity_verification::jwk::FromJwk as _;
469
470  use super::*;
471
472  #[tokio::test]
473  async fn generate_and_sign() {
474    let test_msg: &[u8] = b"test";
475    let store: JwkMemStore = JwkMemStore::new();
476
477    let JwkGenOutput { key_id, jwk } = store
478      .generate(JwkMemStore::ED25519_KEY_TYPE, JwsAlgorithm::EdDSA)
479      .await
480      .unwrap();
481
482    let signature = store.sign(&key_id, test_msg, &jwk.to_public().unwrap()).await.unwrap();
483
484    let public_key = Ed25519PublicKey::from_jwk(&jwk).unwrap();
485    let signature = Ed25519Signature::from_bytes(&signature).unwrap();
486
487    assert!(public_key.verify(test_msg, &signature).is_ok());
488    assert!(store.exists(&key_id).await.unwrap());
489    store.delete(&key_id).await.unwrap();
490  }
491
492  #[tokio::test]
493  async fn insert() {
494    let store: JwkMemStore = JwkMemStore::new();
495
496    let key_pair = generate_ed25519();
497    let mut jwk: Jwk = key_pair.to_jwk().unwrap();
498
499    // INVALID: Inserting a Jwk without an `alg` parameter should fail.
500    let err = store.insert(jwk.clone()).await.unwrap_err();
501    assert!(matches!(err.kind(), KeyStorageErrorKind::UnsupportedSignatureAlgorithm));
502
503    // VALID: Inserting a Jwk with all private key components set should succeed.
504    jwk.set_alg(JwsAlgorithm::EdDSA.name());
505    store.insert(jwk.clone()).await.unwrap();
506
507    // INVALID: Inserting a Jwk with all private key components unset should fail.
508    let err = store.insert(jwk.to_public().unwrap()).await.unwrap_err();
509    assert!(matches!(err.kind(), KeyStorageErrorKind::Unspecified))
510  }
511
512  #[tokio::test]
513  async fn exists() {
514    let store: JwkMemStore = JwkMemStore::new();
515    assert!(!store.exists(&KeyId::new("non-existent-id")).await.unwrap());
516  }
517
518  #[tokio::test]
519  async fn incompatible_key_type() {
520    let store: JwkMemStore = JwkMemStore::new();
521
522    let mut ec_params = JwkParamsEc::new();
523    ec_params.crv = EcCurve::P256.name().to_string();
524    ec_params.x = String::new();
525    ec_params.y = String::new();
526    ec_params.d = Some(String::new());
527    let jwk_ec = Jwk::from_params(ec_params);
528
529    let err = store.insert(jwk_ec).await.unwrap_err();
530    assert!(matches!(err.kind(), KeyStorageErrorKind::UnsupportedKeyType));
531  }
532
533  #[tokio::test]
534  async fn incompatible_key_alg() {
535    let store: JwkMemStore = JwkMemStore::new();
536
537    let key_pair = generate_ed25519();
538    let mut jwk: Jwk = key_pair.to_jwk().unwrap();
539    jwk.set_alg(JwsAlgorithm::ES256.name());
540
541    // INVALID: Inserting an Ed25519 key with the ES256 alg is not compatible.
542    let err = store.insert(jwk.clone()).await.unwrap_err();
543    assert!(matches!(err.kind(), KeyStorageErrorKind::KeyAlgorithmMismatch));
544  }
545}