Colofon
Book a demo →
Circuits

Circuit 2 — Developer authorisation

Status: v1 shipping. The zero-knowledge equivalent of a signed-commit policy check.

SSCoP principles: 2.1 (build-environment access control). CRA provisions: supports Article 13(1) conformity-assessment evidence retention (alongside Circuit 1). NIST SSDF practices: PS.1. DCC scope: Level 2 and Level 3 supply-chain controls.


The claim

The commit that triggered this build was signed by a member of the vendor's authorised-signer set.

Circuit 2 is the clearest illustration of why plain signatures are not sufficient for regulated compliance. A signature by default reveals the signer. "A member of this set signed this, and I am not going to tell you who" is a fundamentally zero-knowledge claim, and is the claim buyers actually need when the signer roster is sensitive personnel information.

What the proof does not claim

  • It does not say which developer signed. That is the point — the proof is zero-knowledge over signer identity.
  • It does not verify Rekor inclusion inside the circuit (the SDK does this off-circuit before witness construction, same as Circuit 1).
  • It does not verify the Fulcio certificate chain inside the circuit (same trust-boundary argument as Circuit 1; see below).
  • It does not say the commit is the one this build was produced from. That binding is enforced at the bundle-verifier level by cross-checking Circuit 2's public commit_hash_* against the commit named in Circuit 1's SLSA predicate.

Why this claim needs a circuit, not just a signature

The standard mechanisms fail in the same three directions every Colofon circuit addresses:

  • Plain gitsign / GPG commit signatures. Verifiable, but the signer identity is public on every commit. For buyers in defence or regulated fintech, the list of signing developers is classified or contractually sensitive. Publishing it with every commit — which is exactly what Sigstore gitsign does — is not acceptable.
  • "Our release engineer signs everything and we promise they are authorised." The buyer has no cryptographic check that the signer is in the vendor's real authorised-signer set; and the release engineer's identity becomes a concentrated phishing target.
  • Signer identity redacted in a human-readable report. Not cryptographic. Defeats the purpose.

The circuit produces the useful half of a plain signature check ("a legitimate signer signed this specific commit") while withholding the dangerous half ("it was Jamie").

Construction

Signed inputs

InputSourceSignature
Commit signatureVendor's developer's gitsign invocationECDSA P-256 over the commit object's git-SHA-256, via Fulcio keyless; Rekor-anchored
Fulcio certificateSigstore Fulcio CAX.509; SAN URI identifies the developer (GitHub OIDC identity)

Public inputs

  • commit_hash_high, commit_hash_low — the 32-byte SHA-256 of the signed commit, split into two BN254-compatible halves
  • authorised_signer_root — Merkle root of the buyer-committed authorised-signer identity set
  • signer_commitment — Poseidon2 hash binding the signing public key to the signer identity (prevents key/identity mixing, same shape as Circuit 1)

What the circuit checks

  1. ECDSA P-256 signature verification. The signature (sig_r, sig_s) validates under (pubkey_x, pubkey_y) for the commit digest reconstructed from the public-input halves. Identical primitive to Circuit 1.
  2. Authorised-signer Merkle membership. The private signer_identity_hash has a valid Merkle opening in the tree rooted at authorised_signer_root.
  3. Signer commitment binding. signer_commitment equals Poseidon2(pkx_high, pkx_low, pky_high, pky_low, signer_identity_hash). Without this binding, a prover could sign the commit with one identity's key (satisfies check 1) while producing a Merkle opening for a different identity (satisfies check 2) — the two private witnesses would be independent and a proof would verify for a key/identity pair that was never legitimate.

That is the entire circuit. Circuit 2 is ~80% shared infrastructure with Circuit 1 — same ECDSA primitive, same Merkle membership primitive, same pubkey-to-identity commitment pattern.

The trust boundary

