Skip to main content

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