Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

1. Abstract

This spec introduces a new ZK circuit (“Ownership Proof”) to the World ID Protocol that allows a user to prove they own a specific registered leaf without revealing their leaf_index or any private key material to the verifier. The circuit takes a domain-separated commitment, H(domain_separator, leaf_index, commitment_blinder), and proves that the user controls a secret key registered in the WorldIDRegistry for that leaf, without exposing the commitment_blinder value (e.g. a credential blinding factor) as a public input. The initial use case is Issuer Authentication, where a user proves ownership of a credential sub to an Issuer for operations like re-issuance, renewal, or deletion.

2. Motivation

There are use cases that require users to prove ownership for their World ID and a specific commitment. At the time of writing there is a well-known use case for Issuer Authentication.

2.1 Issuer Authentication

Issuers may require users to authenticate with their World ID. Some examples of such use cases:

  • Re-issuance. Allowing the user to get a new copy of their credential due to loss.
  • Status information. Providing the user with information on their current enrollment state.
  • Cancellation/deletion/unregistration. Allowing the user to unregister, delete or cancel their existing credential.

For the cases above, Issuers need to know the legitimate owner of a Credential is requesting the operation. Issuers do not know the user’s leaf_index by design, so a mechanism to authenticate based on the Credential’s subject (sub) is required.

3. Specification

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC 2119.

The terms Authenticator, Issuer and Credential are as defined in the World ID Protocol.

This specification introduces a new circuit to the Protocol: “Ownership Proof”. The Ownership Proof is a new circuit which allows users to prove ownership (as defined below) using a specific commitment.

A non-normative high-level description of what the circuit proves:

  1. It proves the user’s leaf_index hashed with a commitment_blinder and a specific domain separator produces an expected_commitment. An example, is the Credential’s blinded sub.
  2. It proves the user’s leaf_index is validly registered in the WorldIDRegistry and the user can produce a signature with a secret key ($sk$) registered as an authenticator for the particular leaf_index.
  3. It proves the user authorized the operation through a signature with a registered authenticator secret key and that such operation is only valid for a specific nonce and context provided.

The Ownership Proof circuit MUST introduce the following inputs. The circuit MUST NOT introduce any additional public inputs which are not specified in this or a future spec.

InputVisibilityTypeDescription
expected_commitmentPublicField elementThe expected output of the hashed commitment (e.g. the sub of a credential)
merkle_rootPublicField elementThe root hash of the Merkle tree used for inclusion (representing the WorldIDRegistry)
depthPublicField elementThe depth of the WorldIDRegistry’s Merkle tree used for inclusion
noncePublicField elementOne-time use value to prevent replays. See Nonce section.
contextPublicField elementA verifier-supplied context for which the proof is valid.
user_pkPrivate[PublicKey; NUM_KEYS]The list of authenticator public keys registered in the WorldIDRegistry.
pk_indexPrivateField elementThe index in user_pk of the key used for signature.
query_sPrivateField elementThe s component from the EdDSA signature on the message (see constraints).
query_rPrivate[Field element; 2]The r component from the EdDSA signature on the message (see constraints).
leaf_indexPrivateField elementThe index of the user’s World ID in the WorldIDRegistry.
siblingsPrivate[Field element; MAX_DEPTH]The sequence of all siblings from the leaf_index to the merkle_root .
commitment_blinderPrivateField elementThe blinding value that is hashed with the leaf_index to output the expected_commitment. The provenance of the value depends on the specific use case (see Use Cases).

For the inputs above, a PublicKey is as defined in the WIP-100 spec (an EdDSA public key represented as its coordinate points $(x,y)$ with each coordinate being a Field element). For reference, user_pk, pk_index, leaf_index (named mt_index), siblings introduce the same constraints that the Nullifier Proof circuit.

3.1 Circuit constants

The Field ($F$), the Curve (BabyJubJub), and the Hashing ($H_t$) definitions MUST follow the requirements of WIP-100. For example, in this spec H_3 refers to the hash constructed from a Poseidon2 permutation of 3-elements.

ConstantValueDescription
NUM_KEYS7Length of the authenticator public key list. Fixed by the Nullifier Proof circuit and the WorldIDRegistry.
MAX_DEPTH30For reference, defined in WIP-100. Maximum supported WorldIDRegistry Merkle tree depth; also the length of siblings.
MERKLE_LEAF_DS105702839725298824521994315 (b"World ID PK")Domain separator for the authenticator key list commitment.
DS_EDDSA360302137480307891234917541314130533 (b"EdDSA Signature")For reference, defined in WIP-100. Domain separator for the EdDSA challenge hash.
DS_WIP_10395972389630003 (b"WIP103")Domain separator for the Ownership Proof signed message. MUST NOT be reused outside of the Ownership Proof signed-message construction.

3.2 Circuit constraints

The Ownership Proof circuit MUST implement the following constraints (order is not relevant):

  1. Expected commitment. Compute the commitment as: H_3(domain_separator, leaf_index, commitment_blinder). The computed commitment MUST equal the expected_commitment. The domain_separator is dependent on the Use Case and all possible domain separators MUST be hardcoded in the circuit.
  2. Signature verification. Compute the message of the signature as: message = H_4(DS_WIP_103, expected_commitment, nonce, context). The circuit MUST verify (query_r, query_s) as an EdDSA-Poseidon2 signature by pk over the message in order to prove authorization by an authenticator over the particular nonce and context. The signature computation and verification MUST follow all the requirements from WIP-100.
  3. Key selection. The circuit MUST select exactly one public key pk = user_pk[pk_index] and MUST constrain:
    1. pk_index < NUM_KEYS, enforced as a comparison over the full field $F$ and not as a truncating cast to a smaller integer type. An out-of-range pk_index MUST cause proving to fail.
    2. The selected pk equals the pk_index-th element of user_pk, for the same pk_index witness.
  4. Key validity. user_pk MUST be a fixed-length array of NUM_KEYS entries in WorldIDRegistry slot order. Slots with no active authenticator, whether never used or removed, MUST be encoded as the neutral element $O = (0,1)$ of the BabyJubJub curve. The circuit MUST constrain the selected $\texttt{pk} = (A_x, A_y)$ as follows: 1) the point MUST be on the curve; 2) the point MUST be in the prime-order subgroup; and 3) the point MUST NOT be the neutral element $O$. Constraint (3) is particularly important because $O$ is used to signal an omitted element from the array, without it, the circuit would not have transitive proof of sk knowledge.
  5. Leaf index commitment. The circuit MUST compute offchainSignerCommitment as designated in the Query Proof, with $H_{16}$ using the domain separator MERKLE_LEAF_DS and each key in user_pk encoded as $(x, y)$ points sequentially, with the last position (index 15) equal to 0. The digest is output state element 1. All keys MUST be included in order, including empty slots with the neutral element $O$.
  6. Merkle inclusion proof. The circuit MUST recompute merkle_root from offchainSignerCommitment as the leaf value at position leaf_index following the definitions of WIP-100.
  7. Leaf-index binding. The leaf_index hashed into the commitment in constraint 1 and the position proven in constraint #6 MUST be the same witness. The circuit MUST NOT accept two independent values. This binding is essential, otherwise the circuit is only proving the prover knows the sk of some leaf, not the one being proven in the expected_commitment.
  8. Verifier-supplied input validation. The nonce and context MUST NOT equal 0. These are usually intended as sentinel values and could surface an incorrect use of them.

3.3 Nonce

The nonce is a security mechanism to prevent replay attacks. Recipients of an Ownership Proof (also called “verifiers” in this spec) are responsible for its generation and verification.

  1. Provenance. Verifiers MUST generate and provide the nonce. Authenticators MUST NOT generate nonces.
  2. Single Use. Verifiers MUST enforce that a nonce may only be used one time. Verifiers MUST invalidate any nonce after first use.
  3. Entropy. Verifiers MUST generate the nonce from a uniformly random distribution using a CSPRNG over the whole field $F$.
  4. Expiration. Nonces MUST have a defined TTL and not be valid after expiration. Verifiers MAY define the mechanism to accomplish this.
  5. The 0 element is NOT a valid nonce.

3.4 Context

The context is the mechanism by which a verifier binds a specific proof to a specific requested operation. The verifier MUST constrain the use of Ownership Proofs to specific operations and assign such operations with a unique context. For example, renewing a credential and deleting a credential are two different contexts. The verifier MUST provide the context when requesting a proof and MUST verify the provided context is valid for the explicit operation being executed.

It is RECOMMENDED that Verifiers use contexts shorter than 31 bytes or use a hash function to avoid collisions.

The 0 element is NOT a valid context.