Identical to Circuit 1:

  • Fulcio certificate chain verification is off-circuit in the SDK.
  • Rekor inclusion proof is off-circuit in the SDK.
  • SAN URI parsing and the Poseidon2 hash into signer_identity_hash is off-circuit.

The design rationale is the same as Circuit 1: re-proving the Sigstore PKI inside the circuit adds tens of thousands of constraints for a trust signal the SDK can compute faster against well-known anchors, and the signal is not improved by the re-proof.

Cross-circuit binding

  • Circuit 2 ↔ Circuit 1. The bundle verifier cross-checks Circuit 2's commit_hash_* public inputs against the commit hash named in Circuit 1's SLSA predicate. A Circuit 2 proof for a different commit than the one the approved build pipeline processed fails the bundle verifier.
  • Circuit 2 is silent about ordering. It does not prove that the signed commit is the tip of the release branch, only that it is some commit that was signed by an authorised signer and was the input to the build pipeline. Branch-tip policies are typically enforced by the CI pipeline's configuration (which Circuit 1 anchors) rather than by Circuit 2.

The authorised_signer_root tree height is 10 (1024 leaves), matching BUILDER_TREE_HEIGHT in Circuit 1. The constants are deliberately named separately — approved-builder and authorised-signer are different domains and may need to diverge (different tree sizes, different canonicalisation rules for the SAN URI) as deployment experience accumulates. Sharing the constant silently would make that divergence a cross-contamination hazard.

What the proof carries — and what it does not

The proof publishes:

  • the signed commit's SHA-256 (the claim's subject; anchors downstream binding)
  • the authorised-signer Merkle root (commitment, not contents)
  • the signer commitment (commitment, not contents)

An adversary who obtains the proof cannot derive:

  • which specific developer signed the commit (the central confidentiality guarantee)
  • the membership of the authorised-signer set — the Merkle root is a commitment; enumeration requires access to the set itself
  • which developer signs most often — two commits by the same signer produce different signer commitments because the per-commit signature randomness propagates (every Fulcio certificate is short-lived and the pubkey rotates on each invocation)
  • the relationship between this project's authorised signers and any other project's — different projects produce different roots; no cross-project correlation is possible without access to the sets

The second bullet is the one compliance buyers in sensitive sectors care about most. A plain gitsign commit on a public repository enumerates the vendor's signing-eligible developers every time one of them commits; over months of observation an adversary reconstructs a complete roster. Circuit 2's zero-knowledge membership proof leaks nothing additional with each commit.

Honest caveats

  • In-circuit X.509 deferred. Same as Circuit 1: the circuit trusts the SDK to derive signer_identity_hash from the Fulcio SAN correctly. Independent cryptographic review of the SDK's SAN parser is pre-launch audit scope.
  • No revocation of a signer's past commits. If an authorised signer is later removed from the set (for example, they leave the company or their identity is compromised), their historically-signed commits remain valid under whatever authorised_signer_root was current at the time they signed. Re-issuing the buyer's approved-set root after a revocation handles current and future commits; it does not retroactively invalidate past ones. This matches standard PKI semantics but is worth being explicit about.
  • Ordering of commits is not in the circuit. See cross-circuit binding above. If a buyer requires "every commit on the release branch up to the build's input was signed by an authorised signer" rather than "the build's input commit was signed by an authorised signer", that is a CI-policy question (enforced by the pipeline Circuit 1 binds) rather than a Circuit 2 question.
  • GPG-signed commits not in v1. Circuit 2 targets Sigstore gitsign. Plain GPG signing without a Fulcio-issued certificate is out of v1 scope; adding it would require a different signature primitive and a different identity derivation. Plausible v1.5 scope if a buyer asks.

Parameters and performance

Measurev1 targetNotes
Proving time< 8s standaloneDominated by ECDSA P-256 verify; no PAE SHA like Circuit 1
Verification time< 1s browserWASM bb.js, single-threaded
AUTHORISED_SIGNER_TREE_HEIGHT10Configurable per deployment

Further reading