Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/qa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,9 @@ jobs:
save-if: ${{ github.ref == 'refs/heads/main' }}
- run: cargo install cargo-hack --locked
- run: cargo hack clippy --each-feature --locked -- -D warnings
# The Token Status List without SD-JWT: the `status` claim of a JWT credential reaches the
# `StatusCheck` with `vc` and `status-list` alone.
- run: cargo test --no-default-features --features vc,status-list,did-key,did-jwk --locked --test vc_validation

# The crate packages and builds from the package, with no git or path dependencies.
publish:
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

## Ground rules

- One crate, two layers. `did`, `jose`, `keys`, `identity`, `resolver` and `methods` never import `vc`, `sd_jwt_vc`, `domain_linkage` or `linked_vp`. CI enforces it; keep it green.
- One crate, two layers. `did`, `jose`, `keys`, `identity`, `resolver` and `methods` never import `vc`, `sd_jwt_vc`, `status_list`, `domain_linkage` or `linked_vp`. CI enforces it; keep it green.
- Extension points are traits (`DidResolver`, `DidProducer`, `KeyStore`). Never add a closed enum of methods or backends to the library.
- JWT/JWS and SD-JWT only. No JSON-LD processing.
- Tests are specification vectors (W3C, RFCs, DIF, method specs), never another library's output.
Expand Down
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]

