identity_kit/keys/signer.rs
1//! `KeySigner`: a key of a `KeyStore` as a `jose::Signer`.
2
3use core::fmt;
4
5use super::{KeyId, KeyStore, KeyStoreError};
6use crate::jose::{Jwk, JwsAlgorithm, SignError, Signer};
7
8/// A [`Signer`] over one key of a [`KeyStore`].
9///
10/// The signer looks the key up once, when it is created, and keeps the public [`Jwk`] so that a
11/// caller building a DID document or a JWS header never round-trips to the store for it
12/// ([`KeySigner::public_jwk`]). Signing goes to the store on every call.
13///
14/// [`Signer::kid`] is the key id, unless [`KeySigner::with_kid`] set another value, such as the
15/// DID URL of the verification method the key backs.
16///
17/// # Examples
18///
19/// ```
20/// # #[cfg(feature = "in-memory")]
21/// # tokio::runtime::Builder::new_current_thread().build()?.block_on(async {
22/// use identity_kit::jose::{CompactJws, DefaultVerifier, JwsAlgorithm, JwsHeader, KeyType, encode_compact};
23/// use identity_kit::keys::{InMemoryKeyStore, KeySigner, KeySpec, KeyStore};
24///
25/// let store = InMemoryKeyStore::new();
26/// let key = store.generate(KeySpec::new(KeyType::Ed25519, JwsAlgorithm::EdDSA)).await?;
27/// let signer = KeySigner::new(&store, key, JwsAlgorithm::EdDSA).await?;
28///
29/// let header = JwsHeader::new(JwsAlgorithm::EdDSA).with_kid(signer.key_id().as_str());
30/// let token = encode_compact(&header, b"{}", &signer).await?;
31/// let jws = CompactJws::decode(&token)?;
32/// jws.verify(&DefaultVerifier, signer.public_jwk())?;
33/// # Ok::<(), Box<dyn std::error::Error>>(())
34/// # })?;
35/// # Ok::<(), Box<dyn std::error::Error>>(())
36/// ```
37pub struct KeySigner<'a> {
38 store: &'a dyn KeyStore,
39 key: KeyId,
40 alg: JwsAlgorithm,
41 public: Jwk,
42 kid: Option<String>,
43}
44
45impl<'a> KeySigner<'a> {
46 /// A signer over `key` of `store`, signing with `alg`.
47 ///
48 /// # Errors
49 ///
50 /// [`KeyStoreError::UnsupportedAlgorithm`] when the store does not advertise `alg`,
51 /// [`KeyStoreError::NotFound`] when the key does not exist,
52 /// [`KeyStoreError::AlgorithmMismatch`] when the key's public JWK names another algorithm,
53 /// and whatever else the store reports.
54 pub async fn new(
55 store: &'a dyn KeyStore,
56 key: KeyId,
57 alg: JwsAlgorithm,
58 ) -> Result<Self, KeyStoreError> {
59 if !store.capabilities().supports(alg) {
60 return Err(KeyStoreError::UnsupportedAlgorithm(alg));
61 }
62 let public = store.public_jwk(&key).await?;
63 if let Some(key_alg) = public.alg() {
64 let key_alg: JwsAlgorithm = key_alg.parse().map_err(|_| KeyStoreError::InvalidKey {
65 key: key.clone(),
66 reason: format!("unknown algorithm `{key_alg}` in its public JWK"),
67 })?;
68 if key_alg != alg {
69 return Err(KeyStoreError::AlgorithmMismatch {
70 key,
71 key_alg,
72 requested: alg,
73 });
74 }
75 }
76 Ok(Self {
77 store,
78 key,
79 alg,
80 public,
81 kid: None,
82 })
83 }
84
85 /// This signer with [`Signer::kid`] set to `kid` instead of the key id.
86 #[must_use]
87 pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
88 self.kid = Some(kid.into());
89 self
90 }
91
92 /// The store the key lives in.
93 pub fn store(&self) -> &'a dyn KeyStore {
94 self.store
95 }
96
97 /// The key this signer signs with.
98 pub fn key_id(&self) -> &KeyId {
99 &self.key
100 }
101
102 /// The public key, as the store reported it when the signer was created.
103 pub fn public_jwk(&self) -> &Jwk {
104 &self.public
105 }
106}
107
108impl fmt::Debug for KeySigner<'_> {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 f.debug_struct("KeySigner")
111 .field("key", &self.key)
112 .field("alg", &self.alg)
113 .field("public", &self.public)
114 .field("kid", &self.kid)
115 .finish_non_exhaustive()
116 }
117}
118
119#[async_trait::async_trait]
120impl Signer for KeySigner<'_> {
121 fn alg(&self) -> JwsAlgorithm {
122 self.alg
123 }
124
125 fn kid(&self) -> Option<&str> {
126 Some(self.kid.as_deref().unwrap_or(self.key.as_str()))
127 }
128
129 async fn sign(&self, signing_input: &[u8]) -> Result<Vec<u8>, SignError> {
130 self.store
131 .sign(&self.key, self.alg, signing_input)
132 .await
133 .map_err(SignError::new)
134 }
135}