Reference

Circuit Reference

Technical specification for all 19 Noir circuits.

Apertrue uses 19 Noir circuits. This section is a reference catalogue — each circuit's purpose, public inputs, outputs, and approximate constraint count. All circuits compile to BN254 and produce UltraHonk proofs (500 fields) unless otherwise noted.

Note
Public inputs are what the verifier sees. Private inputs (the witness) are known only to the prover. A circuit proves a relationship between its public and private inputs without revealing the private data.

Circuit map

Circuit dependency graph: Three tiers — Split Proofs (ProofA, ProofB) feed into Aggregation (ImageAggregator combines both, TreeAggregator recursively combines pairs) which feeds into On-chain (ApertrueVerifier Aztec contract). External Credentials (JWT Identity connects to ApertrueVerifier, Anonymous Credential and Selective Disclosure are standalone). WebAuthn Account authorises all Aztec transactions.
CircuitCategoryApprox. constraintsPurpose
ProofA (generic)Split proof80k–220kCertificate chain verification + graduated privacy disclosure
ProofA ECDSA P-256Split proof~120kP-256 only (ChatGPT, DALL-E, ProofMode)
ProofA ECDSA P-384Split proof~150kP-384 only (Google Pixel, Truepic)
ProofA RSA-2048Split proof~180kRSA-2048 only (standard RSA C2PA)
ProofA RSA-4096Split proof~220kRSA-4096 only (Adobe Photoshop, Lightroom)
ProofA SkipSplit proof~80k2-cert chains (leaf signed by root, no intermediate)
ProofB (generic)Split proof100k–160kCOSE signature verification + claim binding
ProofB ES256Split proof~100kECDSA COSE signatures only
ProofB PS256Split proof~160kRSA-PSS COSE signatures only
ImageAggregatorAggregation~100kCombines ProofA + ProofB into single image proof
TreeAggregatorAggregation~50kBinary tree — combines 2 proofs into 1 (recursive)
Selective DisclosurePrivacy~50kProve claims about verified image without revealing it
JWT IdentityIdentity~80kVerify org membership from JWT without revealing email
Anonymous CredentialIdentity~40kSemaphore-style Merkle membership with nullifier
Credential RegistrationIdentity~10kProve knowledge of secret behind commitment
WebAuthn AccountAztec contractN/APasskey + session key dual-path authorisation
ApertrueVerifierAztec contractN/AOn-chain proof verification and note storage

Split proofs

Every C2PA image generates two proofs that are later combined. ProofA handles certificate chain verification and metadata extraction. ProofB handles COSE signature verification and claim binding. Splitting the workload across two circuits keeps each circuit's constraint count manageable for browser proving.

ProofA — certificate chain + privacy disclosure

ProofA verifies that a C2PA manifest was signed by a certificate chaining to a trusted intermediate CA, extracts metadata (location, time, device, actions), and applies graduated privacy disclosure. The generic circuit handles all five algorithm variants via a selector. The specialised variants (P-256, P-384, RSA-2048, RSA-4096, Skip) are faster because they omit unused code paths.

Public inputTypePurpose
trust_list_rootFieldOracle Merkle root of trusted intermediate CAs
content_hashFieldSHA-256 of stripped image (metadata removed)
nullifierFieldReplay prevention — hash of content + leaf key
proof_timestampFieldUnix seconds when proof was generated
cert_not_beforeFieldCertificate validity start
cert_not_afterFieldCertificate validity end
time_minFieldGraduated time disclosure — lower bound (Unix seconds)
time_maxFieldGraduated time disclosure — upper bound (Unix seconds)
center_lat_scaledFieldPrivacy circle centre latitude (scaled 10^7)
center_lon_scaledFieldPrivacy circle centre longitude (scaled 10^7)
location_flagsu8Bit 0: lat negative (South), Bit 1: lon negative (West)
radius_squared_scaledFieldPrivacy radius squared (scaled units)
tbs_hash_commitmentFieldPoseidon2 commitment of TBS certificate hash
claim_hash_commitmentFieldPoseidon2 commitment of claim hash
content_hash_offsetu32Byte offset of content hash in claim
Public outputTypePurpose
link_commitFieldDevice identity binding — links ProofA to ProofB
location_commitmentFieldPoseidon2([lat, lon, lat_sign, lon_sign, salt])
time_commitmentFieldPoseidon2([timestamp, salt])
actions_assertion_hash[u8; 32]SHA-256 of C2PA action history

The circuit proves that the exact coordinates fall within the disclosed privacy circle (centre + radius), the exact timestamp falls within the disclosed time range (min/max), and the certificate chains to a trusted intermediate in the Oracle's Merkle tree.

ProofB — COSE signature + claim binding

ProofB verifies the COSE signature over the C2PA Sig_structure and confirms that the claim bytes are embedded within it. The generic circuit handles both ES256 (ECDSA) and PS256 (RSA-PSS) via a selector. ProofB ES256 and ProofB PS256 are specialised variants.

Public inputTypePurpose
trust_list_rootFieldOracle Merkle root (must match ProofA)
content_hashFieldSHA-256 of stripped image (must match ProofA)
nullifierFieldReplay prevention (must match ProofA)
proof_timestampFieldUnix seconds (must match ProofA)
cert_not_beforeFieldCertificate validity start (from ProofA)
cert_not_afterFieldCertificate validity end (from ProofA)
claim_hash_commitmentFieldPoseidon2 commitment of claim hash (must match ProofA)
content_hash_offsetu32Byte offset of content hash in claim
Public outputTypePurpose
link_commitFieldDevice identity binding (must match ProofA)

Six public inputs are shared between ProofA and ProofB and must match exactly: trust_list_root, content_hash, nullifier, proof_timestamp, claim_hash_commitment, and link_commit. The ImageAggregator enforces this — if any value differs, aggregation fails.

Algorithm variants

The browser selects the circuit variant based on the C2PA manifest's signature algorithm. Specialised circuits are 30–60% faster than the generic circuit because they compile only the code paths for one algorithm:

VariantAlgorithmCommon devices/tools
ECDSA P-256SHA-256 + secp256r1ChatGPT/DALL-E, ProofMode, Truepic (older)
ECDSA P-384SHA-384 + secp384r1Google Pixel 8/9, Truepic (newer)
RSA-2048SHA-256 + PKCS#1 v1.5Standard RSA C2PA implementations
RSA-4096SHA-256 + PKCS#1 v1.5Adobe Photoshop, Lightroom, Firefly
Skip (2-cert)None (trust via Merkle)2-certificate chains where leaf is signed directly by root

Aggregation circuits

ImageAggregator — single image

The ImageAggregator recursively verifies a ProofA and ProofB inside a new circuit, producing a single proof that represents a fully verified image. It checks that all shared public inputs match between the two split proofs and computes an image commitment from the verified data.

Public inputTypePurpose
trust_list_rootFieldOracle Merkle root
content_hashFieldSHA-256 of stripped image
nullifierFieldReplay prevention
proof_timestampFieldUnix seconds
Public outputTypePurpose
location_commitmentFieldFrom ProofA (passed through)
time_commitmentFieldFrom ProofA (passed through)
edit_time_commitmentFieldFrom ProofA (passed through, 0 if no edit)
link_commitFieldDevice identity binding (verified matching)
actions_hash_fieldFieldPoseidon2 reduction of 32-byte actions hash
image_commitmentFieldPoseidon2 of all 9 verified values
Image commitment computation
image_commitment = Poseidon2([
  trust_list_root,
  content_hash,
  nullifier,
  proof_timestamp,
  location_commitment,
  time_commitment,
  edit_time_commitment,
  link_commit,
  actions_hash_field
], 9)

The image commitment is a single field element that uniquely identifies a verified image with all its metadata. This commitment is the input to the tree aggregation layer.