[features]
default = ["secp256k1", "software", "did-key", "did-jwk", "did-web", "reqwest", "vc", "sd-jwt", "domain-linkage", "linked-vp"]
default = ["secp256k1", "software", "did-key", "did-jwk", "did-web", "reqwest", "vc", "status-list", "sd-jwt", "domain-linkage", "linked-vp"]
# ES256K signatures and `secp256k1` keys, via `k256`.
secp256k1 = ["dep:k256"]
# RS256 and PS256 signatures, via `rsa`; keys of at least 2048 bits (RFC 7518 §3.3, §3.5).
Expand Down Expand Up @@ -47,8 +47,11 @@ reqwest = ["did-web", "dep:reqwest", "dep:rustls", "dep:tokio"]
local-network = ["reqwest"]
# Verifiable credentials and presentations as JWTs (`vc`), via `chrono` for timestamps.
vc = ["dep:chrono"]
# SD-JWT (RFC 9901) and SD-JWT VC (`sd_jwt_vc`): `getrandom` for salts, `miniz_oxide` for status lists.
sd-jwt = ["vc", "dep:getrandom", "dep:miniz_oxide"]
# The IETF Token Status List (`status_list`) for JWT and SD-JWT credentials alike: `miniz_oxide`
# for the compressed list.
status-list = ["vc", "dep:miniz_oxide"]
# SD-JWT (RFC 9901) and SD-JWT VC (`sd_jwt_vc`), with the Token Status List: `getrandom` for salts.
sd-jwt = ["vc", "status-list", "dep:getrandom"]
# DIF Well Known DID Configuration (`domain_linkage`), over the `vc` module and `did:web`'s `HttpClient`.
domain-linkage = ["vc", "did-web"]
# DIF Linked Verifiable Presentation (`linked_vp`), over the `vc` module and `did:web`'s `HttpClient`.
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ Add it to a project with `cargo add identity-kit`. The API reference is on [docs
- ✅ `KeyReference` and `KeyResolver`: a token's key found from a DID URL `kid`, a `#fragment` relative to a DID `iss`, a `jwk` header, `cnf`, or the JWT VC issuer metadata of an HTTPS `iss`
- ✅ `did:key`, `did:jwk` and `did:web` methods, with a hardened HTTPS client for `did:web`
- ✅ Verifiable credentials and presentations as JWTs: the [VCDM 1.1](https://www.w3.org/TR/vc-data-model/) JWT profile, [VCDM 2.0](https://www.w3.org/TR/vc-data-model-2.0/) with [VC-JOSE-COSE](https://www.w3.org/TR/vc-jose-cose/) envelopes, and validators over any DID method
- ✅ SD-JWT (RFC 9901), SD-JWT VC and VCDM 2.0 credentials secured with SD-JWT (`vc+sd-jwt`): issuing with selective disclosure, presenting with key binding, validating over any DID method, the Token Status List and type metadata
- ✅ SD-JWT (RFC 9901), SD-JWT VC and VCDM 2.0 credentials secured with SD-JWT (`vc+sd-jwt`): issuing with selective disclosure, presenting with key binding, validating over any DID method, and type metadata
- ✅ The IETF Token Status List for JWT and SD-JWT credentials alike: issuing and signing status lists, and checking the `status` claim
- ✅ DIF Well Known DID Configuration: the DID configuration resource, its domain linkage credentials and their validation against an origin
- ✅ DIF Linked Verifiable Presentation: `LinkedVerifiablePresentation` services, presentations signed for publishing, and fetching and validating what a DID document links

Expand All @@ -49,7 +50,8 @@ All features are additive; the lower layer (`did`, `jose`, `keys`, `identity`, `
| `in-memory` | | `InMemoryKeyStore`: keys in memory, optionally derived from a seed |
| `pkcs11` | | `Pkcs11KeyStore`: keys in a PKCS#11 module (an HSM, SoftHSM2), via `cryptoki` and `tokio`'s blocking pool |
| `vc` | ✅ | Verifiable credentials and presentations as JWTs (`vc`); pulls in `chrono` |
| `sd-jwt` | ✅ | SD-JWT, SD-JWT VC and `vc+sd-jwt` credentials (`sd_jwt_vc`); implies `vc`, pulls in `miniz_oxide` |
| `status-list` | ✅ | The IETF Token Status List for JWT and SD-JWT credentials (`status_list`); implies `vc`, pulls in `miniz_oxide` |
| `sd-jwt` | ✅ | SD-JWT, SD-JWT VC and `vc+sd-jwt` credentials (`sd_jwt_vc`); implies `vc` and `status-list` |
| `domain-linkage` | ✅ | DIF Well Known DID Configuration (`domain_linkage`); implies `vc` and `did-web` |
| `linked-vp` | ✅ | DIF Linked Verifiable Presentation (`linked_vp`); implies `vc` and `did-web` |
| `jwks` | | `resolver::JwksResolver`: the keys of an HTTPS issuer from its JWT VC issuer metadata; implies `did-web` |
Expand Down
39 changes: 23 additions & 16 deletions docs/DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ implemented; [`FEATURES.md`](FEATURES.md) tracks the status of each feature and
## Principles

1. **One crate, two layers, hard boundary.** Lower layer: `did`, `jose`, `keys`, `identity`,
`resolver`, `methods`. Upper layer: `vc`, `sd_jwt_vc`, `domain_linkage`, `linked_vp`. Lower modules
never import upper ones. The upper modules are feature-gated so CI can prove the boundary
`resolver`, `methods`. Upper layer: `vc`, `sd_jwt_vc`, `status_list`, `domain_linkage`,
`linked_vp`. Lower modules never import upper ones. The upper modules are feature-gated so CI can prove the boundary
(see Conformance).
2. **Traits, not enums, for extension points.** A DID method implements `DidResolver` and/or
`DidProducer` and declares `MethodCapabilities`. A key backend implements `KeyStore` and declares
Expand Down Expand Up @@ -56,7 +56,8 @@ identity-kit/
│ ├── resolver/ # DidResolver, DidProducer, MethodCapabilities, Resolver, DocumentCache, dereferencing, KeyReference, KeyResolver
│ ├── methods/ # key, jwk, web
│ ├── vc/ # [vc] Jwt, Credential, Presentation, CredentialV2, EnvelopedVc, ClaimPath, validation
│ ├── sd_jwt_vc/ # [sd-jwt] SD-JWT (RFC 9901), SD-JWT VC, status list, type metadata, validator
│ ├── sd_jwt_vc/ # [sd-jwt] SD-JWT (RFC 9901), SD-JWT VC, type metadata, validator
│ ├── status_list/ # [status-list] Token Status List: status claim, list, token, StatusListCheck
│ ├── domain_linkage/ # [domain-linkage] DID configuration resource builder and validator
│ └── linked_vp/ # [linked-vp] linked presentation services, builder, fetcher and validator
├── xtask/ # publish = false: release checks, vector regeneration, key CLI
Expand All @@ -79,7 +80,8 @@ All additive. CI runs `cargo hack --each-feature` and the no-default-features bu
| `software` | on | `zeroize`; JWK-file key store |
| `pkcs11` | off | `cryptoki`; `tokio` (`rt`) for the blocking pool; PKCS#11 key store |
| `vc` | on | `chrono` (`now`, `serde`, `std` only); W3C VC/VP JWT profile and validators |
| `sd-jwt` | on | `getrandom` (salts) and `miniz_oxide` (status lists); implies `vc` |
| `status-list` | on | `miniz_oxide` (the compressed list); the Token Status List for JWT and SD-JWT credentials; implies `vc` |
| `sd-jwt` | on | `getrandom` (salts); implies `vc` and `status-list` |
| `domain-linkage` | on | implies `vc` and `did-web` (for `HttpClient`) |
| `linked-vp` | on | implies `vc` and `did-web` (for `HttpClient`) |
| `jwks` | off | implies `did-web` (for `HttpClient`); `JwksResolver` over JWT VC issuer metadata |
Expand Down Expand Up @@ -321,8 +323,8 @@ pub struct JwtValidationOptions { algorithms, typ, issuer, audience, role, issue
`assertionMethod` or `authentication`), `TimeOptions` (explicit `now`, leeway), issuer and
holder binding to the party the `KeyReference` names, audience and nonce, accepted `typ` beyond the profile's,
`SubjectHolderRelationship`, `FailFast`, one format-agnostic `StatusCheck` trait over
`CredentialStatus::Vcdm` (and `CredentialStatus::SdJwt` with `sd-jwt`, for the `status` claim
of SD-JWT and JWT credentials alike, Token Status List §6.1) with a `StatusPolicy`,
`CredentialStatus::Vcdm` (and `CredentialStatus::StatusClaim` with `status-list`, for the
`status` claim of SD-JWT and JWT credentials alike, Token Status List §6.1) with a `StatusPolicy`,
`extract_issuer`/`extract_holder` (ADR 0004, ADR 0006); `ClaimPath` (SD-JWT VC §5.6.1, OID4VP §7 claims path pointer),
re-exported from `sd_jwt_vc`.
- `sd_jwt_vc` (`sd-jwt`): RFC 9901 implemented in the crate (ADR 0005): `Disclosure`,
Expand All @@ -333,21 +335,26 @@ pub struct JwtValidationOptions { algorithms, typ, issuer, audience, role, issue
enforced, `SdJwtVcBuilder`; `SdJwtCredentialV2` for a VCDM 2.0 credential secured with
SD-JWT (VC-JOSE-COSE §3.2.1: `typ: vc+sd-jwt`, the `CredentialV2` as the payload, `cnf` and
`status` beside it), `SdJwtCredentialV2Builder`, and `EnvelopedVc::to_sd_jwt` for the
`application/vc+sd-jwt` envelope of a VCDM 2.0 presentation; `Status`/`StatusListRef`/
`StatusMechanism`, `StatusList` (ZLIB bit array) and `StatusListToken` for the Token Status
List; `TypeMetadata` with claim paths, `extends` folding, claim validation and `Integrity`
`application/vc+sd-jwt` envelope of a VCDM 2.0 presentation; `TypeMetadata` with claim paths, `extends` folding, claim validation and `Integrity`
metadata; `SdJwtCredentialValidator` over `KeyResolver` + `JwsVerifier` (`validate` an SD-JWT
VC, `validate_v2` a VCDM 2.0 credential: issuer key from the token's `KeyReference` under
`assertionMethod`, `iss` or `issuer` bound to the party it names, time claims and validity
period, key binding with `KeyBindingOptions` and the holder key from `cnf` (`jwk` or `kid`)
under `authentication`, the `vc` `StatusCheck` with a `StatusPolicy` for `status` and
`credentialStatus`, `StatusListCheck` over an `HttpClient`: `Accept:
application/statuslist+jwt`, and at most three redirects, each target `https` and fetched
through the client again so that every hop passes its address checks; its `StatusError` tells
a list that could not be fetched from one that is invalid, signed by a key that does not
resolve, expired or too short, so that a caller can retry only the first).
`JwtPresentationValidator::validate_v2` unwraps `vc+sd-jwt` envelopes next to `vc+jwt` ones
(`PresentedCredentialV2`), with key binding required per `PresentationValidationOptions`.
`credentialStatus`). `JwtPresentationValidator::validate_v2` unwraps `vc+sd-jwt` envelopes
next to `vc+jwt` ones (`PresentedCredentialV2`), with key binding required per
`PresentationValidationOptions`.
- `status_list` (`status-list`): the IETF Token Status List, which the draft defines for any JWT
(§6.1), so it serves JWT and SD-JWT credentials alike and needs no SD-JWT code:
`Status`/`StatusListRef`/`StatusMechanism` for the `status` claim, `StatusType`, `StatusList`
(ZLIB bit array) and `StatusListToken` (signed with any `Signer`, read with `status_of`), and,
with `did-web` as well, `StatusListCheck`, the `vc` `StatusCheck` over an `HttpClient`:
`Accept: application/statuslist+jwt`, and at most three redirects, each target `https` and
fetched through the client again so that every hop passes its address checks; its
`StatusError` tells a list that could not be fetched from one that is invalid, signed by a key
that does not resolve, expired or too short, so that a caller can retry only the first. It
depends on `vc` for `Jwt` and `JwtClaims`. `sd_jwt_vc` keeps deprecated aliases of its types,
and `sd_jwt_vc::validation::StatusListCheck`, for one release.
- `domain_linkage` (`domain-linkage`): `DomainLinkageConfiguration` (JWT entries typed, Linked
Data Proof entries carried as data), `DomainLinkageCredentialBuilder`, `DomainLinkageValidator`
implementing the DIF Well Known DID Configuration checks (`iss == sub ==
Expand Down
Loading
Loading