Skip to main content

identity_kit/keys/
in_memory.rs

1//! `InMemoryKeyStore`: keys in process memory, for tests and development.
2
3use core::fmt;
4use std::sync::{Mutex, PoisonError, RwLock};
5
6use indexmap::IndexMap;
7
8use super::local::{self, Entry, SeededRandom};
9use super::{KeyId, KeySpec, KeyStore, KeyStoreCapabilities, KeyStoreError};
10use crate::jose::{Jwk, JwsAlgorithm};
11
12/// A [`KeyStore`] holding its keys in memory.
13///
14/// Signs EdDSA, ES256 and (feature `secp256k1`) ES256K; generates and deletes. Keys are lost
15/// when the store is dropped, and zeroized then. Meant for tests, examples and development, not
16/// for production keys.
17///
18/// [`InMemoryKeyStore::with_seed`] makes key generation deterministic, so that a fixture's keys,
19/// key ids and (for EdDSA and the RFC 6979 ECDSA signatures) signatures are stable across runs.
20/// A seeded store must never hold production keys: anyone with the seed has them.
21///
22/// The key id of a generated key is the spec's label when set, the RFC 7638 thumbprint of its
23/// public key otherwise.
24///
25/// # Examples
26///
27/// ```
28/// # tokio::runtime::Builder::new_current_thread().build()?.block_on(async {
29/// use identity_kit::jose::{JwsAlgorithm, KeyType};
30/// use identity_kit::keys::{InMemoryKeyStore, KeySpec, KeyStore};
31///
32/// let store = InMemoryKeyStore::with_seed([7; 32]);
33/// let key = store.generate(KeySpec::new(KeyType::Ed25519, JwsAlgorithm::EdDSA)).await?;
34/// let signature = store.sign(&key, JwsAlgorithm::EdDSA, b"payload").await?;
35/// assert_eq!(signature.len(), 64);
36///
37/// // The same seed gives the same key.
38/// let again = InMemoryKeyStore::with_seed([7; 32]);
39/// assert_eq!(again.generate(KeySpec::new(KeyType::Ed25519, JwsAlgorithm::EdDSA)).await?, key);
40/// # Ok::<(), Box<dyn std::error::Error>>(())
41/// # })?;
42/// # Ok::<(), Box<dyn std::error::Error>>(())
43/// ```
44pub struct InMemoryKeyStore {
45    keys: RwLock<IndexMap<KeyId, Entry>>,
46    seeded: Option<Mutex<SeededRandom>>,
47}
48
49impl Default for InMemoryKeyStore {
50    fn default() -> Self {
51        Self::new()
52    }
53}
54
55impl InMemoryKeyStore {
56    /// An empty store generating keys from the operating system's randomness.
57    pub fn new() -> Self {
58        Self {
59            keys: RwLock::new(IndexMap::new()),
60            seeded: None,
61        }
62    }
63
64    /// An empty store generating keys deterministically from `seed`.
65    ///
66    /// The n-th key generated from a seed is always the same key. For fixtures only.
67    pub fn with_seed(seed: [u8; 32]) -> Self {
68        Self {
69            keys: RwLock::new(IndexMap::new()),
70            seeded: Some(Mutex::new(SeededRandom::new(seed))),
71        }
72    }
73
74    /// The ids of the keys in the store, in generation order.
75    pub fn key_ids(&self) -> Vec<KeyId> {
76        self.keys
77            .read()
78            .unwrap_or_else(PoisonError::into_inner)
79            .keys()
80            .cloned()
81            .collect()
82    }
83
84    /// Whether key generation is seeded.
85    pub fn is_seeded(&self) -> bool {
86        self.seeded.is_some()
87    }
88
89    fn random(&self, buf: &mut [u8]) -> Result<(), KeyStoreError> {
90        match &self.seeded {
91            Some(seeded) => {
92                seeded
93                    .lock()
94                    .unwrap_or_else(PoisonError::into_inner)
95                    .fill(buf);
96                Ok(())
97            }
98            None => local::os_random(buf),
99        }
100    }
101}
102
103impl fmt::Debug for InMemoryKeyStore {
104    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105        f.debug_struct("InMemoryKeyStore")
106            .field("keys", &self.key_ids())
107            .field("seeded", &self.is_seeded())
108            .finish()
109    }
110}
111
112#[async_trait::async_trait]
113impl KeyStore for InMemoryKeyStore {
114    fn capabilities(&self) -> KeyStoreCapabilities {
115        KeyStoreCapabilities::new(local::ALGORITHMS.iter().copied())
116            .with_generate()
117            .with_delete()
118    }
119
120    async fn public_jwk(&self, key: &KeyId) -> Result<Jwk, KeyStoreError> {
121        self.keys
122            .read()
123            .unwrap_or_else(PoisonError::into_inner)
124            .get(key)
125            .map(Entry::public_jwk)
126            .ok_or_else(|| KeyStoreError::NotFound(key.clone()))
127    }
128
129    async fn sign(
130        &self,
131        key: &KeyId,
132        alg: JwsAlgorithm,
133        data: &[u8],
134    ) -> Result<Vec<u8>, KeyStoreError> {
135        self.keys
136            .read()
137            .unwrap_or_else(PoisonError::into_inner)
138            .get(key)
139            .ok_or_else(|| KeyStoreError::NotFound(key.clone()))?
140            .sign(key, alg, data)
141    }
142
143    async fn exists(&self, key: &KeyId) -> Result<bool, KeyStoreError> {
144        Ok(self
145            .keys
146            .read()
147            .unwrap_or_else(PoisonError::into_inner)
148            .contains_key(key))
149    }
150
151    async fn generate(&self, spec: KeySpec) -> Result<KeyId, KeyStoreError> {
152        let (id, key) = local::generate(&spec, &mut |buf| self.random(buf))?;
153        let mut keys = self.keys.write().unwrap_or_else(PoisonError::into_inner);
154        if keys.contains_key(&id) {
155            return Err(KeyStoreError::AlreadyExists(id));
156        }
157        keys.insert(id.clone(), Entry::new(&id, key, spec.alg));
158        Ok(id)
159    }
160
161    async fn delete(&self, key: &KeyId) -> Result<(), KeyStoreError> {
162        self.keys
163            .write()
164            .unwrap_or_else(PoisonError::into_inner)
165            .shift_remove(key)
166            .map(drop)
167            .ok_or_else(|| KeyStoreError::NotFound(key.clone()))
168    }
169}