Documentation

Proof-Carrying Authorization (PCA) is a cryptographic authorization model that replaces identity-centric access control with proof-based authorization.

Introduction

Instead of asking whether a principal is allowed to perform an action, a verifier checks a non-interactive zero-knowledge (NIZK) proof. This proof attests that some witness exists satisfying a policy predicate bound to the action, context, and public inputs.

This model provides:

  • Privacy: Hides the authorizer identity.
  • Composability: Policies can be reused across programs.
  • Replay Resistance: Nullifiers prevent double-authorization.

The Model

Statement Encoding

The fundamental statement proven in PCA is constructed by encoding the action, context, and public inputs:

stmt = enc(action) || enc(context) || enc(public_inputs)

Scope Binding

To prevent cross-protocol attacks, the proof is bound to a specific scope:

scope = SHA256(DST_SCOPE || action || context || SHA256(p_pieces))

Nullifiers

To ensure an authorization can only be used once, a nullifier is derived from the secret witness and the scope:

nf = SHA256(DST_NF || domain || spend_key || scope)

The verifier tracks used nullifiers to reject replays.

Implementation

The current implementation targets Solana using the Pinocchio library for on-chain verification and Groth16 proofs.

Public Input Layout

The verifier expects public inputs in a specific order (big-endian 128-bit limbs padded to 32 bytes):

0: scope_hi 1: scope_lo 2: domain_hi 3: domain_lo 4: nf_hi 5: nf_lo

Examples

  • Private Payments: SPL vault withdrawals authorized by a PCA proof (programs/private_payments).
  • Private Allowlist: proof-gated access to a resource with a dedicated circuit (circuits/allowlist_gate.circom, programs/private_allowlist).

Get Started

Explore the codebase to see the circuit definitions and Solana program implementation.

View on GitHub →