Skip to content
Registry Stack Docs Latest

SD-JWT VC conformance profile

View as Markdown

Adoption mode: profiled. Registry Notary issues a constrained SD-JWT VC profile (application/dc+sd-jwt, EdDSA over Ed25519, did:jwk holder binding); it does not claim full SD-JWT VC or OpenID4VCI conformance.

Registry Notary currently issues one credential format: SD-JWT VC using the current digital credential media type.

This profile is intentionally narrow. It documents the credential contract that Registry Notary can test and support today, and it names adjacent ecosystem features that are not yet part of the product surface.

FieldValue
Credential media typeapplication/dc+sd-jwt
Compact JWT typ headerdc+sd-jwt
Signing algorithmEdDSA
Issuer key typeOKP/Ed25519
Holder binding DID methoddid:jwk
Credential status methodsDefault: none. Optional: RegistryNotaryCredentialStatus when credential_status.enabled = true, with statusUrl at /v1/credentials/{credential_id}/status. StatusList and revocation-list profiles are not supported.

Registry Notary rejects credential profile format aliases such as sd_jwt_vc and application/vc+sd-jwt. Operator configuration must use the wire media type application/dc+sd-jwt.

Issued credentials use a compact issuer-signed JWT as the first component of the SD-JWT value. The protected header has these Registry Notary invariants:

  • alg is EdDSA.
  • typ is dc+sd-jwt.
  • kid is the kid on the credential profile’s configured signing key.

Only Ed25519 EdDSA signing keys are supported. Local JWK keys are supported for development and tests; PKCS#11 keys are available behind the optional server feature.

Signing key configuration examples are documented in signing-key-provider.md.

Registry Notary sets these payload claims:

  • iss: credential profile issuer.
  • sub: holder DID for holder-bound credentials, otherwise the evaluation subject reference.
  • iat: evaluation issue instant supplied by the caller.
  • exp: iat + credential_profile.validity_seconds.
  • vct: credential profile verifiable credential type URI.
  • jti: generated credential identifier.
  • id: same generated credential identifier as jti.
  • _sd: sorted disclosure digest list.
  • cnf: holder confirmation for holder-bound credentials.

For citizen-facing holder-bound credentials, sub is the holder DID. Registry Notary does not claim that the holder DID is the same identifier as the civil or registry subject.

When a credential profile requires holder proof of possession:

  • holder_binding.mode is did.
  • holder_binding.proof_of_possession is required.
  • holder_binding.allowed_did_methods must contain only did:jwk.
  • The issuance request must provide a holder DID and proof payload accepted by the server holder-proof validator.
  • The issued credential includes cnf.kid equal to the holder DID.
  • The issued credential includes cnf.jwk with the public holder JWK only.

Registry Notary does not support did:key, did:web, CWT proof, or mDoc holder binding in this profile.

Each evaluated claim result becomes one selectively disclosable claim. The disclosure payload contains:

  • claim_id
  • version
  • value
  • satisfied
  • subject_type
  • issued_at

The issuer-signed JWT stores disclosure digests in sorted order. The raw compact SD-JWT value is returned separately from the issuer-signed JWT and disclosure list so callers can verify or present the pieces without reparsing the whole credential.

/.well-known/evidence-service exposes a credential_capabilities object with the same constants listed in this profile:

  • supported credential media types;
  • SD-JWT VC JWT typ;
  • signing algorithms;
  • issuer signing key types;
  • holder binding methods;
  • configured credential profiles;
  • unsupported adjacent features.

The metadata is Registry Notary capability metadata. It is not a claim of full OpenID4VCI issuer conformance.

When OID4VCI is enabled, Registry Notary serves SD-JWT VC Type Metadata at the well-known location derived from each configured HTTPS vct. Per the SD-JWT VC Type Metadata convention, a consumer dereferences an HTTPS vct by inserting /.well-known/vct between the host and the path, so the metadata is served at GET /.well-known/vct/{vct_path}.

  • Matching. The handler strips the /.well-known/vct prefix and reconstructs the candidate vct as https://{host}/{vct_path}, then matches it exactly against a configured credential configuration. The route uses a trailing-wildcard capture, so nested configured paths such as /.well-known/vct/credentials/dhis2/health-status/v1 are supported, not only two segments.
  • Direct dereference. The bare GET /credentials/{vct_path} route is also served for consumers that dereference the vct directly.
  • Auth and prefixes. Both routes are public (no authentication) and expect path-prefix deployments to strip the issuer prefix before forwarding to Notary, while preserving the external host and scheme.
  • Not found. Both return 404 when OID4VCI is disabled or the reconstructed absolute URL does not exactly match a configured credential configuration vct.
  • Document contents. The Type Metadata document includes the exact configured vct, display metadata, and one claim metadata entry for the OID4VCI configuration’s claim_id. Notary-issued claim results are always selectively disclosable, so claim metadata uses sd: "always".
  • CORS. Browser-based wallets from configured self-attestation wallet origins receive CORS headers on the /.well-known/vct/... metadata surface.

Registry Notary ships a verifier compatibility harness that exercises the verify_sd_jwt_vc path in registry-notary-client against a committed set of golden fixtures. The harness requires no secret material and no network access.

Run the harness:

cargo test -p registry-notary-server --test sd_jwt_vc_verifier_compat

Or with cargo-nextest:

cargo nextest run -p registry-notary-server sd_jwt_vc_verifier_compat

Fixture files live under tests/fixtures/sd_jwt_vc/. To regenerate them using the server issuance path:

cargo run -p xtask -- gen-sd-jwt-vc-fixtures

The fixture set covers one valid credential, one valid holder-bound credential, and seven negative variants: unsupported algorithm, wrong kid, wrong vct, missing cnf when holder binding is required, malformed disclosure, expired credential, and holder proof mismatch. Each negative fixture is asserted against its exact error code from the verifier API.

The following features are out of scope for the current profile:

  • application/vc+sd-jwt compatibility alias;
  • JSON-LD Verifiable Credential issuance;
  • Data Integrity proofs;
  • external status-list or revocation-list profiles;
  • mDoc/mDL;
  • CWT proof binding;
  • full OpenID4VCI issuer behavior.

These features require separate compatibility, lifecycle, and security design before implementation.