Zero-knowledge circuits for C2PA media verification, written in Noir.
These circuits prove that a photo or video has a valid C2PA signature from a trusted device — without revealing the device certificate, location, or any identifying metadata.
Apertrue uses a split-proof architecture that separates verification into two independent proofs running in parallel. This enables hardware-adaptive parallelism in the browser and supports RSA-4096 keys that exceed single-circuit constraints.
┌─────────────────┐
C2PA Image ──────▶│ ProofA │──── Certificate chain valid
│ (6 variants) │
└────────┬────────┘
│
┌────────▼────────┐
│ Image Aggregator │──── Both proofs bound to same content
└────────┬────────┘
│
┌────────▼────────┐
C2PA Image ──────▶│ ProofB │──── COSE signature valid,
│ (3 variants) │ issuer in trust list
└────────┬────────┘
│
┌────────▼────────┐
│ Tree Aggregator │──── Batch N images into 1 proof
└─────────────────┘
| Circuit | Algorithm | What It Proves |
|---|---|---|
proof_a |
Multi-algorithm | X.509 certificate chain signature is valid (supports all key types) |
proof_a_rsa_2048 |
RSA-2048 + SHA-256 | Same, optimised for 2048-bit RSA keys |
proof_a_rsa_4096 |
RSA-4096 + SHA-256 | Same, for 4096-bit keys (Adobe, ChatGPT) |
proof_a_ecdsa_p256 |
ECDSA secp256r1 | Same, for P-256 curves (Google Pixel) |
proof_a_ecdsa_p384 |
ECDSA secp384r1 | Same, for P-384 curves |
proof_a_skip |
— | Development bypass (no signature check) |
| Circuit | Algorithm | What It Proves |
|---|---|---|
proof_b |
Generic | COSE signature valid, intermediate CA in Merkle trust list |
proof_b_es256 |
ECDSA P-256 | Specialised for ES256 COSE signatures |
proof_b_ps256 |
RSA-PSS + SHA-256 | Specialised for PS256 COSE signatures |
| Circuit | Purpose |
|---|---|
image_aggregator |
Combines ProofA + ProofB for a single image with link commitment binding |
tree_aggregator |
Binary tree — combines 2 proofs into 1, reusable at any tree level |
| Circuit | Purpose |
|---|---|
selective_disclosure |
Proves image was verified without revealing private metadata (for on-chain use) |
anonymous_credential |
Group membership proof for anonymous identity credentials |
credential_registration |
Identity commitment and nullifier derivation |
jwt_identity |
JWT/OIDC claim verification for identity linking |
| Contract | Purpose |
|---|---|
aztec_verifier |
On-chain proof verification + verification record storage |
webauthn_account |
WebAuthn P-256 account contract with session key support |
Both target Aztec Network v4 testnet.
- Nargo v1.0.0-beta.18 (not stable — circuits depend on beta.18-specific features)
- Docker (for Aztec contract transpilation only)
Version constraint: These circuits require exactly
nargov1.0.0-beta.18. Later versions may introduce breaking changes to the constraint system or standard library. Install with:noirup -v 1.0.0-beta.18
# Single circuit
cd proof_a_rsa_2048
nargo compile
# All circuits (from repo root)
./scripts/build-all-circuits.shcd proof_a_rsa_2048
nargo testcd aztec_verifier
./build.sh # Full: compile → AVM transpile → VK generation → TS codegen
./build.sh --skip-compile # Reprocess without recompilingThe build script runs bb-avm aztec_process in Docker for AVM transpilation and verification key generation, then generates TypeScript bindings via @aztec/builder.
| Library | Version | Purpose |
|---|---|---|
noir_rsa |
0.10.0 (zkpassport fork) | RSA verification with PSS support |
bignum |
0.9.2 | Big integer arithmetic for RSA |
sha256 |
0.3.0 | SHA-256 hash computation |
poseidon |
0.1.1 | ZK-friendly Poseidon hash for commitments |
bb_proof_verification |
4.1.2 | Recursive proof verification (aggregation) |
The noir_rsa dependency uses zkpassport's fork which adds RSA-PSS support required for Adobe and ChatGPT-signed images. Vendored snapshots under vendor/ may use separately pinned versions for compatibility reasons (see vendor/noir-rsa/VENDORED_FROM.md).
- Certificate chains verified in-circuit — invalid signatures produce unsatisfiable constraints
- Content hash binding — prevents proof reuse across different images
- Nullifiers (content hash + leaf key hash) — prevent replay attacks
- Commitment salts never revealed — only Poseidon hashes are public outputs
- Trust list Merkle proofs — issuer inclusion proven against a signed oracle bundle
- Known limitations: Session key expiry and scope are stored in notes but not enforced in-circuit — expiry is checked client-side (documented in
webauthn_account/src/main.nr)
These circuits are built on the work of:
- Noir by Aztec Labs — zero-knowledge circuit language and compiler
- Barretenberg by Aztec Labs — UltraHonk proving backend and recursive verification
- noir_rsa by zkpassport — RSA verification with PSS support (enables Adobe and ChatGPT-signed images)
- noir-jwt by Saleel — JWT/OIDC claim verification circuits
- noir-bignum by Noir Lang — big integer arithmetic for RSA key operations
- noir-edwards by Noir Lang — Edwards curve operations for anonymous credentials
- zk-kit.noir by Privacy & Scaling Explorations (Ethereum Foundation) — binary Merkle root verification
- Poseidon, SHA-256, Schnorr, Base64 by Noir Lang — standard cryptographic primitives
- C2PA — Coalition for Content Provenance and Authenticity open standard
- Aztec Network — private smart contract platform (v4 testnet)
Apache 2.0 — Copyright 2025-2026 Apertrue Ltd. See LICENSE.
