Prepare phase
The Prepare relation proves correct parsing and issuer validation of the SD-JWT–style credential and produces the policy-independent proof the wallet can cache. The Rust stack calls this prepare; the SDK exposes it as precompute (OpenAC.precompute).
What is proven (conceptual)
Per Paper — construction / Prepare: issuer signature verification, disclosure digest consistency, and a commitment interface to the message vector used later in Show.
Inputs the SDK expects
PrecomputeRequest (types.ts):
jwt: compact serialization (header.payload.signature).disclosures: raw base64url disclosure strings (SD-JWT[salt, name, value]triples).issuerPublicKey: P-256 JWK (kty: EC,crv: P-256) or PEM wrapper.keys:KeySetwith prepare + show proving/verifying keys.- Optional:
jwtParams,birthdayClaimIndex,decodeFlags,additionalMatches,claimFormats.
The credential must carry a device binding key:
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;
}
Pedersen / curve notation (paper-aligned)
Prepare ends by committing to the parsed attribute vector using a Pedersen vector commitment, exactly as defined in Paper §Preliminaries — Commitments and Proof Interface. With public generators in a cyclic group of prime order and randomness :
The Prepare relation certifies that the SD-JWT parses to , that the issuer signature verifies, and that commits to under randomness . The same is referenced by Show — the verifier checks consistency on to link the two halves without learning .
In the PoC backend, is the paper's Tom256 (T256) proving curve — configured in code under the alias secq256r1 (see wallet-unit-poc/circom/circomkit.json). ECDSA over P-256 / secp256r1 is used for issuer and device signatures. See Security — assumptions for the full curve breakdown.
JwtCircuitInputs (low-level)
Field-aligned inputs for Circom are built by buildJwtCircuitInputs — see JwtCircuitInputs in types.ts (sig_r, sig_s_inverse, message limbs, matchesCount, claims, …).
DEFAULT_JWT_PARAMS
Values exported from types.ts:
| Field | Default | Meaning |
|---|---|---|
maxMessageLength | 1920 | Maximum JWT length (header.payload) in bytes the circuit handles. |
maxB64PayloadLength | 1900 | Maximum base64url payload length. |
maxMatches | 4 | Maximum number of substring extractions per JWT. |
maxSubstringLength | 50 | Maximum length of each substring match. |
maxClaimLength | 128 | Maximum length of each extracted claim value. |
The 1k / 2k / 4k / 8k proving-key sets baked into scripts/keys/ correspond to scaled maxMessageLength variants of the JWT circuit (jwt, jwt_1k, jwt_2k, …). Choose a VcSize that fits your worst-case JWT.
PrecomputeRequest.decodeFlags and PrecomputeRequest.claimFormats are optional; when omitted, the input builder fills them with zeros (UTF-8 string format, no special decoding) up to maxMatches entries. Set them explicitly if you have integer claims (claimFormats[i] = 1) or need URL-decoding.
Version
Defaults track SDK 0.1.0.