Anti-Sybil Mechanisms
Preventing Sybil attacks, where a single entity creates multiple fake identities to manipulate a system or claim disproportionate rewards, is a critical requirement for secure agent ecosystems. If an AI agent cannot trust that the identities it interacts with are unique humans, the entire economic model collapses: airdrops get farmed, governance votes get manipulated, and reward pools get drained.
zkMe implements robust, privacy-preserving anti-Sybil mechanisms directly within its zero-knowledge circuits, ensuring uniqueness without compromising user anonymity.
Nullifier Generation and Uniqueness
To enforce a strict 1:1 binding between a human and an action, and to prevent zero-knowledge proof replay attacks, the zkMe Credential System utilizes cryptographic nullifiers.
A nullifier is a deterministic, one-way hash generated inside the zero-knowledge circuit. It serves as a unique, anonymous identifier for a specific user in a specific context. The same user interacting with the same session will always produce the same nullifier, but no one can reverse-engineer the user’s identity from the nullifier value.
The Nullifier Flow
The flow operates through five distinct stages:
Session Binding: When an AI agent or a decentralized application (Verifier) requests a proof from a user, they provide a unique
nullifierSessionID. This ID defines the scope of the uniqueness check (e.g., “Airdrop Claim 2026” or “Governance Vote #42”). The session ID is set by the backend when configuring the ZKP request viasetZkpRequest.Circuit Computation: The zero-knowledge circuit processes the request using the
NULLIFYoperator (Operator Code 17). The circuit takes the user’s core identity data (their private key or derived ID) and hashes it together with thenullifierSessionID, the credential data, and the verifier’s identity.Deterministic Output: The circuit outputs this hash as a public signal (
nullifier). This value is mathematically unique to the specific combination of the user’s underlying identity, the specific credential being used, the Verifier requesting the proof, and thenullifierSessionID. The same inputs will always produce the same output, but different inputs (even slightly different) will produce completely different outputs.On-Chain Validation: The smart contract performs an initial integrity check. If
nullifierSessionID != 0, the contract mandates that the resultingnullifiermust also be non-zero. This prevents a circuit from bypassing the uniqueness mechanism by outputting a zero nullifier:Registry Enforcement: The business logic contract (or the AI agent’s backend) maintains a registry of used nullifiers. By checking the submitted nullifier against this registry, the system can instantly detect and reject duplicate attempts, ensuring “one person, one vote” or “one person, one claim” without ever learning the user’s actual identity:
Important: The core verification circuit outputs the nullifier as a public signal and validates the relationship between the nullifier and the session ID, but it does not store the uniqueness registry itself. The registry must be maintained by the business logic layer (either on-chain in a mapping or off-chain in a database). This separation of concerns allows different applications to define their own uniqueness scopes without modifying the circuit.
Unified Authentication (BJJ and ETH Identity)
To generate a valid zero-knowledge proof and a valid nullifier, the system must authenticate the user, proving that the person generating the proof actually controls the identity bound to the credential. Historically, ZK identity systems required users to manage specialized BabyJubJub (BJJ) cryptographic keys, which are optimized for efficient verification inside zero-knowledge circuits but created significant friction for onboarding standard Web3 users who already have Ethereum wallets.
The latest circuit architecture introduces a unified authentication mechanism that supports both key types through a single circuit, significantly simplifying the user experience for agent-driven applications.
How It Works
The system uses a flag called isBJJAuthEnabled to select the authentication path:
BJJ
isBJJAuthEnabled == 1
BabyJubJub EdDSA signature
Identity derived from BJJ key pair; verified against the Global Identity State Tree (GIST) root
High-security scenarios requiring dedicated identity keys, such as institutional custody or multi-party computation setups
ETH
isBJJAuthEnabled == 0
Standard Ethereum wallet signature (e.g., MetaMask, Rabby)
Identity mathematically derived from the sender’s Ethereum address via GenesisUtils.calcIdFromEthAddress()
Standard user onboarding, consumer-facing dApps, and AI agent interactions where minimizing friction is the priority
The contract-level logic that enforces this dual-path authentication is straightforward:
Why This Matters for the Agent Economy
The ETH authentication path eliminates the need for users to manage separate, specialized cryptographic keys just to interact with privacy-preserving agents. The frontend SDK automatically detects the user’s connected wallet type and sets the isBJJAuthEnabled flag accordingly, making the entire process transparent to the end user.
This is particularly important for AI agent onboarding flows. When an agent needs to verify a user’s credential, the user simply signs with their existing Ethereum wallet. There is no additional key generation step, no seed phrase to back up, and no specialized wallet software to install. This allows seamless onboarding of millions of existing EVM users into the zkMe ecosystem while maintaining strict cryptographic proof of ownership and enabling robust anti-Sybil protections.
The BJJ path remains available for advanced use cases where dedicated identity keys provide additional security guarantees, such as institutional wallets that separate signing authority from identity authority, or hardware security module (HSM) integrations where the BJJ key is stored in tamper-resistant hardware.
SIG/MTP Unified Circuit
Further optimizing the verification process, the circuit architecture combines two fundamentally different proof verification methods, Signature-based (SIG) and Merkle Tree Proof-based (MTP), into a single unified circuit.
Background: Two Proof Types
When an Issuer creates a credential for a Holder, the credential’s validity can be established in two ways:
BJJ Signature
BJJSignature2021
The Issuer signs the credential with their BabyJubJub private key. The ZK circuit verifies this signature inside the proof.
Immediate availability (no on-chain transaction needed after issuance). Lower latency for the Holder.
Merkle Tree Proof
Iden3SparseMerkleTreeProof
The credential is added as a leaf to the Issuer’s on-chain Sparse Merkle Tree. The ZK circuit verifies a Merkle inclusion proof.
More gas-efficient for on-chain verification. Stronger auditability because the credential’s existence is anchored in the Issuer’s published state root.
In previous architectures, these two proof types required separate circuits and separate validator contracts. A developer building an AI agent had to implement two different verification paths and manage the routing logic between them.
Unified Approach
The current architecture merges both paths into a single circuit. The system uses a proofType field as a public input to select the appropriate verification path at proof generation time:
The SDK automatically selects the appropriate proof type based on what is available for the credential. If the Verifier does not explicitly specify a proofType (by setting it to 0), the SDK will automatically utilize the available proof, prioritizing MTP when both are available. This prioritization reflects the fact that MTP proofs are generally more gas-efficient for on-chain verification scenarios, because the Merkle root is already anchored on-chain and does not require the verifier contract to perform an in-circuit signature verification.
Developer Impact
This unification simplifies integration for developers building AI agents in two concrete ways. First, a single validator contract handles both proof types, so the agent’s backend does not need conditional routing logic. Second, existing verifiable credentials do not need to be re-issued when switching proof types; only the zero-knowledge proof needs to be regenerated against the unified circuit. The credential data itself remains unchanged.
Last updated