Prove possession of one or more ECC-signed OIDC JWTs without revealing the signatures using zero-knowledge proofs.
This Go library and CLI tool allows you to:
- Generate zero-knowledge proofs that you possess valid ES256 (P-256 ECDSA) signed JWTs
- Batch multiple JWT proofs into a single efficient proof
- Verify proofs without seeing the actual JWT signatures
┌──────────────┐
│ JWT 1, 2, N │ (ES256 signed OIDC tokens)
└──────┬───────┘
│ Parse & Extract
▼
┌──────────────────────────────────────────┐
│ ZK Circuit (gnark/PLONK) │
│ ┌────────────────────────────────────┐ │
│ │ Private: signatures (r,s), messages│ │
│ │ Public: public keys, hashes │ │
│ │ │ │
│ │ Constraints: │ │
│ │ ∀i: ECDSA_Verify(sig_i, msg_i, │ │
│ │ pubkey_i) = ✓ │ │
│ └────────────────────────────────────┘ │
└──────┬───────────────────────────────────┘
│ Generate Proof
▼
┌──────────────┐
│ ZK Proof │ (~small, fast to verify)
└──────┬───────┘
│
▼
┌──────────────┐
│ Verifier │ ✓ Valid without seeing signatures
└──────────────┘
Proof System: PLONK with KZG commitments
- Universal trusted setup (one ceremony for all circuits)
- Fast proving time
- Constant proof size regardless of batch count
- No circuit-specific setup needed when changing batch sizes
Supported Algorithms:
- ES256 (P-256 / secp256r1 ECDSA)
- SHA-256 message hashing
go install github.com/zkp-jwt/cmd/zkp-jwt@latestOr build from source:
git clone https://github.com/zkp-jwt
cd zkp-jwt
go build -o zkp-jwt ./cmd/zkp-jwtGenerate universal PLONK parameters:
zkp-jwt setup --max-batch 10 --output keys/This creates reusable proving/verification keys for circuits handling up to 10 JWTs.
Generate a proof of JWT possession:
# Single JWT
zkp-jwt prove --jwt token.jwt --keys keys/ --output proof.bin
# Multiple JWTs (batched)
zkp-jwt prove --jwt token1.jwt --jwt token2.jwt --jwt token3.jwt \
--keys keys/ --output proof.binVerify a proof:
zkp-jwt verify --proof proof.bin --keys keys/ --public-keys pubkeys.jsonpackage main
import (
"github.com/zkp-jwt/pkg/circuit"
"github.com/zkp-jwt/pkg/prover"
"github.com/zkp-jwt/pkg/verifier"
"github.com/zkp-jwt/pkg/jwt"
)
func main() {
// Parse JWTs
tokens := []string{jwtString1, jwtString2}
parsedJWTs := make([]*jwt.ParsedJWT, len(tokens))
for i, t := range tokens {
parsedJWTs[i], _ = jwt.Parse(t)
}
// Setup circuit
batchCircuit := circuit.NewBatchCircuit(len(parsedJWTs))
pk, vk, _ := circuit.Setup(batchCircuit)
// Generate proof
proof, publicInputs, _ := prover.Prove(parsedJWTs, pk, batchCircuit)
// Verify proof
valid := verifier.Verify(proof, publicInputs, vk)
println("Proof valid:", valid)
}- Completeness: Valid JWTs always produce verifiable proofs
- Soundness: Cannot forge proofs without valid signatures
- Zero-knowledge: Verifier learns only that signatures are valid, not the signatures themselves
- PLONK uses a universal trusted setup ceremony
- We use parameters from Ethereum KZG ceremony
- Setup is updateable and only needs one honest participant
- Only supports ES256 (P-256 ECDSA) signatures
- Circuit size must be determined at setup time (max batch size)
- Does not hide JWT claims (only signatures are private)
- Proving time grows linearly with number of JWTs
MIT License