TreeAggregator — batch combination

The TreeAggregator combines two proofs into one. It is reusable at every level of the binary tree — it can combine two ImageAggregator proofs, two TreeAggregator proofs, or one of each. The final TreeAggregator proof represents the entire batch.

Public inputTypePurpose
pad_0 through pad_8Field (all zero)Padding to match ImageAggregator public input layout
Public outputTypePurpose
combined_commitmentFieldPoseidon2([left_commitment, right_commitment])

The padding fields exist so that the TreeAggregator's public input layout matches the ImageAggregator's — both circuits have the same number of public inputs. This simplifies recursive verification because the verifier doesn't need to distinguish between circuit types.

Key Insight
The aggregation tree is binary. A batch of n images requires n ImageAggregator proofs and n−1 TreeAggregator proofs. The final root proof is submitted to the Aztec contract.

Privacy circuits

Selective Disclosure

The Selective Disclosure circuit lets a user prove specific claims about a verified image (location range, time range, device, actions) without revealing the image itself. The prover chooses which fields to disclose — hidden fields are set to zero.

Public inputTypePurpose
root_commitmentFieldBatch Merkle root (verified on-chain)
nullifierFieldBatch nullifier
contract_addressFieldAztec contract address
disclosed_content_hashFieldImage identity (0 if hidden)
disclosed_trust_list_rootFieldOracle root at proof time (0 if hidden)
disclosed_proof_timestampFieldWhen proof was generated (0 if hidden)
disclosed_lat_min / lat_maxFieldLatitude bounding box (0 if hidden)
disclosed_lon_min / lon_maxFieldLongitude bounding box (0 if hidden)
disclosed_location_flagsFieldHemisphere indicators (0 if hidden)
disclosed_time_min / time_maxFieldTime range (0 if hidden)
disclosed_link_commitFieldDevice identity (0 if hidden)
disclosed_actions_hashFieldC2PA action history (0 if hidden)

The circuit verifies a Merkle path from the image commitment to the batch root commitment (8 levels, supporting up to 256 images per batch). For each disclosed field, it proves that the disclosed value is consistent with the original committed data — the prover cannot fabricate disclosed values.

Identity circuits

JWT Identity

The JWT Identity circuit verifies an OIDC JWT from any provider (Google, Microsoft, Okta) and proves the user's email domain without revealing the full email address. Built on ZKEmail's noir-jwt.

Public inputTypePurpose
domainBoundedVec<u8, 64>Organisation domain to verify (e.g., "reuters.com")

Private inputs include the JWT payload (partial SHA mode for efficiency), the RSA-2048 public key, and the signature. The circuit verifies the RSA signature, parses the JWT to find the email claim, confirms email_verified == true, and checks that the domain after the @ matches the public input.

Anonymous Credential

A Semaphore-style credential circuit. Proves membership in a Merkle tree of identity commitments with a scoped nullifier for rate limiting. Uses Baby Jubjub elliptic curve arithmetic from noir-edwards and Poseidon hashing from zk-kit.noir.

Public inputTypePurpose
hashed_scopeFieldScoped nullifier domain (e.g., app identifier)
hashed_messageFieldMessage to sign (prevents proof malleability)
Public outputTypePurpose
rootFieldMerkle root of identity commitment tree
nullifierFieldPoseidon([scope, secret]) — deterministic per scope

The Merkle tree supports up to 20 levels (approximately 1 million identities). The nullifier is deterministic per secret per scope — using the same credential twice in the same scope produces the same nullifier, enabling rate limiting without revealing identity.

Credential Registration

A lightweight circuit that proves knowledge of the Baby Jubjub secret key behind a Poseidon commitment. Used during credential registration to verify the user controls the secret before adding the commitment to the tree.

Public inputTypePurpose
commitmentFieldPoseidon2([pubkey.x, pubkey.y])
Public outputTypePurpose
computedFieldRecomputed commitment (must match input)

Aztec contracts

Two Aztec contracts handle on-chain operations. These are Noir programs compiled for the Aztec Virtual Machine rather than standalone ZK circuits.

WebAuthn Account

The user's Aztec account contract. Supports dual-path authorisation via a discriminator at witness[0]:

PathDiscriminatorAuth methodUse case
WebAuthn0ECDSA P-256 (passkey)Initial setup, high-value operations
Session key1Schnorr (Grumpkin)Proof submissions during active session

The WebAuthn path verifies a P-256 ECDSA signature over the transaction hash, with the authenticator data and client data JSON as witness inputs. The session key path verifies a Schnorr signature from an ephemeral Grumpkin key pair authorised by the user's passkey. Session keys have a configurable expiry and scope, stored as encrypted notes in the contract's private state.

ApertrueVerifier

The verification contract. Receives the final TreeAggregator proof and creates private notes representing verified media. Also stores identity proofs (JWT and passport).

FunctionInputCreates
verify_and_storeTreeAggregator proof + VKVerifiedMediaNote (root_commitment, image_count, trust_list_root, epoch_week)
store_identityJWT identity proof hash + domain hashIdentityNote
store_passport_identityPassport proof hash + nullifier hashPassportIdentityNote

Shared cryptographic primitives

Across all circuits, the following primitives are used:

PrimitiveUsed inPurpose
Poseidon2All circuitsField-native hashing — commitments, Merkle trees, nullifiers
SHA-256ProofA, ProofB, ImageAggContent hashing, certificate TBS hashing, claim hashing
SHA-384ProofA P-384TBS certificate hashing for P-384 certificates
PedersenProofA, ProofBTrust list intermediate leaf hashing (Oracle compatibility)
ECDSA P-256ProofA, ProofB, WebAuthn AccountC2PA certificate verification, passkey authorisation
ECDSA P-384ProofA P-384Google Pixel / Truepic certificate verification
RSA-2048ProofA RSA, ProofB PS256, JWT IdentityC2PA certificate verification, JWT signature verification
RSA-4096ProofA RSA-4096Adobe certificate verification
Schnorr (Grumpkin)WebAuthn AccountSession key authorisation
Baby JubjubAnon Credential, RegistrationCredential key derivation

Constraint budget

Constraint count determines proving time. In the browser (Barretenberg WASM), rough benchmarks on mid-range hardware:

ConstraintsBrowser proving timeMemory
~50k3–5 seconds~300 MB
~100k6–10 seconds~400 MB
~150k10–15 seconds~400 MB
~220k15–25 seconds~500 MB

The split proof architecture exists specifically because of these constraints. A single monolithic circuit covering all of ProofA + ProofB would exceed 300k constraints and require 30+ seconds of proving time with ~600 MB of memory — impractical for browser use. Splitting into two circuits and proving them in parallel on separate Web Workers halves the wall-clock time.

Key Insight
All proofs are 500 fields (UltraHonkZKProof). This is a fixed size regardless of circuit complexity — a 10k-constraint credential registration proof and a 220k-constraint RSA-4096 proof produce identically sized proofs. Verification time is also constant.

Sensor line circuits

The sensor fingerprint line has its own circuit family, built and proven on the phone rather than in the browser. Measured on an iPhone 13 Pro Max:

CircuitSizeOn-deviceWhat it proves
Threshold membership~170k gates4.4 s proveA fresh capture matches the enrolled keyed template within the accept threshold, in zero knowledge
zk_attest~322k gates8.5 s prove · 2.0 s verifyThe device's Apple-issued App Attest credential is genuine, without contacting Apple or revealing it
Delegation~415k gatesonce, at enrolmentLocally minted per-capture assertions chain back to that single attestation, keeping captures unlinkable

The circuit family is public at Apertrue/sensor-circuits, and the protocol behind it is specified in Prove the Camera, Not the Cloud.

The final section covers the security model — threat analysis, trust assumptions, and the guarantees the system provides.