Skip to content

vkpatva/zkp-commitment-scheme

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Privacy-First Audit Verification System

A Node.js proof-of-concept demonstrating zero-knowledge proof techniques for verifying financial totals without revealing individual entry values. Perfect for privacy-preserving audits where a client proves their total to an auditor without disclosing sensitive transaction details.

Overview

This system uses:

  • Pedersen Commitments - Cryptographically hide individual entry values
  • Homomorphic Addition - Sum commitments without decrypting
  • Schnorr Proofs via Fiat-Shamir - Non-interactive zero-knowledge proof that the client knows the committed values

Scenario

  1. Client (User): Enters multiple financial entries (e.g., transactions, P&L items). Each entry is committed using a Pedersen Commitment. The commitments are summed homomorphically.
  2. NIZK Generation: A Schnorr proof is generated using the Fiat-Shamir transformation to prove knowledge of the total value and blinding factors.
  3. Auditor (Verifier): Receives only the total commitment and proof. Verifies the proof without seeing individual entries.

Mathematical Foundation

Parameters (Demo)

  • Prime modulus: p = 89
  • Generator for values: g = 3
  • Generator for blinding: h = 7
  • Group order: 88 (p - 1)

Pedersen Commitment

C = g^v · h^r (mod p)

Where v is the value and r is a random blinding factor.

Homomorphic Property

C_total = ∏ C_i (mod p)

The product of commitments equals the commitment of the sum.

Schnorr Proof (Fiat-Shamir)

Client (Prover):

  1. Pick random k_v, k_r
  2. Compute announcement: a = g^k_v · h^k_r (mod p)
  3. Compute challenge: e = H(C_total, a, v_total)
  4. Compute responses: z_v = k_v + e·v_total (mod order), z_r = k_r + e·r_total (mod order)

Auditor (Verifier):

  1. Recompute challenge: e = H(C_total, a, v_total)
  2. Verify: g^z_v · h^z_r ≡ a · C_total^e (mod p)

Step-by-Step Proof Generation (Detailed)

Here's the exact sequence of how the NIZK proof is generated:

┌─────────────────────────────────────────────────────────┐
│  CLIENT already has these from the entries:             │
│    • C_total (product of all commitments)               │
│    • v_total (sum of entry values - public)             │
│    • r_total (sum of blinding factors - SECRET)         │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│  STEP 1: Pick random k_v and k_r                        │
│    • k_v = random()  (kept secret)                      │
│    • k_r = random()  (kept secret)                      │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│  STEP 2: Compute announcement 'a'                       │
│    • a = g^k_v × h^k_r mod p                            │
│    This is a "commitment to randomness"                 │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│  STEP 3: Compute challenge 'e' via Fiat-Shamir hash     │
│    • e = Hash(C_total, a, v_total) mod order            │
│                                                         │
│    Note: Uses v_total (public value), NOT z_v!          │
│    The client cannot predict 'e' before committing 'a'  │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│  STEP 4: Compute responses z_v and z_r                  │
│    • z_v = k_v + (e × v_total) mod order                │
│    • z_r = k_r + (e × r_total) mod order                │
│                                                         │
│    These "blend" the random k values with secrets,      │
│    making them verifiable but not reversible.           │
└─────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────┐
│  PROOF SENT TO AUDITOR:                                 │
│    • C_total    - Total commitment                      │
│    • a          - Announcement                          │
│    • e          - Challenge                             │
│    • z_v        - Response for value                    │
│    • z_r        - Response for blinding factor          │
│    • v_total    - Claimed total value                   │
│    • entryCount - Number of entries                     │
│                                                         │
│    Individual entry values remain PRIVATE!              │
└─────────────────────────────────────────────────────────┘

Key Insight: The order is a → e → z_v, z_r (no circular dependency). The challenge e is computed from the hash of (C_total, a, v_total) BEFORE computing z_v and z_r.

Why This Works:

  • The client commits to random a first
  • Challenge e is unpredictably derived from a via hash
  • Responses z_v and z_r are "blinded" by random k_v and k_r
  • The auditor cannot extract secrets, but the verification equation only passes if the client knows the real values

Installation

# No external dependencies required - uses only Node.js built-ins
npm install  # (optional, no dependencies)

Usage

Interactive Mode

npm start
# or
node index.js

Commands

Command Description
[number] Enter an audit entry amount (positive or negative)
status View all entries and commitments
finish Generate the NIZK proof
audit Enter Auditor Mode (verification view)
reset Clear all entries
help Show available commands
exit Quit the application

Example Session

Client> 100
--- Entry Committed ---
   Entry #1
   Value: 100
   Blinding Factor (r): 45
   Commitment (C): 23

Client> 50
--- Entry Committed ---
   Entry #2
   Value: 50
   ...

Client> -30
--- Entry Committed ---
   Entry #3
   Value: -30
   ...

Client> finish
--- NIZK PROOF GENERATED ---
   Total Commitment: 56
   Announcement: 31
   Challenge: 42
   ...

Client> audit
--- AUDITOR MODE (Verification View) ---
   [AUDITOR] Verifying proof...
   VERIFICATION RESULT: SUCCESS
   [AUDITOR] Verified total: 120
   [AUDITOR] Individual entry values remain PRIVATE.

Automated Test

npm test
# or
node test.js

Project Structure

├── index.js    # Main interactive application
├── test.js     # Automated test script
├── package.json
└── README.md

Security Notes

This is a POC for educational purposes only:

  • Uses small parameters (p = 89) for demonstration
  • Production systems require:
    • Large primes (2048+ bits)
    • Cryptographically secure parameter generation
    • Additional security hardening

How It Achieves Privacy

  1. Individual entries are hidden: Each entry is wrapped in a commitment that cannot be opened without the blinding factor.
  2. Homomorphic aggregation: Commitments can be combined mathematically without revealing underlying values.
  3. Zero-knowledge proof: The auditor is convinced the client knows the total value without learning anything else about individual entries.

Use Cases

  • Financial Audits: Prove total revenue/expenses without revealing individual transactions
  • Tax Compliance: Verify aggregate amounts while keeping transaction details private
  • Supply Chain: Prove total quantities without exposing supplier relationships
  • Healthcare: Verify aggregate patient data without exposing individual records

License

ISC

About

ZKP commitment scheme practice

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors