DEV Community

wellallyTech
wellallyTech

Posted on

Your Health, Your Secrets: How to Prove Vaccination Status Without Leaking a Single Byte (ZKP Guide)

Privacy is the new gold. In an era where digital identity is everything, why do we still hand over our entire medical history just to prove a single fact? Whether it’s entering a venue or crossing a border, the current "show your QR code" method leaks your name, date of birth, and specific medical records. πŸ›‘οΈ

Today, we are diving deep into Zero-Knowledge Proofs (ZKP) and Privacy Computing. By using Circom and SnarkJS, we will build a system where you can prove "I have been vaccinated" without revealing which vaccine you got or when you got it. This is the future of Blockchain Identity and secure health data sharing.

The Architecture of Privacy

The magic of ZK-SNARKs (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge) lies in the separation of the Prover and the Verifier. The Prover uses their private data to generate a mathematical proof, and the Verifier checks that proof against a public "circuit" without ever seeing the raw data.

Data Flow for a Private Health Claim

sequenceDiagram
    participant User as Prover (User)
    participant Circuit as Circom Circuit
    participant Verifier as Verifier (Smart Contract/Server)

    Note over User: Private Input: VaccineID, Secret Key
    Note over User: Public Input: Hash(VaccineID)

    User->>Circuit: Input private & public signals
    Circuit->>User: Generate Proof (.json) + Public Signals

    User->>Verifier: Submit Proof + Public Signals

    Note over Verifier: Verify math, not data
    Verifier-->>User: Logic Result (TRUE/FALSE)
    Note right of Verifier: "Proof is valid. Access granted!"
Enter fullscreen mode Exit fullscreen mode

Prerequisites

To follow this advanced tutorial, ensure you have the following tech_stack ready:

  • Circom: The domain-specific language for writing ZK circuits.
  • SnarkJS: The JavaScript implementation to generate and verify proofs.
  • Node.js: For running our verification logic.
  • Identity Protocols: Understanding of how public keys represent identity.

Step 1: Writing the Circom Circuit

We need to create a circuit that proves we know a vaccineSerial that, when hashed with a secret, matches a publicly known identityCommitment. This ensures the proof belongs to you and the vaccine is valid.

pragma circom 2.0.0;

include "node_modules/circomlib/circuits/poseidon.circom";

template VaccineProof() {
    // Private inputs (the world doesn't see these!)
    signal input vaccineSerial;
    signal input userSecret;

    // Public inputs
    signal input identityCommitment;

    // Components
    component hasher = Poseidon(2);

    // Logic: Hash the serial and the secret
    hasher.inputs[0] <== vaccineSerial;
    hasher.inputs[1] <== userSecret;

    // Constraint: The result must match the public commitment
    identityCommitment === hasher.out;
}

component main {public [identityCommitment]} = VaccineProof();
Enter fullscreen mode Exit fullscreen mode

Step 2: Generating the Proof with SnarkJS

Once our circuit is compiled, we need to generate a "witness." Think of the witness as the set of values that satisfies the mathematical equations in our circuit. 🧬

const snarkjs = require("snarkjs");
const fs = require("fs");

async function run() {
    // 1. Define the inputs
    const inputs = {
        vaccineSerial: "987654321", // Private
        userSecret: "my_super_secret_key", // Private
        identityCommitment: "1928374655123..." // Public
    };

    // 2. Generate Proof
    const { proof, publicSignals } = await snarkjs.groth16.fullProve(
        inputs, 
        "vaccine_proof.wasm", 
        "circuit_final.zkey"
    );

    console.log("Proof Generated: ", JSON.stringify(proof, null, 1));

    // 3. Export for the Verifier
    fs.writeFileSync("proof.json", JSON.stringify(proof));
    fs.writeFileSync("public.json", JSON.stringify(publicSignals));
}

run().then(() => process.exit(0));
Enter fullscreen mode Exit fullscreen mode

Step 3: Verification (The "Zero-Knowledge" Moment)

The Verifier receives the proof.json and public.json. They have no idea what the vaccineSerial is, but the math guarantees that the Prover must know it to have generated that specific proof.

const res = await snarkjs.groth16.verify(vKey, publicSignals, proof);

if (res === true) {
    console.log("βœ… Verification OK: Access Granted to Health Records!");
} else {
    console.log("❌ Verification Failed: Invalid Proof.");
}
Enter fullscreen mode Exit fullscreen mode

The "Official" Way: Scaling Privacy

While building a local ZK proof is a great learning exercise, production-grade systems require robust infrastructure for key management, recursive proofs, and on-chain verification.

If you're looking for more production-ready examples and advanced patterns in privacy-preserving architectures, I highly recommend checking out the technical deep-dives at WellAlly Tech Blog. They cover how to integrate these ZK-SNARK patterns into enterprise health systems and the latest updates in decentralized identity (DID).


Conclusion: Privacy is a Feature, Not an Afterthought

By utilizing ZKP, we shift the paradigm from "Trust me, here is my data" to "Don't trust me, verify the math." This implementation using Circom and SnarkJS is just the tip of the iceberg. As blockchain identity protocols mature, we will see this logic applied to financial credit scores, age verification, and even anonymous voting. πŸ—³οΈ

What’s your take on ZKP? Is it the silver bullet for data privacy, or is the computational overhead still too high for mainstream mobile apps? Let’s chat in the comments! πŸ‘‡

Top comments (0)