Skip to content

Domain-separated hashing in the Noir circuit implementation #2082

@unspecifiedcoder

Description

@unspecifiedcoder

Context

Proving is Circom-only today; the Noir crates (noir/crates/dg1, noir/crates/econtent) do not yet implement the nullifier/scope/commitment hashes, and the Noir migration is active. This proposes a hardening to build into Noir by construction ,explicitly not a patch to deployed Circom.

The gap

Three Poseidon usages lack per-purpose domain separation:

  • Nullifier — Poseidon(2)([secret, scope]) (vc_and_disclose.circom:133, vc_and_disclose_kyc.circom:135)
  • KYC leaf — Poseidon(2)([secret, msg_hasher.out]) (vc_and_disclose_kyc.circom:105)
  • Commitment — Poseidon(5)([secret, attestation_id, ...]) (register.circom:218-224)

Nullifier and KYC leaf are both arity-2 with secret first, differing only in operand 2. No tag distinguishes "nullifier" from "tree leaf".

Severity

Defense-in-depth aligned with Semaphore v4 practice not a demonstrated vulnerability. scope is verifier-bound and enforced on-chain (IdentityVerificationHubImplV2.sol:833-844) and off-chain (SelfBackendVerifier.ts:62,106-115); there is no known path to force scope == msg_hasher.out. Value: don't let the Noir port inherit the untagged construction.

Why not patch Circom

Tagging changes hash outputs: stored nullifiers (IdentityRegistryImplV1.sol:83) stop matching → double-use regression; commitment change invalidates the on-chain identity tree; and any R1CS change forces a new Groth16 phase-2 ceremony plus verifier redeployment. The hardening is only free where there is no deployed state ,the unbuilt Noir circuits.

Proposal

  1. Implement Noir nullifier/scope/commitment with explicit tag constants from day one (H(TAG_NULLIFIER, ...), H(TAG_LEAF, ...), H(TAG_COMMITMENT, ...)).
  2. Ship divergence vectors (legacy Circom as reference) enumerating exactly which outputs differ and why equivalence is intentionally not a goal.
  3. Include a compatibility analysis: nullifier-set invalidation, tree re-registration, trusted-setup impact, and a circuit-version coexistence strategy.

Out of scope

Deployed Circom/contracts/registries; scope derivation (correct as-is); any exploit claim.

Questions for maintainers

  1. Is Noir the long-term proving path, and is now the right time to bake this in?
  2. Preferred domain-tag scheme?
  3. Version coexistence vs. flag-day cutover?

cc @Nesopie @remicolin @0xturboblitz

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions