[GroupSignature Circuit] Circom and SnarkJS

[GroupSignature Circuit] Circom and SnarkJS
none 0.0 0

Introduction

Zero-knowledge proofs (ZKPs) enable privacy-preserving verification of information, and Circom is a popular tool for building ZK circuits. In this tutorial, we will implement a Group Signature verification scheme. The goal of this circuit is to allow a prover to prove membership in a group of public keys without revealing which specific key (or secret key) they possess.

This concept is often used in anonymity-preserving cryptography (like ring signatures or group signatures) to prove that a user belongs to a group without revealing their specific identity. It’s similar to how Zcash’s zk-SNARKs work, enabling privacy in transactions.

Key Concepts:

Group Signatures

A group signature allows a member of a group to sign a message anonymously on behalf of the group. The verifier can confirm that a valid signature was made by someone from the group but won’t know which member signed it.
This concept is used in:

  • Privacy-preserving payments (like Zcash or Tornado Cash)
  • Anonymous voting in decentralized governance
  • Proof of Membership for access control in zero-knowledge systems

How This Circuit Works

  1. Key Generation:
  • Each member of the group generates a public key (pk) from a secret key (sk).
  • Public keys are computed using a hash function (Mimc Sponge) applied to sk.
  1. Membership Proof:
  • The prover provides a secret key (sk).
  • The circuit computes the corresponding public key (pk).
  • The circuit checks if this pk matches one of the provided group public keys (pk1, pk2, pk3).
  • If the public key matches one of the known public keys, the proof is valid, proving that the prover is a member of the group.

ZK Concepts

  1. Prover and Verifier: The prover generates a proof that the verifier can use to confirm a statement’s validity without learning any details.
  2. Proof: A piece of cryptographic evidence that convinces the verifier.
  3. Witness: Hidden input data used to construct the proof.
  4. Circuit: A set of logical steps used to create a proof. It defines how the inputs relate to the outputs.
  5. Constraint System: Rules combining inputs using mathematical and cryptographic operations.
  6. Trusted Setup: A one-time process to generate cryptographic keys for the prover and verifier to use.

The combination of these elements allows zero-knowledge proofs to provide privacy, security, and verification without revealing sensitive information.

To learn more, read this introduction

Introduction to Zero-Knowledge Proofs

Tools and Environment Setups.

To build and run ZKPs, you’ll need the following tools installed on your system:
Note: you can use zkREPL as a faster and easier web based editor with built in circom compiler.

  1. Node.js: JavaScript runtime to run SnarkJS scripts.
  2. Rust: Used to compile Circom.
  3. Circom: A specialized programming language for ZK circuits.
  4. SnarkJS: A JavaScript library for generating and verifying ZKP proofs.

Step 1: Install Prerequisites

For installation and project setup, check out Part1 of this section in the series.

Building the Circuit

Create a file called groupsignature in the circuits/ folder.

groupsignature.circom


pragma circom 2.1.6;

include "circomlib/mimcsponge.circom";


template GroupSig () {

    signal input sk;


    signal input pk1;
    signal input pk2;
    signal input pk3;


    component pkGen = MiMCSponge(1, 220, 1); // has one input signal `in`, and one input signal `out`
    pkGen.ins[0] <== sk;
    pkGen.k <== 0;

    signal pk;
    pk <== pkGen.outs[0];

    signal intern;
    intern <== (pk - pk1)*(pk - pk2);
    intern * (pk - pk3) === 0;

    
}

component main { public [ pk1,pk2, pk3] } = GroupSig();

/* INPUT = {
    "sk": "32",
    "pk1": "55",
    "pk2": " 3cb6bc651b03d994974629eacef9696311d85c2ca9e3d08cbdf01e1833c2a405",
    "pk3": "50",
} */

Code Walkthrough

Imports

include "circomlib/mimcsponge.circom";
  • The circuit imports MiMCSponge, a cryptographic hash function optimized for zero-knowledge proofs.
  • MiMCSponge allows you to generate cryptographic hashes using an internal sponge construction.

Signals Declaration

signal input sk;     // The prover's secret key (private input)
signal input pk1;    // Public key 1 (known member of the group)
signal input pk2;    // Public key 2 (known member of the group)
signal input pk3;    // Public key 3 (known member of the group)
  • Inputs:
    • sk: The prover’s private key. This is a secret input that only the prover knows.
    • pk1, pk2, pk3: The public keys of known members in the group. These are the “known members” the prover wants to prove they belong to.

Public Inputs:

component main { public [ pk1, pk2, pk3 ] } = GroupSig();
  • These three public keys are available for the verifier to see, and they define the “group” of users to which the prover must prove membership.

Key Generation

component pkGen = MiMCSponge(1, 220, 1); 
pkGen.ins[0] <== sk;   // The secret key `sk` is input to the sponge
pkGen.k <== 0;         // The constant k (sometimes used as salt) is set to 0
  • MiMCSponge is used to compute the public key pk from the secret key sk.
  • The sponge takes 1 input (the secret key) and generates 1 output (the public key).
  • Why use MiMCSponge? It’s zk-friendly and allows hashing of the secret key to generate a public key, similar to how Ethereum addresses are generated from private keys.

