Credential formats
OpenAC does not invent credential formats. It adds a proof layer over existing issuer-signed credentials. Before OpenAC can process a credential, the wallet runs a credential binding: it identifies which values are covered by issuer authentication. Only those values can become OpenAC statement input.
For the full binding model, see Implementation profile §5.
SD-JWT VC (primary path)
Status: Implemented in openac-sdk 0.1.0.
SD-JWT VC is the primary credential family in the PoC, defined by draft-ietf-oauth-sd-jwt-vc.
Binding
The SD-JWT binding identifies:
- the issuer-signed JWT payload;
- the disclosures used as OpenAC input (
[salt, name, value]triples); - the salts and digests needed to check disclosure consistency against
_sdvalues in the payload; - the issuer signature and issuer verification material (P-256 / ES256 JWK in the PoC);
- device binding material from
cnf.jwk— required for the device-key ownership proof in Show.
The SDK's Credential.parse implements this binding:
// source: wallet-unit-poc/openac-sdk/src/credential.ts
const cred = Credential.parse(jwt, disclosures);
Each disclosure decodes to [salt, name, value] and a SHA-256 digest aligned with SD-JWT _sd digests.
Issuer-authentication path (Prepare)
Per Paper §3, Prepare runs:
- Parse the SD-JWT into messages, salts, hashes, and issuer signature.
- Check each hash corresponds to the message and salt (
_sdconsistency). - Verify the issuer signature under the issuer public key.
- Commit to the message vector with a Pedersen commitment.
A claim value may become OpenAC statement input only if it passes this path.
Claim addressing
Claim values are indexed by position in the disclosure array. The SDK's birthday detection illustrates the naming-based addressing before index assignment:
// source: wallet-unit-poc/openac-sdk/src/credential.ts:75-84
findBirthdayClaim(): number | null {
const birthdayKeys = ["roc_birthday", "birthdate", "birthday", "date_of_birth"];
for (const claim of this.claims) {
if (birthdayKeys.includes(claim.name)) {
return claim.index;
}
}
return null;
}
After binding, the claim's index is the stable identifier used in predicate definitions (claimRef in ShowInputOptions).
Device binding requirement
The credential must carry a device binding key:
// source: wallet-unit-poc/openac-sdk/src/credential.ts:87-97
get deviceBindingKey(): EcdsaPublicKey | null {
const cnf = this.payload.cnf as { jwk?: EcdsaPublicKey } | undefined;
if (!cnf?.jwk) return null;
const jwk = cnf.jwk;
if (jwk.kty !== "EC" || jwk.crv !== "P-256" || !jwk.x || !jwk.y) {
return null;
}
return jwk;
}
If cnf.jwk is absent or malformed, precompute fails. The device key is bound into Show via signDeviceNonce — see Show phase.
What does not become statement input
The SD-JWT binding treats the following as non-statement material:
- JWT header fields (algorithm, key ID, type);
iss,iat,exp,nbf,sub,aud— retained for trust and validity checks, not for predicates unless a statement type explicitly uses them;cnf.jwk— retained for device binding, not disclosure;_sd_algand digest algorithm selection — binding infrastructure.
ISO/IEC 18013-5 (mdoc) — experimental
Status: Experimental circuit target in wallet-unit-poc/circom (yarn compile:mdoc). Not wired to precompute / present in SDK 0.1.0. Do not assume feature parity with the SD-JWT path until versioned.
Binding model
For mdoc, the credential binding identifies:
- issuer-signed data elements that may become OpenAC statement input, with their namespace and element identifier;
- MSO (Mobile Security Object) as the issuer-authentication boundary;
- issuer-authentication material (issuer certificate chain, signature over the MSO);
- device-signed or session-specific material — separated from issuer-signed input, available for device binding only;
- validity, status, and trust material for verifier-side checks.
The binding MUST separate issuer-signed data from device-signed or session-specific material. Only issuer-signed data may become statement input.
After binding, each retained value is addressed by its (namespace, element_identifier) pair — these identifiers are preserved through normalization.
Implementation status
The wallet-unit-poc/circom/docs/mdoc-spec.md documents the mdoc circuit specification. The mdoc circuit target is compiled separately from jwt. Full integration with the SDK Prepare/Show pipeline is tracked on the zkID 2026 roadmap.
ePassport (ICAO 9303)
The paper discusses ePassport data groups and hash bindings as a candidate credential family. The in-repo PoC path is ES256 SD-JWT, not raw ICAO LDS. Treat ICAO 9303 as the standards anchor for physical-document credentials; circuit work for dedicated DG parsing may extend the Prepare relation. Track the implementation profile for normative detail.
W3C Verifiable Credentials
Not implemented as a first-class parser in openac-sdk 0.1.0. Presentations that use the same signing and digest story (ES256 + selective disclosure) could be integrated at the Prepare input builder layer in future versions — flagged as roadmap.
Cross-links
- Prepare phase — JWT circuit inputs, commitment notation.
- Show phase — device binding and predicate evaluation.
- Implementation profile §5 — normative credential binding requirements.
- Generalized predicates — policy over disclosed claims.
- Paper — formats / instantiations