identity_storage/key_storage/
memstore.rs

1// Copyright 2020-2025 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  /// Gets the public key corresponding to the given `key_id`.
57  pub async fn get_public_key(&self, key_id: &KeyId) -> KeyStorageResult<Jwk> {
58    self
59      .jwk_store
60      .read()
61      .await
62      .get(key_id)
63      .cloned()
64      .map(|jwk| jwk.to_public().unwrap_or(jwk))
65      .ok_or_else(|| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound))
66  }
67}
68
69// Refer to the `JwkStorage` interface docs for high-level documentation of the individual methods.
70#[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
71#[cfg_attr(feature = "send-sync-storage", async_trait)]
72impl JwkStorage for JwkMemStore {
73  async fn generate(&self, key_type: KeyType, alg: JwsAlgorithm) -> KeyStorageResult<JwkGenOutput> {
74    let key_type: MemStoreKeyType = MemStoreKeyType::try_from(&key_type)?;
75
76    check_key_alg_compatibility(key_type, &alg)?;
77
78    let keypair = match key_type {
79      MemStoreKeyType::Ed25519 => Ed25519KeyPair::generate(&mut rand::thread_rng()),
80      other => {
81        return Err(
82          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
83            .with_custom_message(format!("{other} is not supported")),
84        );
85      }
86    };
87
88    let kid: KeyId = random_key_id();
89
90    let mut jwk: Jwk = keypair.to_jwk().map_err(|err| {
91      KeyStorageError::new(KeyStorageErrorKind::Unspecified)
92        .with_custom_message("could not convert `Ed25519KeyPair` to `Jwk`")
93        .with_source(err)
94    })?;
95    jwk.set_alg(alg.name());
96    jwk.set_kid(jwk.thumbprint_sha256_b64());
97    let public_jwk: Jwk = jwk.to_public().expect("should only panic if kty == oct");
98
99    let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
100    jwk_store.insert(kid.clone(), jwk);
101
102    Ok(JwkGenOutput::new(kid, public_jwk))
103  }
104
105  async fn insert(&self, jwk: Jwk) -> KeyStorageResult<KeyId> {
106    let key_type = MemStoreKeyType::try_from(&jwk)?;
107
108    if !jwk.is_private() {
109      return Err(
110        KeyStorageError::new(KeyStorageErrorKind::Unspecified)
111          .with_custom_message("expected a Jwk with all private key components set"),
112      );
113    }
114
115    match jwk.alg() {
116      Some(alg) => {
117        let alg: JwsAlgorithm = JwsAlgorithm::from_str(alg)
118          .map_err(|err| KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm).with_source(err))?;
119        check_key_alg_compatibility(key_type, &alg)?;
120      }
121      None => {
122        return Err(
123          KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
124            .with_custom_message("expected a Jwk with an `alg` parameter"),
125        );
126      }
127    }
128
129    if jwk.alg().is_none() {
130      return Err(
131        KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
132          .with_custom_message("expected a Jwk with an `alg` parameter"),
133      );
134    }
135
136    let key_id: KeyId = random_key_id();
137
138    let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
139
140    jwk_store.insert(key_id.clone(), jwk);
141
142    Ok(key_id)
143  }
144
145  async fn sign(&self, key_id: &KeyId, data: &[u8], public_key: &Jwk) -> KeyStorageResult<Vec<u8>> {
146    let jwk_store: RwLockReadGuard<'_, JwkKeyStore> = self.jwk_store.read().await;
147
148    // Extract the required alg from the given public key
149    let alg = public_key
150      .alg()
151      .ok_or(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
152      .and_then(|alg_str| {
153        JwsAlgorithm::from_str(alg_str).map_err(|_| KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
154      })?;
155
156    // Check that `kty` is `Okp` and `crv = Ed25519`.
157    match alg {
158      JwsAlgorithm::EdDSA => {
159        let okp_params = public_key.try_okp_params().map_err(|err| {
160          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
161            .with_custom_message(format!("expected a Jwk with Okp params in order to sign with {alg}"))
162            .with_source(err)
163        })?;
164        if okp_params.crv != EdCurve::Ed25519.name() {
165          return Err(
166            KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_custom_message(format!(
167              "expected Jwk with Okp {} crv in order to sign with {alg}",
168              EdCurve::Ed25519
169            )),
170          );
171        }
172      }
173      other => {
174        return Err(
175          KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
176            .with_custom_message(format!("{other} is not supported")),
177        );
178      }
179    };
180
181    // Obtain the corresponding private key and sign `data`.
182    let jwk: &Jwk = jwk_store
183      .get(key_id)
184      .ok_or_else(|| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound))?;
185    let secret_key = Ed25519KeyPair::from_jwk(jwk).map_err(|err| {
186      KeyStorageError::new(KeyStorageErrorKind::Unspecified)
187        .with_custom_message("could not convert `Jwk` to `Ed25519KeyPair`")
188        .with_source(err)
189    })?;
190    Ok(Signer::<Ed25519Signature>::sign(&secret_key, data).as_ref().to_vec())
191  }
192
193  async fn delete(&self, key_id: &KeyId) -> KeyStorageResult<()> {
194    let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
195
196    jwk_store
197      .remove(key_id)
198      .map(|_| ())
199      .ok_or_else(|| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound))
200  }
201
202  async fn exists(&self, key_id: &KeyId) -> KeyStorageResult<bool> {
203    let jwk_store: RwLockReadGuard<'_, JwkKeyStore> = self.jwk_store.read().await;
204    Ok(jwk_store.contains_key(key_id))
205  }
206}
207
208#[derive(Debug, Copy, Clone)]
209enum MemStoreKeyType {
210  Ed25519,
211  BLS12381G2,
212}
213
214impl JwkMemStore {
215  const ED25519_KEY_TYPE_STR: &'static str = "Ed25519";
216  /// The Ed25519 key type.
217  pub const ED25519_KEY_TYPE: KeyType = KeyType::from_static_str(Self::ED25519_KEY_TYPE_STR);
218
219  const BLS12381G2_KEY_TYPE_STR: &'static str = "BLS12381G2";
220  /// The BLS12381G2 key type
221  pub const BLS12381G2_KEY_TYPE: KeyType = KeyType::from_static_str(Self::BLS12381G2_KEY_TYPE_STR);
222
223  const PQ_KEY_TYPE_STR: &'static str = "AKP";
224  /// ML-DSA algorithms key types;
225  pub const PQ_KEY_TYPE: KeyType = KeyType::from_static_str(Self::PQ_KEY_TYPE_STR);
226}
227
228impl MemStoreKeyType {
229  const fn name(&self) -> &'static str {
230    match self {
231      MemStoreKeyType::Ed25519 => JwkMemStore::ED25519_KEY_TYPE_STR,
232      MemStoreKeyType::BLS12381G2 => JwkMemStore::BLS12381G2_KEY_TYPE_STR,
233    }
234  }
235}
236
237impl Display for MemStoreKeyType {
238  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239    f.write_str(self.name())
240  }
241}
242
243impl TryFrom<&KeyType> for MemStoreKeyType {
244  type Error = KeyStorageError;
245
246  fn try_from(value: &KeyType) -> Result<Self, Self::Error> {
247    match value.as_str() {
248      JwkMemStore::ED25519_KEY_TYPE_STR => Ok(MemStoreKeyType::Ed25519),
249      JwkMemStore::BLS12381G2_KEY_TYPE_STR => Ok(MemStoreKeyType::BLS12381G2),
250      _ => Err(KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)),
251    }
252  }
253}
254
255impl TryFrom<&Jwk> for MemStoreKeyType {
256  type Error = KeyStorageError;
257
258  fn try_from(jwk: &Jwk) -> Result<Self, Self::Error> {
259    match jwk.kty() {
260      JwkType::Okp => {
261        let okp_params = jwk.try_okp_params().map_err(|err| {
262          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
263            .with_custom_message("expected Okp parameters for a JWK with `kty` Okp")
264            .with_source(err)
265        })?;
266        match okp_params.try_ed_curve().map_err(|err| {
267          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
268            .with_custom_message("only Ed curves are supported for signing")
269            .with_source(err)
270        })? {
271          EdCurve::Ed25519 => Ok(MemStoreKeyType::Ed25519),
272          curve => Err(
273            KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
274              .with_custom_message(format!("{curve} not supported")),
275          ),
276        }
277      }
278      JwkType::Ec => {
279        let ec_params = jwk.try_ec_params().map_err(|err| {
280          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
281            .with_custom_message("expected EC parameters for a JWK with `kty` Ec")
282            .with_source(err)
283        })?;
284        match ec_params.try_bls_curve().map_err(|err| {
285          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
286            .with_custom_message("only Ed curves are supported for signing")
287            .with_source(err)
288        })? {
289          BlsCurve::BLS12381G2 => Ok(MemStoreKeyType::BLS12381G2),
290          curve => Err(
291            KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
292              .with_custom_message(format!("{curve} not supported")),
293          ),
294        }
295      }
296      other => Err(
297        KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
298          .with_custom_message(format!("Jwk `kty` {other} not supported")),
299      ),
300    }
301  }
302}
303
304impl Default for JwkMemStore {
305  fn default() -> Self {
306    Self::new()
307  }
308}
309
310/// Generate a random alphanumeric string of len 32.
311fn random_key_id() -> KeyId {
312  KeyId::new(rand::distributions::Alphanumeric.sample_string(&mut rand::thread_rng(), 32))
313}
314
315/// Check that the key type can be used with the algorithm.
316fn check_key_alg_compatibility(key_type: MemStoreKeyType, alg: &JwsAlgorithm) -> KeyStorageResult<()> {
317  match (key_type, alg) {
318    (MemStoreKeyType::Ed25519, JwsAlgorithm::EdDSA) => Ok(()),
319    (key_type, alg) => Err(
320      KeyStorageError::new(crate::key_storage::KeyStorageErrorKind::KeyAlgorithmMismatch)
321        .with_custom_message(format!("`cannot use key type `{key_type}` with algorithm `{alg}`")),
322    ),
323  }
324}
325
326#[cfg(feature = "pqc-liboqs")]
327mod pqc_liboqs {
328  use async_trait::async_trait;
329  use identity_verification::jose::jwk::Jwk;
330  use identity_verification::jose::jwk::JwkType;
331  use identity_verification::jose::jws::JwsAlgorithm;
332  use identity_verification::jwk::JwkParams;
333  use identity_verification::jwk::PostQuantumJwk;
334  use identity_verification::jwu;
335  use oqs::sig::Algorithm;
336  use oqs::sig::Sig;
337  use std::str::FromStr;
338  use tokio::sync::RwLockReadGuard;
339  use tokio::sync::RwLockWriteGuard;
340
341  use super::random_key_id;
342  use super::JwkKeyStore;
343  use super::JwkMemStore;
344  use super::KeyId;
345  use super::KeyStorageError;
346  use super::KeyStorageErrorKind;
347  use super::KeyStorageResult;
348  use super::KeyType;
349  use crate::key_storage::jwk_storage_pqc::JwkStoragePQ;
350  use crate::JwkGenOutput;
351
352  fn check_pq_alg_compatibility(alg: &JwsAlgorithm) -> KeyStorageResult<Algorithm> {
353    match alg {
354      JwsAlgorithm::ML_DSA_44 => Ok(Algorithm::MlDsa44),
355      JwsAlgorithm::ML_DSA_65 => Ok(Algorithm::MlDsa65),
356      JwsAlgorithm::ML_DSA_87 => Ok(Algorithm::MlDsa87),
357      JwsAlgorithm::SLH_DSA_SHA2_128s => Ok(Algorithm::SphincsSha2128sSimple),
358      JwsAlgorithm::SLH_DSA_SHAKE_128s => Ok(Algorithm::SphincsShake128sSimple),
359      JwsAlgorithm::SLH_DSA_SHA2_128f => Ok(Algorithm::SphincsSha2128fSimple),
360
361      JwsAlgorithm::SLH_DSA_SHAKE_128f => Ok(Algorithm::SphincsShake128fSimple),
362      JwsAlgorithm::SLH_DSA_SHA2_192s => Ok(Algorithm::SphincsSha2192sSimple),
363      JwsAlgorithm::SLH_DSA_SHAKE_192s => Ok(Algorithm::SphincsShake192sSimple),
364      JwsAlgorithm::SLH_DSA_SHA2_192f => Ok(Algorithm::SphincsSha2192fSimple),
365      JwsAlgorithm::SLH_DSA_SHAKE_192f => Ok(Algorithm::SphincsShake192fSimple),
366      JwsAlgorithm::SLH_DSA_SHA2_256s => Ok(Algorithm::SphincsSha2256sSimple),
367      JwsAlgorithm::SLH_DSA_SHAKE_256s => Ok(Algorithm::SphincsShake256sSimple),
368      JwsAlgorithm::SLH_DSA_SHA2_256f => Ok(Algorithm::SphincsSha2256fSimple),
369      JwsAlgorithm::SLH_DSA_SHAKE_256f => Ok(Algorithm::SphincsShake256fSimple),
370
371      JwsAlgorithm::FALCON512 => Ok(Algorithm::Falcon512),
372      JwsAlgorithm::FALCON1024 => Ok(Algorithm::Falcon1024),
373      other => Err(
374        KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
375          .with_custom_message(format!("{other} is not supported")),
376      ),
377    }
378  }
379
380  /// JwkStoragePQ implementation for JwkMemStore based on liboqs.
381  #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
382  #[cfg_attr(feature = "send-sync-storage", async_trait)]
383  impl JwkStoragePQ for JwkMemStore {
384    async fn generate_pq_key(&self, key_type: KeyType, alg: JwsAlgorithm) -> KeyStorageResult<JwkGenOutput> {
385      if key_type != JwkMemStore::PQ_KEY_TYPE {
386        return Err(
387          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
388            .with_custom_message(format!("unsupported key type {key_type}")),
389        );
390      }
391
392      let oqs_alg = check_pq_alg_compatibility(&alg)?;
393      oqs::init();
394
395      let scheme = Sig::new(oqs_alg).map_err(|err| {
396        KeyStorageError::new(KeyStorageErrorKind::Unspecified)
397          .with_custom_message("signature scheme init failed".to_string())
398          .with_source(err)
399      })?;
400      let (pk, sk) = scheme.keypair().map_err(|err| {
401        KeyStorageError::new(KeyStorageErrorKind::Unspecified)
402          .with_custom_message("keypair generation failed!".to_string())
403          .with_source(err)
404      })?;
405
406      let kid: KeyId = random_key_id();
407
408      let public = jwu::encode_b64(pk.into_vec());
409      let private = jwu::encode_b64(sk.into_vec());
410
411      let mut jwk_params = match alg {
412        JwsAlgorithm::ML_DSA_44 => JwkParams::new(JwkType::Akp),
413        JwsAlgorithm::ML_DSA_65 => JwkParams::new(JwkType::Akp),
414        JwsAlgorithm::ML_DSA_87 => JwkParams::new(JwkType::Akp),
415        JwsAlgorithm::SLH_DSA_SHA2_128s => JwkParams::new(JwkType::Akp),
416        JwsAlgorithm::SLH_DSA_SHAKE_128s => JwkParams::new(JwkType::Akp),
417        JwsAlgorithm::SLH_DSA_SHA2_128f => JwkParams::new(JwkType::Akp),
418        JwsAlgorithm::SLH_DSA_SHAKE_128f => JwkParams::new(JwkType::Akp),
419        JwsAlgorithm::SLH_DSA_SHA2_192s => JwkParams::new(JwkType::Akp),
420        JwsAlgorithm::SLH_DSA_SHAKE_192s => JwkParams::new(JwkType::Akp),
421        JwsAlgorithm::SLH_DSA_SHA2_192f => JwkParams::new(JwkType::Akp),
422        JwsAlgorithm::SLH_DSA_SHAKE_192f => JwkParams::new(JwkType::Akp),
423        JwsAlgorithm::SLH_DSA_SHA2_256s => JwkParams::new(JwkType::Akp),
424        JwsAlgorithm::SLH_DSA_SHAKE_256s => JwkParams::new(JwkType::Akp),
425        JwsAlgorithm::SLH_DSA_SHA2_256f => JwkParams::new(JwkType::Akp),
426        JwsAlgorithm::SLH_DSA_SHAKE_256f => JwkParams::new(JwkType::Akp),
427        JwsAlgorithm::FALCON512 => JwkParams::new(JwkType::Akp),
428        JwsAlgorithm::FALCON1024 => JwkParams::new(JwkType::Akp),
429        other => {
430          return Err(
431            KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
432              .with_custom_message(format!("{other} is not supported")),
433          );
434        }
435      };
436
437      match jwk_params {
438        JwkParams::Akp(ref mut params) => {
439          params.public = public;
440          params.private = Some(private);
441        }
442        _ => {
443          return Err(
444            KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType).with_custom_message("Should NOT happen!"),
445          )
446        }
447      }
448
449      let mut jwk = Jwk::from_params(jwk_params);
450
451      jwk.set_alg(alg.name());
452      jwk.set_kid(jwk.thumbprint_sha256_b64());
453      let public_jwk: Jwk = jwk.to_public().expect("should only panic if kty == oct");
454
455      let mut jwk_store: RwLockWriteGuard<'_, JwkKeyStore> = self.jwk_store.write().await;
456      jwk_store.insert(kid.clone(), jwk);
457
458      Ok(JwkGenOutput::new(kid, public_jwk))
459    }
460
461    async fn pq_sign(
462      &self,
463      key_id: &KeyId,
464      data: &[u8],
465      public_key: &PostQuantumJwk,
466      ctx: Option<&[u8]>,
467    ) -> KeyStorageResult<Vec<u8>> {
468      let jwk_store: RwLockReadGuard<'_, JwkKeyStore> = self.jwk_store.read().await;
469
470      // Extract the required alg from the given public key
471      let alg = public_key
472        .alg()
473        .ok_or(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
474        .and_then(|alg_str| {
475          JwsAlgorithm::from_str(alg_str).map_err(|_| KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
476        })?;
477
478      let oqs_alg = check_pq_alg_compatibility(&alg)?;
479
480      // Check that `kty` is `AKP`.
481      match alg {
482        JwsAlgorithm::ML_DSA_44
483        | JwsAlgorithm::ML_DSA_65
484        | JwsAlgorithm::ML_DSA_87
485        | JwsAlgorithm::SLH_DSA_SHA2_128s
486        | JwsAlgorithm::SLH_DSA_SHAKE_128s
487        | JwsAlgorithm::SLH_DSA_SHA2_128f
488        | JwsAlgorithm::SLH_DSA_SHAKE_128f
489        | JwsAlgorithm::SLH_DSA_SHA2_192s
490        | JwsAlgorithm::SLH_DSA_SHAKE_192s
491        | JwsAlgorithm::SLH_DSA_SHA2_192f
492        | JwsAlgorithm::SLH_DSA_SHAKE_192f
493        | JwsAlgorithm::SLH_DSA_SHA2_256s
494        | JwsAlgorithm::SLH_DSA_SHAKE_256s
495        | JwsAlgorithm::SLH_DSA_SHA2_256f
496        | JwsAlgorithm::SLH_DSA_SHAKE_256f
497        | JwsAlgorithm::FALCON512
498        | JwsAlgorithm::FALCON1024 => public_key.try_akp_params().map_err(|err| {
499          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
500            .with_custom_message(format!("expected a Jwk with AKP params in order to sign with {alg}"))
501            .with_source(err)
502        })?,
503        other => {
504          return Err(
505            KeyStorageError::new(KeyStorageErrorKind::UnsupportedSignatureAlgorithm)
506              .with_custom_message(format!("{other} is not supported")),
507          );
508        }
509      };
510
511      // Obtain the corresponding private key and sign `data`.
512      let jwk: &Jwk = jwk_store
513        .get(key_id)
514        .ok_or_else(|| KeyStorageError::new(KeyStorageErrorKind::KeyNotFound))?;
515
516      let params = jwk.try_akp_params().unwrap();
517
518      let sk_bytes = params
519        .private
520        .as_deref()
521        .map(jwu::decode_b64)
522        .ok_or_else(|| {
523          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
524            .with_custom_message("expected Jwk `private` param to be present")
525        })?
526        .map_err(|err| {
527          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
528            .with_custom_message("unable to decode `private` param")
529            .with_source(err)
530        })?;
531      oqs::init();
532
533      let scheme = Sig::new(oqs_alg).map_err(|err| {
534        KeyStorageError::new(KeyStorageErrorKind::Unspecified)
535          .with_custom_message("signature scheme init failed".to_string())
536          .with_source(err)
537      })?;
538
539      let secret_key = scheme.secret_key_from_bytes(&sk_bytes).ok_or(
540        KeyStorageError::new(KeyStorageErrorKind::Unspecified).with_custom_message("invalid private key".to_string()),
541      )?;
542
543      let signature: oqs::sig::Signature;
544
545      if let Some(ctx) = ctx {
546        if !scheme.has_ctx_str_support() {
547          return Err(
548            KeyStorageError::new(KeyStorageErrorKind::Unspecified)
549              .with_custom_message("signature with ctx is not supported with this algorithm".to_string()),
550          );
551        }
552        signature = scheme.sign_with_ctx_str(data, ctx, secret_key).map_err(|err| {
553          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
554            .with_custom_message("signature computation failed".to_string())
555            .with_source(err)
556        })?;
557      } else {
558        signature = scheme.sign(data, secret_key).map_err(|err| {
559          KeyStorageError::new(KeyStorageErrorKind::Unspecified)
560            .with_custom_message("signature computation failed".to_string())
561            .with_source(err)
562        })?;
563      }
564
565      Ok(signature.into_vec())
566    }
567  }
568}
569
570#[cfg(feature = "jpt-bbs-plus")]
571mod bbs_plus_impl {
572  use std::str::FromStr as _;
573
574  use crate::key_storage::bls::encode_bls_jwk;
575  use crate::key_storage::bls::expand_bls_jwk;
576  use crate::key_storage::bls::generate_bbs_keypair;
577  use crate::key_storage::bls::sign_bbs;
578  use crate::key_storage::bls::update_bbs_signature;
579  use crate::JwkGenOutput;
580  use crate::JwkMemStore;
581  use crate::JwkStorageBbsPlusExt;
582  use crate::KeyId;
583  use crate::KeyStorageError;
584  use crate::KeyStorageErrorKind;
585  use crate::KeyStorageResult;
586  use crate::KeyType;
587  use crate::ProofUpdateCtx;
588  use async_trait::async_trait;
589  use identity_verification::jwk::BlsCurve;
590  use identity_verification::jwk::Jwk;
591  use jsonprooftoken::jpa::algs::ProofAlgorithm;
592
593  use super::random_key_id;
594
595  /// JwkStorageBbsPlusExt implementation for JwkMemStore
596  #[cfg_attr(not(feature = "send-sync-storage"), async_trait(?Send))]
597  #[cfg_attr(feature = "send-sync-storage", async_trait)]
598  impl JwkStorageBbsPlusExt for JwkMemStore {
599    async fn generate_bbs(&self, key_type: KeyType, alg: ProofAlgorithm) -> KeyStorageResult<JwkGenOutput> {
600      if key_type != JwkMemStore::BLS12381G2_KEY_TYPE {
601        return Err(
602          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
603            .with_custom_message(format!("unsupported key type {key_type}")),
604        );
605      }
606
607      let (private_key, public_key) = generate_bbs_keypair(alg)?;
608      let (jwk, public_jwk) = encode_bls_jwk(&private_key, &public_key, alg);
609
610      let kid: KeyId = random_key_id();
611      let mut jwk_store = self.jwk_store.write().await;
612      jwk_store.insert(kid.clone(), jwk);
613
614      Ok(JwkGenOutput::new(kid, public_jwk))
615    }
616
617    async fn sign_bbs(
618      &self,
619      key_id: &KeyId,
620      data: &[Vec<u8>],
621      header: &[u8],
622      public_key: &Jwk,
623    ) -> KeyStorageResult<Vec<u8>> {
624      let jwk_store = self.jwk_store.read().await;
625
626      // Extract the required alg from the given public key
627      let alg = public_key
628        .alg()
629        .and_then(|alg_str| ProofAlgorithm::from_str(alg_str).ok())
630        .ok_or(KeyStorageErrorKind::UnsupportedProofAlgorithm)?;
631
632      // Check the provided JWK represents a BLS12381G2 key.
633      if !public_key
634        .try_ec_params()
635        .map(|ec| ec.crv == BlsCurve::BLS12381G2.to_string())
636        .unwrap_or(false)
637      {
638        return Err(
639          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
640            .with_custom_message(format!("expected a key from the {} curve", BlsCurve::BLS12381G2)),
641        );
642      }
643
644      // Obtain the corresponding private key.
645      let jwk: &Jwk = jwk_store.get(key_id).ok_or(KeyStorageErrorKind::KeyNotFound)?;
646      let (sk, pk) = expand_bls_jwk(jwk)?;
647
648      sign_bbs(alg, data, &sk.expect("jwk is private"), &pk, header)
649    }
650
651    async fn update_signature(
652      &self,
653      key_id: &KeyId,
654      public_key: &Jwk,
655      signature: &[u8],
656      ctx: ProofUpdateCtx,
657    ) -> KeyStorageResult<Vec<u8>> {
658      let jwk_store = self.jwk_store.read().await;
659
660      // Extract the required alg from the given public key
661      let alg = public_key
662        .alg()
663        .ok_or(KeyStorageErrorKind::UnsupportedProofAlgorithm)
664        .and_then(|alg_str| {
665          ProofAlgorithm::from_str(alg_str).map_err(|_| KeyStorageErrorKind::UnsupportedProofAlgorithm)
666        })?;
667
668      // Check the provided JWK represents a BLS12381G2 key.
669      if !public_key
670        .try_ec_params()
671        .map(|ec| ec.crv == BlsCurve::BLS12381G2.to_string())
672        .unwrap_or(false)
673      {
674        return Err(
675          KeyStorageError::new(KeyStorageErrorKind::UnsupportedKeyType)
676            .with_custom_message(format!("expected a key from the {} curve", BlsCurve::BLS12381G2)),
677        );
678      }
679
680      // Obtain the corresponding private key.
681      let jwk = jwk_store.get(key_id).ok_or(KeyStorageErrorKind::KeyNotFound)?;
682      let sk = expand_bls_jwk(jwk)?.0.expect("jwk is private");
683
684      // Update the signature.
685      update_bbs_signature(alg, signature, &sk, &ctx)
686    }
687  }
688}
689pub(crate) mod shared {
690  use core::fmt::Debug;
691  use core::fmt::Formatter;
692  use tokio::sync::RwLock;
693  use tokio::sync::RwLockReadGuard;
694  use tokio::sync::RwLockWriteGuard;
695
696  #[derive(Default)]
697  pub(crate) struct Shared<T>(RwLock<T>);
698
699  impl<T> Shared<T> {
700    pub(crate) fn new(data: T) -> Self {
701      Self(RwLock::new(data))
702    }
703
704    pub(crate) async fn read(&self) -> RwLockReadGuard<'_, T> {
705      self.0.read().await
706    }
707
708    pub(crate) async fn write(&self) -> RwLockWriteGuard<'_, T> {
709      self.0.write().await
710    }
711  }
712
713  impl<T: Debug> Debug for Shared<T> {
714    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
715      Debug::fmt(&self.0, f)
716    }
717  }
718}
719
720#[cfg(test)]
721mod tests {
722  use crate::key_storage::tests::utils::generate_ed25519;
723  use fastcrypto::ed25519::Ed25519PublicKey;
724  use fastcrypto::traits::ToFromBytes as _;
725  use fastcrypto::traits::VerifyingKey as _;
726  use identity_verification::jose::jwk::EcCurve;
727  use identity_verification::jose::jwk::JwkParamsEc;
728  use identity_verification::jwk::FromJwk as _;
729
730  use super::*;
731
732  #[tokio::test]
733  async fn generate_and_sign() {
734    let test_msg: &[u8] = b"test";
735    let store: JwkMemStore = JwkMemStore::new();
736
737    let JwkGenOutput { key_id, jwk } = store
738      .generate(JwkMemStore::ED25519_KEY_TYPE, JwsAlgorithm::EdDSA)
739      .await
740      .unwrap();
741
742    let signature = store.sign(&key_id, test_msg, &jwk.to_public().unwrap()).await.unwrap();
743
744    let public_key = Ed25519PublicKey::from_jwk(&jwk).unwrap();
745    let signature = Ed25519Signature::from_bytes(&signature).unwrap();
746
747    assert!(public_key.verify(test_msg, &signature).is_ok());
748    assert!(store.exists(&key_id).await.unwrap());
749    store.delete(&key_id).await.unwrap();
750  }
751
752  #[tokio::test]
753  async fn insert() {
754    let store: JwkMemStore = JwkMemStore::new();
755
756    let key_pair = generate_ed25519();
757    let mut jwk: Jwk = key_pair.to_jwk().unwrap();
758
759    // INVALID: Inserting a Jwk without an `alg` parameter should fail.
760    let err = store.insert(jwk.clone()).await.unwrap_err();
761    assert!(matches!(err.kind(), KeyStorageErrorKind::UnsupportedSignatureAlgorithm));
762
763    // VALID: Inserting a Jwk with all private key components set should succeed.
764    jwk.set_alg(JwsAlgorithm::EdDSA.name());
765    store.insert(jwk.clone()).await.unwrap();
766
767    // INVALID: Inserting a Jwk with all private key components unset should fail.
768    let err = store.insert(jwk.to_public().unwrap()).await.unwrap_err();
769    assert!(matches!(err.kind(), KeyStorageErrorKind::Unspecified))
770  }
771
772  #[tokio::test]
773  async fn exists() {
774    let store: JwkMemStore = JwkMemStore::new();
775    assert!(!store.exists(&KeyId::new("non-existent-id")).await.unwrap());
776  }
777
778  #[tokio::test]
779  async fn incompatible_key_type() {
780    let store: JwkMemStore = JwkMemStore::new();
781
782    let mut ec_params = JwkParamsEc::new();
783    ec_params.crv = EcCurve::P256.name().to_string();
784    ec_params.x = String::new();
785    ec_params.y = String::new();
786    ec_params.d = Some(String::new());
787    let jwk_ec = Jwk::from_params(ec_params);
788
789    let err = store.insert(jwk_ec).await.unwrap_err();
790    assert!(matches!(err.kind(), KeyStorageErrorKind::UnsupportedKeyType));
791  }
792
793  #[tokio::test]
794  async fn incompatible_key_alg() {
795    let store: JwkMemStore = JwkMemStore::new();
796
797    let key_pair = generate_ed25519();
798    let mut jwk: Jwk = key_pair.to_jwk().unwrap();
799    jwk.set_alg(JwsAlgorithm::ES256.name());
800
801    // INVALID: Inserting an Ed25519 key with the ES256 alg is not compatible.
802    let err = store.insert(jwk.clone()).await.unwrap_err();
803    assert!(matches!(err.kind(), KeyStorageErrorKind::KeyAlgorithmMismatch));
804  }
805}