refactor!: collapse create_session proof type into session x create - #866
refactor!: collapse create_session proof type into session x create#866kilianglas wants to merge 44 commits into
Conversation
…uniqueness-session-binding-contracts
…hub.com:worldcoin/world-id-protocol into kilianglas/uniqueness-session-binding-contracts
Replace the dedicated create-session proof type with a three-state session reference so request semantics are explicit on the wire. Co-authored-by: Cursor <cursoragent@cursor.com>
Carry the RP-signed uniqueness action separately from the session OPRF seed so nodes can safely authorize atomic session creation. Co-authored-by: Cursor <cursoragent@cursor.com>
Accept create and existing session references on uniqueness requests, mint sessions through the authenticator, and validate the corresponding response semantics. Co-authored-by: Cursor <cursoragent@cursor.com>
Exercise the signed-action create flow end to end and generate verifier fixtures from a session minted by the uniqueness request. Co-authored-by: Cursor <cursoragent@cursor.com>
Document create, existing, and unbound uniqueness modes together with the signed-action OPRF flow and deployment order. Co-authored-by: Cursor <cursoragent@cursor.com>
5fee2df to
f651855
Compare
| /// If omitted, the request is strictly treated as a [`ProofType::Uniqueness`] request. | ||
| /// Session creation and session proving must opt in explicitly. | ||
| #[serde(default)] | ||
| pub proof_type: ProofType, |
There was a problem hiding this comment.
Old create_session requests will now fail for sure though.
faf2043 to
2d7d228
Compare
Zero is the circuit's "no session" sentinel, satisfiable by any World ID regardless of mt_index, so a session record left at zero could be satisfied by an arbitrary World ID with a patched authenticator. verifyWithSession already rejected it; apply the same check to verifySession. V2 only — older implementations are immutable. Reported via HackerOne #3912490. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ilianglas/collapse-create-session
V2 is released and immutable, so the new entry point and the session id validation move to a new version instead of extending V2 in place. - IWorldIDVerifierV3 declares verifyWithSession + InvalidSessionId - WorldIDVerifierV3 implements verifyWithSession and overrides verifySession to reject sessionId == 0 - WorldIDVerifierV2.sol is restored byte-identical to main; InvalidAction stays declared inline there and V3 inherits it - V2 tests return to their original set (regenerated fixture values only); the binding and zero-session cases move to WorldIDVerifierV3Test - test-utils deploys V3 behind the proxy; e2e and the fixture tool retarget Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ilianglas/collapse-create-session # Conflicts: # contracts/src/core/interfaces/IWorldIDVerifierV3.sol
The regenerated fixture values were unnecessary: all six V2 tests are negative (four revert on InvalidAction before any proof math, two expect ProofInvalid), so they pass unchanged against the refreshed mocks. Leaves the PR with no V2 footprint at all. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ilianglas/collapse-create-session
The verifySession zero-session check is a separate security fix and moves to its own PR against main. V3 here adds only the new entry point. Depends on that PR landing for the session-proof hardening. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ilianglas/collapse-create-session # Conflicts: # contracts/src/core/interfaces/IWorldIDVerifierV3.sol
The base branch was changed.
…reate-session # Conflicts: # contracts/test/core/WorldIDVerifierV3Test.t.sol # crates/authenticator/src/prove.rs # crates/core/tests/generate_proof.rs # crates/primitives/src/request/mod.rs # crates/test-utils/src/anvil.rs # docs/world-id-4-specs/README.md
The merge left main's session-bound section alongside the create-and-bind one, so the tool no longer compiled: session_id is now SessionRef, not Option<SessionId>. Removes the stale block and the nullifier clone it needed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Keeps the discriminants aligned with the action prefixes: 0x00 for a uniqueness action, 0x02 for a session action. 0x01 is skipped because it prefixes the session oprf_seed rather than a proof flow. Reinstates the reservation from #711 for future signed request payloads, and pins it with a test so the values cannot renumber silently. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
@kilianglas Why do we disallow |
|
It's worth noting that this PR allows oprf nodes to link the initial uniqueness action and the session seed. I don't really think that's a problem in practice, just wanted to flag it. |
|
|
||
| let rp_signature_verification = match (proof_request.proof_type, proof_request.action) { | ||
| (ProofType::Uniqueness, Some(action)) => { | ||
| Some(RpSignatureVerification::UniquenessAction { action }) |
There was a problem hiding this comment.
The signed message doesn't include proof_type or session_id, which means these fields can be changed and the signature is still valid. We probably want to fix this.
There was a problem hiding this comment.
This is a valid more general question. The proof type is implicitly encoded in the action (action with 0x00 prefix is present -> uniqueness, no action -> session). The session id field is currently not signed over by the RP, and there might be ways to abuse this (need to think about this). Since this is pre-existing behavior, though, I don't think this should be addressed in this PR.
We thought it might be an anonymity footgun if we allow RPs to bind uniqueness proofs to existing sessions. |
Yes, that's true. Didn't think this was an issue, especially since |
Removes
ProofType::CreateSessionand redesigns the request model:proof_typeshrinks to{uniqueness, session}andsession_idbecomes a three-state field. Creating a session was always create-and-prove (a session proof in the same response), so it collapses intoproof_type: "session"+session_id: "create"with no capability change. Hence, there are only two proof types now, Uniqueness Proofs and Session Proofs. Both can create a session, only session proofs can be bound to an existing session. Therefore, sessions can either be bound to a nullifier, or can be started without any binding. Note, that Uniquess Proofs can still be requested without creating or binding a session.Stacked on #862.
Request model
New
SessionReftype carried byProofRequest.session_id:nullSessionRef::None"create"SessionRef::Create— mint a fresh session, prove it in the same response"session_<hex>"SessionRef::Existing(SessionId)Note: The
SessionRefis not in the signed part of the request.Validation matrix (
validate_proof_type):null"create""session_<hex>"uniquenesssessioncreate_session)actionremains forbidden forproof_type: "session"(both sub-states).Different combinations have different use cases:
uniqueness,null: Standalone uniqueness proof that does not require session.uniqueness,"create": Uniqueness proof that requires bound session for later session proof.session,"create": Create new standalone session to prove human continuity without uniqueness guarantees. Used e.g., in the upcoming DeepFace proof type.session,"session_<hex>": Session proof for existing session. This is used for anysession_id, uniqueness bound or not.Changes to OPRF nodes
To make this work, the auth module of the ORPF nodes had to be slightly changed. OPRF queries with prefix
0x01(used to obtain the session seed), can now originate from Uniqueness Proof RP requests. Those have anactionfield iin the signed data. Hence, in order to verify the RP Request signature, the OPRF request needs to carry thisaction, which differs fromaction = oprf_seedwhich is used for the query. We introduce a new field calledrp_signature_verificationonNullifierOprfRequestAuthV1struct.WIP-101 RPs
Important: Combined uniqueness + create session requests are not yet supported. This will be implemented in a follow-up PR.