Skip to main content
Ciphera is fundamentally built on separating verification from disclosure using Zero-Knowledge succinct Non-interactive ARguments of Knowledge (zk-SNARKs).

Cryptographic Stack

ComponentChoice & Rationale
Proof SystemPLONK — no per-circuit trusted setup, reuses Aztec Ignition ceremony. Circuit updates don’t require re-ceremony.
Elliptic CurveBN254 — ~145,000 opcode budget on Algorand Virtual Machine (AVM) vs ~185,000 for BLS12-381. Cheaper, faster, sufficient security.
Verifier TypeLogicSig — max budget 320,000 opcodes vs 190,400 for smart contracts. Preserves smart contract budget for app logic.
Nullifier HashPoseidon — ZK-friendly, dramatically fewer circuit constraints than SHA-256.
Merkle Tree HashMiMC — ZK-optimized, ideal for tree operations inside circuits.
Merkle Tree TypeSparse Merkle Tree — O(1) leaf updates enable efficient credential revocation without tree rebuild.

Data Flow: Proof Generation

The core promise of Ciphera is that raw data never leaves the user’s browser.
  1. Local Parsing: The user’s Aadhaar Offline XML is parsed purely in-memory in the browser.
  2. Local Proof Generation: snarkjs (running in WASM) generates the PLONK proof locally. The proof takes ~2-8 seconds to generate and is mathematically impossible to reverse-engineer.
  3. Nullifier Derivation: A unique, per-app identifier is derived (see Per-App Nullifier Design).
  4. Encryption (Conditional Anonymity): An encrypted identity blob is created using ECIES. Only the 3-of-5 Custodian Shamir threshold can decrypt this.
  5. Memory Wiped: The raw XML and PII are deleted from browser memory.
  6. On-Chain Anchor: Only the cryptographic proof (~200 bytes) and the encrypted box storage blob are sent to the Algorand blockchain.
Note: Ensure to add an architecture diagram image here named architecture-flow.png

On-Chain Components

Our smart contracts are written in Python (PuyaPy via AlgoKit) to take advantage of the AVM’s high throughput and low fees.
  • LogicSig Verifier: Auto-generated by AlgoPlonk from the gnark circuit. Validates the PLONK proof on the BN254 curve natively on-chain.
  • Nullifier Registry: Smart contract mapping: nullifier_hash → wallet_address. Prevents the same Aadhaar from creating multiple credentials for the same app.
  • Credential ASA: Non-transferable Algorand Standard Asset (ASA) issued to the verified wallet. Clawback enabled exclusively for revocation.
  • Box Storage: Stores the ECIES-encrypted identity blob. The key is the nullifier.
  • Sparse Merkle Root: Maintains the on-chain root of valid credentials for instant revoke() capabilities.

The ZK Circuit Design

The circuit enforces all mathematical rules. If the XML is tampered with, or the person is underage, the math simply fails and no proof is generated.

1. Aadhaar UIDAI Signature Verification

Every Aadhaar Offline XML downloaded from UIDAI is digitally signed by UIDAI’s RSA-2048 private key. Without verifying this inside the circuit, an attacker could craft a fake XML. The first circuit constraint verifies the RSA signature against the hardcoded, public UIDAI key.

2. Mandatory Claims

If the signature is valid, the circuit checks the non-negotiable KYC laws:
  • assert nationality == 'IN'
  • assert age(dob, currentDate) >= 18
  • assert kycStatus == VERIFIED
  • assert SMT.verify(nullifier, merkleRoot, merklePath)

3. Optional Constraints

If the relying app (the DeFi protocol) requires more context (like State or precise Age), the user can opt-in to reveal commitments to those fields, preserving absolute data minimization.

Per-App Nullifier Design

A global nullifier (e.g., Hash(Aadhaar)) creates a cross-app tracking identifier. An observer could see 0xABC on a DeFi app and 0xABC on an NFT marketplace, linking their activity and destroying privacy. Ciphera guarantees unlinkability by including the app_id in the derivation:
nullifier = Poseidon(aadhaar_hash, app_id, wallet_secret)
This ensures that the same physical human has a completely different, mathematically un-linkable identifier on every application they use.

Revocation: Sparse Merkle Trees

When a credential must be revoked (e.g., by a court order), the on-chain registry must update. Regular Merkle trees require a full O(n) rebuild for any update. Ciphera uses Sparse Merkle Trees (SMT). This allows O(1) leaf updates. Changing a single user’s status to REVOKED propagates up the tree in O(log n) operations, requiring only 1 Algorand transaction to permanently exile a bad actor globally from the ecosystem.