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>);