4. Use Cases

  1. Authenticators MUST restrict Ownership Proofs subject only to the use cases outlined in this spec or a future spec which references this one.
  2. If future use cases are added, the commitment_blinder IS REQUIRED to always be a high entropy value to prevent finding a user’s leaf_index. Unless otherwise noted, it should be randomly generated from a CSPRNG uniformly distributed over the entire $F$.

4.1 WIP-103-001: Issuer Authentication

  • Use case: Authenticating to an Issuer where the Issuer has a sub as identifier.
  • Domain Separator: b"H_CS(id, r)" named DS_C_CS.
  • commitment_blinder value: The credential’s blinding factor as stored (or re-derived) by the Authenticator.
  • Use conditions: Can only be used to prove ownership with the specific Issuer for which the sub was generated. The Authenticator MUST ensure this proof is only provided to the Issuer it corresponds. The Authenticator knows this from the blinding factor used as the commitment_blinder input. Furthermore, providing this proof MUST come from a user initiated action and SHALL NOT be requested by Issuers. Acceptable examples include: when a user requests a Credential re-issuance, when a user requests a Credential renewal, when a user requests a Credential deletion.
  • Verification: Issuers MUST ensure the expected_commitment public input of the circuit matches the expected identifier (i.e. Credential’s sub).

5. Rationale

A keen observer would notice the Ownership Proof circuit is very similar to the Query Proof circuit. Using this circuit is not an option because the verifying party would need to learn the commitment_blinder being verified to ensure the hash validity (it’s a public input). For example, an Issuer does not need to learn the credential blinding factor, which would defeat the purpose of the blinding factor because the leaf_index could be easily brute forced.

6. Reference Verification

An Issuer will be able to know a specific user is the legitimate owner of a sub by performing the following validations:

  1. Ensuring the received zero-knowledge proof verifies correctly with the correct verifying keys.
  2. The root hash (public input) matches a root hash for a valid Merkle tree of the WorldIDRegistry, as well as the expected depth.
  3. The expected_commitment public input matches the expected sub being verified. This value MAY also be used for lookups when the sub is not known in advance.
  4. The nonce public input is one the Issuer itself generated, has not expired, and has not been used before. The nonce MUST be invalidated at this point. See Nonce.
  5. The context public input is the context the Issuer assigns to the operation it is about to execute. A proof carrying any other context MUST be rejected, even if every other check passes. See Context.

Validations 4 and 5 are what make the proof unreplayable and single-purpose. The circuit constrains the nonce and context to be bound in the authenticator’s signature, but it cannot know which values the verifier intended, so a verifier that accepts the values supplied alongside the proof without comparing them to its own gains no replay or cross-context protection.

It is strongly RECOMMENDED that implementations verify the Ownership Proof using a proof format and verification method supported by ProveKit.

7. Security

  1. commitment_blinder is never revealed. Unlike the Query Proof circuit, the commitment_blinder value is a private input. For Issuer Authentication (WIP-103-001), this means the credential blinding factor is never exposed to the Issuer. If it were, the Issuer could brute-force the leaf_index (the domain of possible values is small) and break the unlinkability property of the blinding factor.
  2. Domain separation prevents cross-context replay. Each use case defines its own domain separator, and the domain_separator is committed inside the hash. A proof generated for one use case cannot be replayed in another context. Authenticators MUST enforce that only domain separators defined in this spec or a referencing spec are used.
  3. Authenticator-gated usage. Authenticators MUST restrict generation of Ownership Proofs to user-initiated actions for the defined use cases. Issuers SHALL NOT be able to request this proof; the Authenticator must initiate it. This prevents an Issuer from using the proof as an oracle to test whether a given sub belongs to a particular user.
  4. Nonce and context are bound into the signed message. The signature verified by the circuit is over nonce, context and expected_commitment, not only over the bare commitment. This means every Ownership Proof requires a fresh signature from the authenticator signing key for that specific (nonce, context) tuple (in addition to the expected_commitment), anchoring proof generation to an active signing event by the Authenticator.
  5. For note, proofs for the same expected_commitment (e.g. the same sub) are linkable because they use the same public input. This is intended as the expected_commitment is what its getting proven. This proof however prevents cross-verifier linking as the commitment_blinder differs.

8. Backwards Compatibility

This World Improvement Proposal (WIP) introduces a new interface; no existing contracts or other previously existing functionality is affected.