Add 1/OPENAC spec (raw)#88
Conversation
Adds the OpenAC anonymous-credential presentation protocol spec at specs/1-openac/. Previously incubated in privacy-ethereum/zkspecs as 7/OPENAC-CORE (zkspecs#23; an earlier draft existed at zkspecs#21). Renamed from `OPENAC-CORE` to `OPENAC` at the identifier level (slug, folder, title, frontmatter name). In-body uses of "OpenAC Core" as a scope qualifier (distinguishing the core layer from future extensions like revocation, nullifiers, on-chain verifiers) are preserved as-is. Companion PR: privacy-ethereum#87 (the other two specs). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
| - Core text treats those items as future extensions or future profile specs, | ||
| not latent requirements of the current protocol. | ||
|
|
||
| ## 2. Device Public Key Exposure |
There was a problem hiding this comment.
The device key isn't just exposed by the SDK, it's a public output of the Show circuit itself.
wasm/src/lib.rs:258—verify()doesn't take any device key as input. It only takes proof bytes, verifying keys, and instances.verifier.ts:67-70— the device key comes out asshowPublicValues[1]and[2], right afterexpression_resultat[0].
So every Show proof emits (expression_result, deviceKeyX, deviceKeyY) as public values. Same device → same coordinates → verifier can link sessions.
To hide the device key, you need a new Show circuit (key becomes a private witness, PoP is proven against a commitment), a new verifying key, and a new version identifier. i think its more relevant to change profile change not an SDK
Also: the device key is in payload.cnf.jwk in the SD-JWT cleartext, so the issuer always sees the device→credential link. Hiding it from verifiers only helps if they don't talk to the issuer.
Suggested split:
- Current
SD-JWT-P256profile: accept thatdeviceKeyX/Yare public outputs. Document it. - New profile later for the unlinkable version, with its own
versionid.
There was a problem hiding this comment.
Thanks @0xVikasRushi for the clarification. I want to make sure I understand the privacy implication correctly.
If deviceKeyX/Y are public values of the current Show circuit, then this is not just an SDK API artifact. It means the current SD-JWT-P256 profile exposes a stable device public key to verifiers. If the same device key is reused across presentations, colluding verifiers can link sessions by comparing the key coordinates.
That seems to substantially weaken verifier-side unlinkability for the current implementation. My read is that this should be documented as a current-profile privacy caveat, rather than treated as an OpenAC Core property.
A few questions to help us define the spec accurately:
- Should the current
SD-JWT-P256profile explicitly say thatdeviceKeyX/Yare public outputs and therefore verifier-observable linking identifiers? - Should OpenAC Core still state the intended long-term property as “proof of possession of the bound device key,” without requiring stable device key exposure?
- Would an unlinkable device-binding version require a new Show circuit, verifying key, profile/version identifier, and a hidden-key or commitment-based PoP design?
I think the spec should distinguish:
- current implementation behavior,
- current privacy caveat,
- intended long-term OpenAC Core property,
- and future unlinkable device-binding profile work.
There was a problem hiding this comment.
Thanks @cc03668 for laying out the framing so clearly, it actually helped me look at this more carefully, and i think the fix turns out to be simpler than my earlier comment suggested. Apologies for the earlier framing pointing toward a new profile; the unlinkable device-binding machinery is in fact already implemented, we just aren't taking advantage of it.
A quick walk-through of what's already there:
- Both circuits share a witness layout [KeyBindingX, KeyBindingY, claimValues...] (prepare_circuit.rs:247-263, show_circuit.rs:222-238).
- present() samples fresh shared blinds per session and reblinds both proofs (wasm/src/lib.rs:278-298).
- verify() checks prepare_instance.comm_W_shared == show_instance.comm_W_shared (wasm/src/lib.rs:362).
commitment is hiding, re-randomized per session, and already binds Prepare's extracted device key to Show's signing key so by construction we don't actually need the device binding key in the circuit's public values at all.
Suggested fix: Remove the device-binding-key exposure from the Show circuit's public values. The key still flows from the issuer-signed credential through Prepare into Show's shared witness, and comm_W_shared equality enforces that binding so the verifier already has the cryptographic guarantee they need without seeing the raw coordinates.
On your three questions, with this in mind:
1/ I'd lean toward treating this as a current-profile bug to fix rather than a property to document. The intent of the design is unlinkability; the leak is unintentional, and fixing it doesn't require breaking changes elsewhere.
2/ Yes, PoP of the bound key, no stable identifier exposure, is the right long-term Core property. comm_W_shared is already the PoP mechanism that delivers it.
3/ With the fix above, a new Show circuit / VK / profile id shouldn't be necessary. The construction is in place; it just needs to actually be private.
There was a problem hiding this comment.
Oh that's a great news! Just one more check: does removing deviceKeyX/Y from public values change the public input layout, verifying key, serialized instance format, or test vectors?
If yes, we may still need a version/test-vector update even if we do not need a new profile.
|
|
||
| Primary external references: | ||
|
|
||
| - [OpenAC whitepaper](https://github.com/privacy-ethereum/zkID/tree/main/paper) |
There was a problem hiding this comment.
please use this link https://eprint.iacr.org/2026/251
Summary
specs/1-openac/— the OpenAC anonymous-credential presentation protocol spec.raw. Imported fromzkspecs#23(previously7/OPENAC-CORE); supersedes the earlier draft atzkspecs#21.What's included
specs/1-openac/README.md— the spec itself.specs/1-openac/OPEN-QUESTIONS.md— maintainer decision log (status promotion record).specs/1-openac/SOURCE-MATRIX.md— provenance matrix tying spec text to whitepaper sections andopenac-sdksource files.Rename:
OPENAC-CORE→OPENACThe spec is renamed at the identifier level only:
1-openac-core/→1-openac/7→1; title:7/OPENAC-CORE→1/OPENACnamefrontmatter:OpenAC Core Protocol→OpenACIn-body uses of "OpenAC Core" as a scope qualifier are preserved — the spec uses that phrase to distinguish the core protocol layer from future extensions (revocation, nullifiers, on-chain verifiers, additional credential profiles). Changing it everywhere would obscure that distinction.
Why this is a separate PR from #87
OpenAC is still in editorial flux. Tracking issue #89 (formerly
zkspecs#22) collects open verifier-profile boundary questions that need resolution before promotion beyondraw. Keeping this PR separate from #87 lets the other two specs land without being held up by that review.Cross-references
privacy-ethereum/zkspecs/specs/1(same convention as the other zkID specs).specs/README.md— that file is added by Add specs/: import proof-of-personhood and age-verification specs from zkspecs #87. Once both PRs land, a small follow-up edit tospecs/README.mdwill add the1/OPENACrow to the index.3/ZK-AGE-VERIFICATION(in Add specs/: import proof-of-personhood and age-verification specs from zkspecs #87) references this spec at../1-openac/README.md; that link resolves once this PR merges.Follow-ups
Once this lands:
1/OPENACrow tospecs/README.md(one-line edit).specs/1-openac/.cc @oskarth @vplasencia
🤖 Generated with Claude Code