Skip to main content

identity_kit/keys/
mod.rs

1//! Key stores: private keys behind an opaque id, with signing as a remote operation.
2//!
3//! The module is shaped by remote signers, an HSM or a cloud KMS being the design target:
4//!
5//! - A key is named by an opaque [`KeyId`] (a label, a PKCS#11 object name, a cloud key ARN).
6//! - Private material never leaves the backend. [`KeyStore::sign`] is `async`, and callers keep
7//!   the public [`Jwk`] they get from [`KeyStore::public_jwk`] rather than asking for it on every
8//!   use; [`KeySigner`] does exactly that and implements [`Signer`](crate::jose::Signer).
9//! - Every key has one algorithm. A backend declares what it signs with, and whether it can
10//!   generate and delete keys, in [`KeyStoreCapabilities`]; the declaration must be truthful and
11//!   [`testsuite`] (feature `test-vectors`) checks it.
12//! - Signatures are returned in their JWS form: 64 bytes for EdDSA, raw `r || s` for ECDSA.
13//!
14//! Three backends ship with the crate. [`InMemoryKeyStore`] (feature `in-memory`) and
15//! [`SoftwareKeyStore`] (feature `software`), a private JWK set file, are for development and
16//! tests: both sign EdDSA, ES256 and, with the `secp256k1` feature, ES256K, and neither signs
17//! with RSA, which stays outside the software backends per
18//! `docs/adr/0001-rsa-verification-only.md`. [`Pkcs11KeyStore`] (feature `pkcs11`) signs in a
19//! PKCS#11 module, an HSM or a software token, with whatever the module offers, RSA included.
20//! Vendor-specific backends live in their own crates and implement [`KeyStore`];
21//! `docs/key-stores.md` is the contract they follow.
22//!
23//! # Cargo features
24//!
25//! - `in-memory`: [`InMemoryKeyStore`].
26//! - `software` (default): [`SoftwareKeyStore`].
27//! - `pkcs11`: [`Pkcs11KeyStore`].
28//! - `test-vectors`: [`testsuite`].
29
30use core::fmt;
31use std::sync::Arc;
32
33use serde::{Deserialize, Serialize};
34
35use crate::jose::{Jwk, JwsAlgorithm};
36
37mod error;
38#[cfg(feature = "in-memory")]
39mod in_memory;
40#[cfg(any(feature = "in-memory", feature = "software"))]
41mod local;
42#[cfg(feature = "pkcs11")]
43mod pkcs11;
44mod signer;
45#[cfg(feature = "software")]
46mod software;
47#[cfg(feature = "test-vectors")]
48#[cfg_attr(docsrs, doc(cfg(feature = "test-vectors")))]
49pub mod testsuite;
50
51pub use crate::jose::KeyType;
52pub use error::KeyStoreError;
53#[cfg(feature = "in-memory")]
54#[cfg_attr(docsrs, doc(cfg(feature = "in-memory")))]
55pub use in_memory::InMemoryKeyStore;
56#[cfg(feature = "pkcs11")]
57#[cfg_attr(docsrs, doc(cfg(feature = "pkcs11")))]
58pub use pkcs11::{
59    Pkcs11Error, Pkcs11KeyStore, Pkcs11KeyStoreOptions, Pkcs11Token, STAGING_LABEL_PREFIX,
60};
61pub use signer::KeySigner;
62#[cfg(feature = "software")]
63#[cfg_attr(docsrs, doc(cfg(feature = "software")))]
64pub use software::{SoftwareKeyStore, SoftwareKeyStoreOptions};
65
66/// The opaque id of a key in a [`KeyStore`].
67///
68/// What the string means is up to the backend: a label, a PKCS#11 object name, a cloud key ARN.
69/// The crate never parses it. It serializes as a plain string.
70///
71/// # Examples
72///
73/// ```
74/// use identity_kit::keys::KeyId;
75///
76/// let id = KeyId::new("signing-2026");
77/// assert_eq!(id.as_str(), "signing-2026");
78/// assert_eq!(id.to_string(), "signing-2026");
79/// assert_eq!(serde_json::to_string(&id)?, "\"signing-2026\"");
80/// # Ok::<(), serde_json::Error>(())
81/// ```
82#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
83#[serde(transparent)]
84pub struct KeyId(String);
85
86impl KeyId {
87    /// A key id from any string.
88    pub fn new(id: impl Into<String>) -> Self {
89        Self(id.into())
90    }
91
92    /// The id as a string.
93    pub fn as_str(&self) -> &str {
94        &self.0
95    }
96}
97
98impl From<&str> for KeyId {
99    fn from(id: &str) -> Self {
100        Self::new(id)
101    }
102}
103
104impl From<String> for KeyId {
105    fn from(id: String) -> Self {
106        Self(id)
107    }
108}
109
110impl From<KeyId> for String {
111    fn from(id: KeyId) -> Self {
112        id.0
113    }
114}
115
116impl AsRef<str> for KeyId {
117    fn as_ref(&self) -> &str {
118        &self.0
119    }
120}
121
122impl fmt::Debug for KeyId {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        fmt::Debug::fmt(&self.0, f)
125    }
126}
127
128impl fmt::Display for KeyId {
129    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130        f.write_str(&self.0)
131    }
132}
133
134/// What to generate: a key type, the one algorithm the key will sign with, and an optional
135/// label.
136///
137/// The algorithm must fit the key type ([`KeySpec::validate`]); backends reject a spec that
138/// does not. How a backend uses the label is its own business; the software backends use it as
139/// the key id when set and the RFC 7638 thumbprint of the public key otherwise.
140///
141/// # Examples
142///
143/// ```
144/// use identity_kit::jose::{JwsAlgorithm, KeyType};
145/// use identity_kit::keys::{KeySpec, KeyStoreError};
146///
147/// let spec = KeySpec::new(KeyType::P256, JwsAlgorithm::ES256).with_label("issuer");
148/// assert!(spec.validate().is_ok());
149///
150/// let wrong = KeySpec::new(KeyType::P256, JwsAlgorithm::EdDSA);
151/// assert!(matches!(wrong.validate(), Err(KeyStoreError::InvalidSpec { .. })));
152/// ```
153#[derive(Debug, Clone, PartialEq, Eq)]
154#[non_exhaustive]
155pub struct KeySpec {
156    /// The kind of key.
157    pub key_type: KeyType,
158    /// The algorithm the key signs with.
159    pub alg: JwsAlgorithm,
160    /// A label for the backend: the key id for the software backends, a PKCS#11 label, a
161    /// display name.
162    pub label: Option<String>,
163}
164
165impl KeySpec {
166    /// A spec without a label.
167    pub fn new(key_type: KeyType, alg: JwsAlgorithm) -> Self {
168        Self {
169            key_type,
170            alg,
171            label: None,
172        }
173    }
174
175    /// This spec with a label.
176    #[must_use]
177    pub fn with_label(mut self, label: impl Into<String>) -> Self {
178        self.label = Some(label.into());
179        self
180    }
181
182    /// Checks that the algorithm fits the key type.
183    ///
184    /// # Errors
185    ///
186    /// [`KeyStoreError::InvalidSpec`] when it does not: `EdDSA` needs an Ed25519 key, `ES256` a
187    /// P-256 key, `ES256K` a secp256k1 key and `RS256` or `PS256` an RSA key. `ES384` fits no
188    /// [`KeyType`] this crate has.
189    pub fn validate(&self) -> Result<(), KeyStoreError> {
190        if algorithm_fits(self.key_type, self.alg) {
191            Ok(())
192        } else {
193            Err(KeyStoreError::InvalidSpec {
194                key_type: self.key_type,
195                alg: self.alg,
196            })
197        }
198    }
199}
200
201/// Whether `alg` is an algorithm for keys of type `key_type`.
202fn algorithm_fits(key_type: KeyType, alg: JwsAlgorithm) -> bool {
203    match key_type {
204        KeyType::Ed25519 => alg == JwsAlgorithm::EdDSA,
205        KeyType::P256 => alg == JwsAlgorithm::ES256,
206        KeyType::Secp256k1 => alg == JwsAlgorithm::ES256K,
207        KeyType::Rsa2048 | KeyType::Rsa4096 => {
208            matches!(alg, JwsAlgorithm::RS256 | JwsAlgorithm::PS256)
209        }
210    }
211}
212
213/// What a [`KeyStore`] can do.
214///
215/// A backend's declaration must be truthful: every algorithm listed signs, and `generate` and
216/// `delete` succeed only when advertised. [`testsuite`] checks both directions.
217///
218/// # Examples
219///
220/// ```
221/// use identity_kit::jose::JwsAlgorithm;
222/// use identity_kit::keys::KeyStoreCapabilities;
223///
224/// let hsm = KeyStoreCapabilities::new([JwsAlgorithm::ES256, JwsAlgorithm::RS256]).remote();
225/// assert!(hsm.supports(JwsAlgorithm::ES256));
226/// assert!(!hsm.supports(JwsAlgorithm::EdDSA));
227/// assert!(!hsm.can_generate && !hsm.can_delete && hsm.remote);
228/// ```
229#[derive(Debug, Clone, PartialEq, Eq)]
230#[non_exhaustive]
231pub struct KeyStoreCapabilities {
232    /// Whether [`KeyStore::generate`] is implemented.
233    pub can_generate: bool,
234    /// Whether [`KeyStore::delete`] is implemented.
235    pub can_delete: bool,
236    /// Whether signing leaves the process (an HSM, a network service), so that callers should
237    /// expect latency and transient failures.
238    pub remote: bool,
239    /// The algorithms the store signs with, without duplicates.
240    pub algorithms: Vec<JwsAlgorithm>,
241}
242
243impl KeyStoreCapabilities {
244    /// A local store that signs with `algorithms` and can neither generate nor delete.
245    pub fn new(algorithms: impl IntoIterator<Item = JwsAlgorithm>) -> Self {
246        let mut deduplicated: Vec<JwsAlgorithm> = Vec::new();
247        for alg in algorithms {
248            if !deduplicated.contains(&alg) {
249                deduplicated.push(alg);
250            }
251        }
252        Self {
253            can_generate: false,
254            can_delete: false,
255            remote: false,
256            algorithms: deduplicated,
257        }
258    }
259
260    /// With [`can_generate`](Self::can_generate) set.
261    #[must_use]
262    pub fn with_generate(mut self) -> Self {
263        self.can_generate = true;
264        self
265    }
266
267    /// With [`can_delete`](Self::can_delete) set.
268    #[must_use]
269    pub fn with_delete(mut self) -> Self {
270        self.can_delete = true;
271        self
272    }
273
274    /// With [`remote`](Self::remote) set.
275    #[must_use]
276    pub fn remote(mut self) -> Self {
277        self.remote = true;
278        self
279    }
280
281    /// Whether the store signs with `alg`.
282    pub fn supports(&self, alg: JwsAlgorithm) -> bool {
283        self.algorithms.contains(&alg)
284    }
285}
286
287/// A backend holding private keys.
288///
289/// This is the crate's key extension point: a KMS, an HSM or a software store implements it,
290/// and everything that signs takes a `&dyn KeyStore` (usually through [`KeySigner`]). The
291/// contract, which [`testsuite`] checks, is:
292///
293/// - [`capabilities`](Self::capabilities) is truthful: every listed algorithm signs; `generate`
294///   and `delete` return [`KeyStoreError::Unsupported`] unless advertised.
295/// - [`public_jwk`](Self::public_jwk) returns a key without private parameters, with `alg` set
296///   to the key's algorithm when the backend knows it.
297/// - [`sign`](Self::sign) takes the raw signing input (the backend hashes) and returns the JWS
298///   form of the signature: 64 bytes for EdDSA, raw `r || s` for ECDSA, the modulus-sized octet
299///   string for RSA. A key signs only with its own algorithm; any other `alg` is
300///   [`KeyStoreError::AlgorithmMismatch`].
301/// - An unknown key is [`KeyStoreError::NotFound`] from every operation but
302///   [`exists`](Self::exists), which returns `false`.
303///
304/// `generate` and `delete` have default implementations returning
305/// [`KeyStoreError::Unsupported`], for backends whose keys are provisioned out of band.
306#[async_trait::async_trait]
307pub trait KeyStore: Send + Sync {
308    /// What this store can do.
309    fn capabilities(&self) -> KeyStoreCapabilities;
310
311    /// The public key of `key`, without private parameters.
312    ///
313    /// # Errors
314    ///
315    /// [`KeyStoreError::NotFound`] when there is no such key.
316    async fn public_jwk(&self, key: &KeyId) -> Result<Jwk, KeyStoreError>;
317
318    /// Signs `data` with `key` under `alg`, returning the signature in its JWS form.
319    ///
320    /// # Errors
321    ///
322    /// [`KeyStoreError::NotFound`] when there is no such key and
323    /// [`KeyStoreError::AlgorithmMismatch`] when `alg` is not the key's algorithm.
324    async fn sign(
325        &self,
326        key: &KeyId,
327        alg: JwsAlgorithm,
328        data: &[u8],
329    ) -> Result<Vec<u8>, KeyStoreError>;
330
331    /// Whether `key` exists.
332    ///
333    /// # Errors
334    ///
335    /// Only when the backend cannot answer.
336    async fn exists(&self, key: &KeyId) -> Result<bool, KeyStoreError>;
337
338    /// Generates a key as `spec` describes and returns its id.
339    ///
340    /// # Errors
341    ///
342    /// [`KeyStoreError::Unsupported`] when the store does not generate keys (the default),
343    /// [`KeyStoreError::InvalidSpec`] when the algorithm does not fit the key type,
344    /// [`KeyStoreError::UnsupportedKeyType`] or [`KeyStoreError::UnsupportedAlgorithm`] when the
345    /// store cannot make such a key, and [`KeyStoreError::AlreadyExists`] when the label names
346    /// an existing key.
347    async fn generate(&self, spec: KeySpec) -> Result<KeyId, KeyStoreError> {
348        let _ = spec;
349        Err(KeyStoreError::Unsupported("generate"))
350    }
351
352    /// Deletes `key`.
353    ///
354    /// # Errors
355    ///
356    /// [`KeyStoreError::Unsupported`] when the store does not delete keys (the default) and
357    /// [`KeyStoreError::NotFound`] when there is no such key.
358    async fn delete(&self, key: &KeyId) -> Result<(), KeyStoreError> {
359        let _ = key;
360        Err(KeyStoreError::Unsupported("delete"))
361    }
362}
363
364macro_rules! forward_key_store {
365    ($($ty:ty),* $(,)?) => {
366        $(
367            #[async_trait::async_trait]
368            impl<T: KeyStore + ?Sized> KeyStore for $ty {
369                fn capabilities(&self) -> KeyStoreCapabilities {
370                    (**self).capabilities()
371                }
372
373                async fn public_jwk(&self, key: &KeyId) -> Result<Jwk, KeyStoreError> {
374                    (**self).public_jwk(key).await
375                }
376
377                async fn sign(
378                    &self,
379                    key: &KeyId,
380                    alg: JwsAlgorithm,
381                    data: &[u8],
382                ) -> Result<Vec<u8>, KeyStoreError> {
383                    (**self).sign(key, alg, data).await
384                }
385
386                async fn exists(&self, key: &KeyId) -> Result<bool, KeyStoreError> {
387                    (**self).exists(key).await
388                }
389
390                async fn generate(&self, spec: KeySpec) -> Result<KeyId, KeyStoreError> {
391                    (**self).generate(spec).await
392                }
393
394                async fn delete(&self, key: &KeyId) -> Result<(), KeyStoreError> {
395                    (**self).delete(key).await
396                }
397            }
398        )*
399    };
400}
401
402forward_key_store!(&T, Box<T>, Arc<T>);