identity_kit/identity/mod.rs
1//! The signing party: which identifiers it controls and the keys that sign for them.
2//!
3//! Every protocol that signs on behalf of a party needs the same three things: an identifier for
4//! `iss` or `sub`, a `kid` for the header and a [`Signer`]. Which identifier depends on what the
5//! other side accepts, a DID of some method or the RFC 7638 thumbprint of a bare key, and on the
6//! algorithm both sides agreed on. The protocol negotiates that pair (SIOPv2 matches
7//! `subject_syntax_types_supported` against what the wallet offers, OID4VCI the
8//! `proof_signing_alg_values_supported`), using [`Identity::capabilities`] for the identity's
9//! side; the identity then answers for the chosen pair:
10//!
11//! - [`Identity::identifier`]: the `iss`/`sub` value, a [`Did`] or a thumbprint;
12//! - [`Identity::public_jwk`]: the public key, for `cnf.jwk`, `sub_jwk` or a `jwk` header;
13//! - [`Identity::signer`]: a [`Signer`] whose [`kid`](Signer::kid) is the absolute DID URL of the
14//! verification method for a DID, and `None` for a thumbprint, where the caller puts the
15//! public key in the header instead.
16//!
17//! All three name the same key for the same pair. A party may hold a key per algorithm for one
18//! kind, so the identifier takes the algorithm too: a `did:key`, a `did:jwk` or a thumbprint is
19//! derived from the key and differs between them.
20//!
21//! [`KeyStoreIdentity`] is the implementation the crate ships, over any [`KeyStore`] and the
22//! [`DidProducer`]s registered with it. There is no hook for an external signer: a remote or
23//! hardware signer is a [`KeyStore`] (ADR 0006).
24//!
25//! # Examples
26//!
27//! ```
28//! # #[cfg(all(feature = "in-memory", feature = "did-key"))]
29//! # tokio::runtime::Builder::new_current_thread().build()?.block_on(async {
30//! use std::sync::Arc;
31//!
32//! use identity_kit::did::{DidMethod, VerificationRelationship};
33//! use identity_kit::identity::{Identity, IdentityKind, KeyStoreIdentity};
34//! use identity_kit::jose::{CompactJws, DefaultVerifier, JwsAlgorithm, JwsHeader, KeyType, encode_compact};
35//! use identity_kit::keys::{InMemoryKeyStore, KeySpec, KeyStore};
36//! use identity_kit::methods::DidKey;
37//! use identity_kit::resolver::Resolver;
38//!
39//! let store = Arc::new(InMemoryKeyStore::new());
40//! let key = store.generate(KeySpec::new(KeyType::Ed25519, JwsAlgorithm::EdDSA)).await?;
41//! let identity = KeyStoreIdentity::new(store).with_producer(DidKey::default());
42//!
43//! // The protocol settled on did:key and EdDSA.
44//! let kind = IdentityKind::Did(DidMethod::new("key")?);
45//! let did = identity.bind(kind.clone(), JwsAlgorithm::EdDSA, key).await?;
46//! assert!(did.to_string().starts_with("did:key:z6Mk"));
47//!
48//! let signer = identity.signer(&kind, JwsAlgorithm::EdDSA).await?;
49//! let kid = signer.kid().expect("a DID signer names its verification method");
50//! let header = JwsHeader::new(JwsAlgorithm::EdDSA).with_kid(kid);
51//! let token = encode_compact(&header, br#"{"iss":"me"}"#, &*signer).await?;
52//!
53//! // A verifier finds the key through the DID in `kid`.
54//! let resolver = Resolver::builder().with(DidKey::default()).build();
55//! let url = kid.parse()?;
56//! let key = resolver
57//! .resolve_public_key(&url, Some(VerificationRelationship::AssertionMethod))
58//! .await?;
59//! CompactJws::decode(&token)?.verify(&DefaultVerifier, &key)?;
60//! # Ok::<(), Box<dyn std::error::Error>>(())
61//! # })?;
62//! # Ok::<(), Box<dyn std::error::Error>>(())
63//! ```
64//!
65//! [`KeyStore`]: crate::keys::KeyStore
66//! [`DidProducer`]: crate::resolver::DidProducer
67
68use core::fmt;
69use std::sync::Arc;
70
71use crate::did::{Did, DidMethod};
72use crate::jose::{Jwk, JwkError, JwsAlgorithm, Signer};
73use crate::keys::KeyStoreError;
74use crate::resolver::ProduceError;
75
76mod key_store;
77
78pub use key_store::KeyStoreIdentity;
79
80/// The kind of identifier the other side accepts.
81///
82/// # Examples
83///
84/// ```
85/// use identity_kit::did::DidMethod;
86/// use identity_kit::identity::IdentityKind;
87///
88/// let kind = IdentityKind::Did(DidMethod::new("key")?);
89/// assert_eq!(kind.to_string(), "did:key");
90/// assert_eq!(IdentityKind::JwkThumbprint.to_string(), "JWK thumbprint");
91/// # Ok::<(), identity_kit::did::DidError>(())
92/// ```
93#[derive(Debug, Clone, PartialEq, Eq, Hash)]
94#[non_exhaustive]
95pub enum IdentityKind {
96 /// A DID of this method, and of its namespace when the [`DidMethod`] has one.
97 Did(DidMethod),
98 /// The RFC 7638 SHA-256 thumbprint of the public key, as SIOPv2 self-issued ID tokens and
99 /// key binding with `cnf.jwk` use it.
100 JwkThumbprint,
101}
102
103impl fmt::Display for IdentityKind {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 match self {
106 Self::Did(method) => fmt::Display::fmt(method, f),
107 Self::JwkThumbprint => f.write_str("JWK thumbprint"),
108 }
109 }
110}
111
112/// The identifier of an [`Identity`] for one kind and algorithm; [`Display`](fmt::Display) gives
113/// the `iss` or `sub` value.
114///
115/// # Examples
116///
117/// ```
118/// use identity_kit::did::Did;
119/// use identity_kit::identity::Identifier;
120///
121/// let did = Identifier::Did(Did::parse("did:example:123")?);
122/// assert_eq!(did.to_string(), "did:example:123");
123/// assert!(did.as_did().is_some());
124///
125/// // RFC 7638 ยง3.1.
126/// let thumbprint = Identifier::JwkThumbprint("NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs".into());
127/// assert_eq!(thumbprint.to_string(), "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs");
128/// assert!(thumbprint.as_did().is_none());
129/// # Ok::<(), identity_kit::did::DidError>(())
130/// ```
131#[derive(Debug, Clone, PartialEq, Eq, Hash)]
132#[non_exhaustive]
133pub enum Identifier {
134 /// A DID.
135 Did(Did),
136 /// The RFC 7638 SHA-256 thumbprint of the public key, base64url without padding.
137 JwkThumbprint(String),
138}
139
140impl Identifier {
141 /// The DID, if this is one.
142 pub fn as_did(&self) -> Option<&Did> {
143 match self {
144 Self::Did(did) => Some(did),
145 Self::JwkThumbprint(_) => None,
146 }
147 }
148}
149
150impl fmt::Display for Identifier {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 Self::Did(did) => fmt::Display::fmt(did, f),
154 Self::JwkThumbprint(thumbprint) => f.write_str(thumbprint),
155 }
156 }
157}
158
159/// Why an [`Identity`] cannot answer for a kind and algorithm.
160#[derive(Debug, thiserror::Error)]
161#[non_exhaustive]
162pub enum IdentityError {
163 /// The identity has no identifier of this kind: no key is bound to it, or nothing can
164 /// produce a DID of the method (and namespace).
165 #[error("no identifier of kind {0}")]
166 UnsupportedKind(IdentityKind),
167 /// The identity has an identifier of the kind, but no key for this algorithm, or the key
168 /// store cannot sign with it.
169 #[error("no key for algorithm {0}")]
170 UnsupportedAlgorithm(JwsAlgorithm),
171 /// The document a producer made for the key has no verification method to name in `kid`.
172 #[error("the document of {0} has no verification method")]
173 NoVerificationMethod(Did),
174 /// The key store failed, or refused the key for the algorithm.
175 #[error(transparent)]
176 KeyStore(#[from] KeyStoreError),
177 /// The DID producer failed.
178 #[error(transparent)]
179 Produce(#[from] ProduceError),
180 /// The thumbprint of the public key could not be computed.
181 #[error(transparent)]
182 Jwk(#[from] JwkError),
183}
184
185/// A party that signs: the identifiers it controls and the keys behind them.
186///
187/// See the [module documentation](self) for how a protocol uses it. Every method answers for a
188/// `(kind, alg)` pair; for one pair, [`identifier`](Identity::identifier),
189/// [`public_jwk`](Identity::public_jwk) and [`signer`](Identity::signer) name the same key.
190#[async_trait::async_trait]
191pub trait Identity: Send + Sync {
192 /// The `(kind, alg)` pairs this identity can sign for, for negotiation.
193 fn capabilities(&self) -> Vec<(IdentityKind, JwsAlgorithm)>;
194
195 /// The identifier for `(kind, alg)`. A thumbprint, a `did:key` or a `did:jwk` is derived from
196 /// the key bound to the pair, so it names the same key as
197 /// [`public_jwk`](Identity::public_jwk) and [`signer`](Identity::signer) only for the same
198 /// pair.
199 ///
200 /// # Errors
201 ///
202 /// [`IdentityError::UnsupportedKind`] or [`IdentityError::UnsupportedAlgorithm`] when the
203 /// identity cannot answer for the pair, and whatever producing the identifier reports.
204 async fn identifier(
205 &self,
206 kind: &IdentityKind,
207 alg: JwsAlgorithm,
208 ) -> Result<Identifier, IdentityError>;
209
210 /// The public key that [`signer(kind, alg)`](Identity::signer) signs with, for `cnf.jwk`,
211 /// `sub_jwk` or a `jwk` header.
212 ///
213 /// # Errors
214 ///
215 /// As [`identifier`](Identity::identifier).
216 async fn public_jwk(
217 &self,
218 kind: &IdentityKind,
219 alg: JwsAlgorithm,
220 ) -> Result<Jwk, IdentityError>;
221
222 /// A signer for `(kind, alg)`. For [`IdentityKind::Did`], [`Signer::kid`] is the absolute
223 /// DID URL of the verification method; for [`IdentityKind::JwkThumbprint`] it is `None` and
224 /// the caller puts [`public_jwk`](Identity::public_jwk) in the header.
225 ///
226 /// # Errors
227 ///
228 /// As [`identifier`](Identity::identifier).
229 async fn signer(
230 &self,
231 kind: &IdentityKind,
232 alg: JwsAlgorithm,
233 ) -> Result<Box<dyn Signer + '_>, IdentityError>;
234}
235
236macro_rules! forward_identity {
237 ($($ty:ty),* $(,)?) => {
238 $(
239 #[async_trait::async_trait]
240 impl<T: Identity + ?Sized> Identity for $ty {
241 fn capabilities(&self) -> Vec<(IdentityKind, JwsAlgorithm)> {
242 (**self).capabilities()
243 }
244
245 async fn identifier(
246 &self,
247 kind: &IdentityKind,
248 alg: JwsAlgorithm,
249 ) -> Result<Identifier, IdentityError> {
250 (**self).identifier(kind, alg).await
251 }
252
253 async fn public_jwk(
254 &self,
255 kind: &IdentityKind,
256 alg: JwsAlgorithm,
257 ) -> Result<Jwk, IdentityError> {
258 (**self).public_jwk(kind, alg).await
259 }
260
261 async fn signer(
262 &self,
263 kind: &IdentityKind,
264 alg: JwsAlgorithm,
265 ) -> Result<Box<dyn Signer + '_>, IdentityError> {
266 (**self).signer(kind, alg).await
267 }
268 }
269 )*
270 };
271}
272
273forward_identity!(&T, Box<T>, Arc<T>);