
Collection of primitives for SSI libraries and applications: decentralized identifiers (DIDs), key management behind a pluggable key store, and JWT-based verifiable credentials.
Add it to a project with cargo add identity-kit. The API reference is on docs.rs.
Features
- ✅
Did,DidUrlandDidMethodparsing and validation per W3C DID Core - ✅
DidDocumentdata model and DID Resolution types - ✅ JWK and compact JWS (RFC 7515, RFC 7517) with EdDSA, ECDSA and RSA verification
- ✅
KeyStoretrait designed for remote signers, with in-memory and file-based backends - ✅ PKCS#11 key store backend, tested against SoftHSM2
- ✅
Identitytrait for the signing party: the identifier,kidand signer for a negotiated identifier kind and algorithm, withKeyStoreIdentityover any key store - ✅
DidResolverandDidProducertraits with aResolverregistry, document cache and DID URL dereferencing - ✅
KeyReferenceandKeyResolver: a token's key found from a DID URLkid, a#fragmentrelative to a DIDiss, ajwkheader,cnf, or the JWT VC issuer metadata of an HTTPSiss - ✅
did:key,did:jwkanddid:webmethods, with a hardened HTTPS client fordid:web - ✅ Verifiable credentials and presentations as JWTs: the VCDM 1.1 JWT profile, VCDM 2.0 with 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, and type metadata - ✅ The IETF Token Status List for JWT and SD-JWT credentials alike: issuing and signing status lists, and checking the
statusclaim - ✅ DIF Well Known DID Configuration: the DID configuration resource, its domain linkage credentials and their validation against an origin
- ✅ DIF Linked Verifiable Presentation:
LinkedVerifiablePresentationservices, presentations signed for publishing, and fetching and validating what a DID document links
✅ implemented, 📋 planned. The full checklist, with Cargo features, tracking issues and links to every specification, is in docs/FEATURES.md.
The architecture is described in docs/DESIGN.md.
Cargo features
All features are additive; the lower layer (did, jose, keys, identity, resolver) is always compiled.
| Feature | Default | Enables |
|---|---|---|
did-key |
✅ | The did:key method (methods::key) |
did-jwk |
✅ | The did:jwk method (methods::jwk) |
did-web |
✅ | The did:web method (methods::web) over any HttpClient |
reqwest |
✅ | The hardened default HttpClient for did:web (reqwest with rustls); implies did-web |
local-network |
Development and tests only: ReqwestClient::builder with opt-ins to reach loopback and private addresses over http; implies reqwest |
|
secp256k1 |
✅ | ES256K signatures and secp256k1 keys, via k256 |
rsa |
RS256 and PS256 signature verification with keys of 2048 bits or more, via rsa |
|
software |
✅ | SoftwareKeyStore: private keys in a JWK set file |
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 |
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 |
|
utoipa |
OpenAPI schemas (utoipa::ToSchema) for DidDocument, VerificationMethod, Service, Jwk, JwsAlgorithm, Credential, Presentation, CredentialV2, PresentationV2, EnvelopedVc, EnvelopedVp, DomainLinkageConfiguration, ClaimPath, TypeMetadata and StatusType |
|
test-vectors |
keys::testsuite, the behavioural suite every key store must pass, and vectors, the specification vectors |
identity_kit::prelude gathers the common types of the lower layer and identity_kit::prelude::credentials those of the upper layer, for glob import.
Key stores
Private keys live behind the KeyStore trait: opaque key ids, one algorithm per key, signing as an async operation that may leave the process.
The crate ships vendor-neutral backends; a cloud KMS or HSM backend implements the trait from its own crate.
| Backend | Feature | Keys live in | Generates | Deletes | Signs with |
|---|---|---|---|---|---|
InMemoryKeyStore |
in-memory |
Process memory, zeroized on drop | ✅ | ✅ | EdDSA, ES256, ES256K (secp256k1) |
SoftwareKeyStore |
software |
A private JWK set file, permission-checked, atomically rewritten | ✅ | ✅ | EdDSA, ES256, ES256K (secp256k1) |
Pkcs11KeyStore |
pkcs11 |
A PKCS#11 token: an HSM, a smart card, SoftHSM2 | ✅ unless write-protected | ✅ unless write-protected | What the module offers: EdDSA, ES256, ES256K (secp256k1), RS256 or PS256 |
Neither software backend signs with RSA: see ADR 0001; the PKCS#11 backend signs RSA in the module.
The full contract, with the error vocabulary and the algorithm matrix, is in docs/key-stores.md.
Extending
Adding a DID method
A method is a type that implements DidResolver (the Read operation) and, when it can create documents from keys, DidProducer.
No enum lists the methods: a Resolver dispatches on the DID's method name to whatever was registered.
use *;
;
let resolver = builder.with.build;
DidProducer adds capabilities() (whether documents can be updated, where they are hosted, how many keys fit) and produce(keys, params), which builds a document for a set of public keys.
Keep the method in a module of its own behind a Cargo feature, test it against its specification's vectors, and add a row to docs/FEATURES.md.
Adding a key store backend
A backend implements KeyStore: capabilities, public_jwk, sign and exists, plus generate and delete when the backend supports them.
Signatures come back in their JWS form (64 bytes for EdDSA, raw r || s for ECDSA), and the capabilities must be truthful: every algorithm listed signs, and generate succeeds only when advertised.
use *;
The behavioural suite checks the contract against a live backend; run it in the backend's own tests with the test-vectors feature enabled:
run.await; // a backend that generates keys
run_with_keys.await; // provisioned keys
Development
Report bugs and propose features in GitHub issues.
See CONTRIBUTING.md for how to set up, test and submit changes.
The parsers are fuzzed continuously with cargo-fuzz; fuzz/README.md explains the targets, their seeds and how to run them.
To report a vulnerability, see SECURITY.md.
Release
Releases are automated with release-plz and follow Cargo's SemVer rules.
Every push to main updates a release pull request with the next version and changelog, derived from the Conventional Commits since the last release.
Merging it publishes to crates.io and creates the tag and GitHub release.
Versions and CHANGELOG.md are never edited by hand.
See docs/RELEASE.md for the versioning rules, changelog filtering and the credential setup.