Skip to main content

identity_kit/sd_jwt_vc/
mod.rs

1//! Selective disclosure for JWTs (SD-JWT, RFC 9901) and SD-JWT-based verifiable credentials
2//! (SD-JWT VC), with the Token Status List and SD-JWT VC type metadata.
3//!
4//! - [`SdJwt`] is the data format: an issuer-signed JWT, its [`Disclosure`]s and an optional
5//!   key binding JWT, parsed from and serialized to the `~`-separated compact form.
6//!   [`SdJwt::process`] matches the disclosures to the digests (RFC 9901 §7.1) and yields the
7//!   processed payload; [`SdJwt::disclose`] and [`SdJwt::select`] make a presentation;
8//!   [`SdJwt::sign_key_binding`] turns it into an SD-JWT+KB.
9//! - [`SdJwtBuilder`] issues: it conceals the claims at the given [`ClaimPath`]s, nested ones
10//!   recursively, adds decoys and signs with any [`Signer`](crate::jose::Signer).
11//! - [`SdJwtVc`] is an SD-JWT VC: `typ: dc+sd-jwt`, a `vct`, the registered claims that may
12//!   not be selectively disclosed kept as such, [`SdJwtVcBuilder`] to issue one and
13//!   [`SdJwtCredentialValidator`] to validate one, key binding included.
14//! - [`SdJwtCredentialV2`] is a VCDM 2.0 credential secured with SD-JWT (VC-JOSE-COSE
15//!   §3.2.1): `typ: vc+sd-jwt`, the [`CredentialV2`](crate::vc::CredentialV2) itself as the
16//!   payload, [`SdJwtCredentialV2Builder`] to issue one and
17//!   [`SdJwtCredentialValidator::validate_v2`] to validate one.
18//! - [`TypeMetadata`] implements the SD-JWT VC type metadata with its claim paths, integrity
19//!   metadata and `extends` processing.
20//!
21//! The Token Status List referenced from the `status` claim is not specific to SD-JWT and
22//! lives in [`status_list`](crate::status_list), which this feature enables. Its types are
23//! still reachable here, deprecated, for one release.
24//!
25//! The SD-JWT layer is implemented here rather than taken from a dependency (ADR 0005).
26//!
27//! # Two SD-JWT credential types
28//!
29//! An SD-JWT VC ([`SdJwtVc`]) carries a flat claims set typed by `vct`, with `typ:
30//! dc+sd-jwt` (`vc+sd-jwt` in earlier drafts). A VCDM 2.0 credential secured with SD-JWT
31//! ([`SdJwtCredentialV2`]) carries the credential's own `@context`, `type`, `issuer` and
32//! `credentialSubject`, with `typ: vc+sd-jwt` and media type `application/vc+sd-jwt`. So
33//! `vc+sd-jwt` alone does not say which one a token is; the payload does: an SD-JWT VC has
34//! `vct`, a VCDM 2.0 credential has `@context` and no `vct`.
35//!
36//! A verifier that accepts both picks the parser by `typ` first and by the payload second.
37//! [`SdJwtVc::from_sd_jwt`] accepts `vc+sd-jwt` only when the payload has `vct`, and reports a
38//! `vc+sd-jwt` payload with `@context` and no `vct` as [`SdJwtVcError::VcdmPayload`], so that
39//! a caller which tried the SD-JWT VC parser first can fall through:
40//!
41//! ```
42//! use identity_kit::sd_jwt_vc::{SdJwt, SdJwtCredentialV2, SdJwtVc, SdJwtVcError};
43//!
44//! enum Parsed {
45//!     SdJwtVc(SdJwtVc),
46//!     Vcdm(SdJwtCredentialV2),
47//! }
48//!
49//! fn parse(sd_jwt: SdJwt) -> Result<Parsed, Box<dyn std::error::Error>> {
50//!     match SdJwtVc::from_sd_jwt(sd_jwt.clone()) {
51//!         Ok(credential) => Ok(Parsed::SdJwtVc(credential)),
52//!         Err(SdJwtVcError::VcdmPayload | SdJwtVcError::MissingTyp) => {
53//!             Ok(Parsed::Vcdm(SdJwtCredentialV2::from_sd_jwt(sd_jwt)?))
54//!         }
55//!         Err(error) => Err(error.into()),
56//!     }
57//! }
58//! # let _ = parse;
59//! ```
60
61mod builder;
62mod credential;
63mod credential_v2;
64mod disclosure;
65mod error;
66mod hash;
67mod sd_jwt;
68mod type_metadata;
69pub mod validation;
70
71pub use builder::{Encoded, SdJwtBuilder};
72pub use credential::{
73    Confirmation, LEGACY_TYP, MEDIA_TYPE, NON_DISCLOSABLE_CLAIMS, SdJwtVc, SdJwtVcBuilder,
74    SdJwtVcClaims, SdJwtVcError, TYP,
75};
76pub use credential_v2::{SdJwtCredentialV2, SdJwtCredentialV2Builder, SdJwtCredentialV2Error};
77pub use disclosure::Disclosure;
78pub use error::SdJwtError;
79pub use hash::HashAlgorithm;
80pub use sd_jwt::{DisclosedClaim, ELLIPSIS_KEY, KB_JWT_TYP, KeyBinding, Processed, SD_KEY, SdJwt};
81
82pub use crate::vc::{ClaimPath, ClaimPathError, PathComponent};
83pub use type_metadata::{
84    ClaimDisplay, ClaimMetadata, DisplayMetadata, Integrity, IntegrityAlgorithm, IntegrityError,
85    SdPolicy, TypeMetadata, TypeMetadataError,
86};
87pub use validation::{
88    KeyBindingOptions, SdJwtCredentialValidator, SdJwtValidationError, SdJwtValidationOptions,
89    ValidatedSdJwtCredentialV2, ValidatedSdJwtVc,
90};
91
92// `#[deprecated]` has no effect on a `pub use` (rust-lang/rust#30827), so the old paths are
93// deprecated type aliases. Remove them in the release after the next one.
94
95/// Moved to [`status_list::Status`](crate::status_list::Status).
96#[deprecated(since = "0.3.0", note = "moved to `status_list::Status`")]
97pub type Status = crate::status_list::Status;
98/// Moved to [`status_list::StatusList`](crate::status_list::StatusList).
99#[deprecated(since = "0.3.0", note = "moved to `status_list::StatusList`")]
100pub type StatusList = crate::status_list::StatusList;
101/// Moved to [`status_list::StatusListError`](crate::status_list::StatusListError).
102#[deprecated(since = "0.3.0", note = "moved to `status_list::StatusListError`")]
103pub type StatusListError = crate::status_list::StatusListError;
104/// Moved to [`status_list::StatusListRef`](crate::status_list::StatusListRef).
105#[deprecated(since = "0.3.0", note = "moved to `status_list::StatusListRef`")]
106pub type StatusListRef = crate::status_list::StatusListRef;
107/// Moved to [`status_list::StatusListToken`](crate::status_list::StatusListToken).
108#[deprecated(since = "0.3.0", note = "moved to `status_list::StatusListToken`")]
109pub type StatusListToken = crate::status_list::StatusListToken;
110/// Moved to [`status_list::StatusMechanism`](crate::status_list::StatusMechanism).
111#[deprecated(since = "0.3.0", note = "moved to `status_list::StatusMechanism`")]
112pub type StatusMechanism<'a> = crate::status_list::StatusMechanism<'a>;
113/// Moved to [`status_list::StatusType`](crate::status_list::StatusType).
114#[deprecated(since = "0.3.0", note = "moved to `status_list::StatusType`")]
115pub type StatusType = crate::status_list::StatusType;