Public Key Check

signal pk;
pk <== pkGen.outs[0];  // Output from MiMCSponge is the public key (pk)
  • The output of the MiMCSponge is assigned to pk, which is the prover’s computed public key.

Membership Proof

signal intern;
intern <== (pk - pk1) * (pk - pk2);
intern * (pk - pk3) === 0;
  • The prover proves that their pk is equal to pk1, pk2, or pk3 using the following logic:
(pk - pk1) * (pk - pk2) * (pk - pk3) === 0

How it works

  1. If pk matches one of pk1, pk2, or pk3, then one of the terms becomes zero.
  • For example, if pk = pk2, then:
(pk - pk1) * (0) * (pk - pk3) = 0
  1. This ensures that at least one of the terms is zero, making the whole product zero.
  2. If pk does not match any of the three public keys, the product will be non-zero.

How it Works (Example)

Let’s walk through an example of how this works.

Prover (User)

  1. sk = 32 (this is the user’s private key, secret to the prover).
  2. The circuit computes pk using MiMCSponge:
pk = MiMCSponge(sk)

Verifier (Group)

The verifier has 3 public keys:

pk1 = 55
pk2 = 3cb6bc651b03d994974629eacef9696311d85c2ca9e3d08cbdf01e1833c2a405
pk3 = 50

Proof Check

  1. The circuit computes:
(pk - pk1) * (pk - pk2) * (pk - pk3) === 0
  1. If pk matches one of these public keys, the result will be 0.
  2. If pk does not match any of them, the result will be non-zero, and the proof will fail.

Compiling the Circuit

Run the following command to compile the circuit:

circom circuits/groupsignature.circom --r1cs --wasm --sym -o output/

Let’s look at each file:

  • –r1cs: Generates the Rank 1 Constraint System (R1CS) file.

  • –wasm: Outputs a WebAssembly file for computation.

  • –sym: Outputs a symbol file for debugging.

  • -o output/: Specifies the output directory.

Check for the following files in output/:

  • groupsignature.r1cs: The constraint system file.

  • groupsignature.wasm: The WebAssembly file.

  • groupsignature.sym: The symbol file for debugging.

Generating Trusted Setup

Generate the keys for the prover and verifier:

wget https://hermez.s3-eu-west-1.amazonaws.com/powersOfTau28_hez_final_10.ptau -O outputs/pot10.ptau
snarkjs groth16 setup outputs/groupsignature.r1cs outputs/pot10.ptau outputs/groupsignature_final.zkey

Export the Verification Key

snarkjs zkey export verificationkey outputs/groupsignature_final.zkey outputs/verification_key.json

Creating Inputs

Create an input file input/input.json with the following content:

{
     "sk": "32",
    "pk1": "55",
    "pk2": " 3cb6bc651b03d994974629eacef9696311d85c2ca9e3d08cbdf01e1833c2a405",
    "pk3": "50",
}

These Inputs will be used to generate the proof.

Generating the Proof

  1. Generate Witness

node outputs/groupsignature_js/generate_witness.js outputs/groupsignature_js/groupsignature.wasm inputs/input.json outputs/witness.wtns

  1. Generate the Proof

snarkjs groth16 prove outputs/groupsignature_final.zkey outputs/witness.wtns outputs/proof.json outputs/public.json
  1. Verify the Proof

snarkjs groth16 verify outputs/verification_key.json outputs/public.json outputs/proof.json

If everything is setup correctly, you should see:

snarkJS: OK!

Summary

  1. Design the Circuit: Create the logic of the proof in a Circom file.

  2. Compile the Circuit: Convert the logic to constraint systems and WebAssembly files.

  3. Generate Trusted Setup: Produce keys for the prover and verifier.

  4. Input Data: Provide the inputs for the computation.

  5. Generate Witness: Produce intermediate computation results.

  6. Generate Proof: Create a proof from the witness.

  7. Verify Proof: Use the verifier to check the proof’s validity.

Use Cases

  • Anonymous Voting: Prove you are a registered voter without revealing your identity.
  • Anonymous Access Control: Prove you are on an access list without revealing which user you are.
  • Ring Signatures: Sign a message as part of a group, but keep your identity private (like in Monero).
  • Private Payments: Send payments from a known list of contributors while hiding the contributor’s identity.

Conclusion

Congratulations! You’ve completed a hands-on implementation of zero-knowledge proofs. This process may seem complex initially, but as you create more advanced circuits, you’ll see how useful ZKPs can be for privacy-preserving computation.

We encourage you to continue your exploration of topics on zk proofs. If you’re interested in diving deeper, you can follow up on the pathway here Zero-Knowledge Proofs on the Celo Blockchain: A Comprehensive Tutorial Series - Pathways - Celo Academy

Resources

Circom docs
SnarkJS
0xparc Circom Workshop

About the author

I’m Jonathan Iheme, A full stack block-chain Developer from Nigeria. With a great passion for Zero Knowledge Technology.

linkedIn
Twitter