DOCUMENTATION
API reference and integration guide for the Cerebro ZK prover
OVERVIEW
Cerebro is a zero-knowledge proving service for JWT verification. It accepts a standard JWT and its RSA public key modulus, then generates a Groth16 proof that the token is valid — without revealing the token's contents on-chain.
The proof and public signals can be submitted to Starknet, where a Cairo smart contract verifies them using Garaga's BN254 pairing engine.
API_ENDPOINT
The prover accepts JSON payloads up to 1MB. All communication is over HTTPS with CORS enabled.
REQUEST_FORMAT
Send a POST request with the following JSON body:
{
"jwt": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOi...",
"modulus": "2148399...large decimal or hex string..."
}A complete RS256-signed JWT string with three dot-separated parts (header.payload.signature). Must contain sub, iss, aud, nonce, and exp claims. Must not be expired.
The RSA public key modulus (n) from the JWKS endpoint of the JWT issuer. Accepts decimal string or hex format.
RESPONSE_FORMAT
On success, the API returns:
{
"success": true,
"proof": {
"pi_a": ["12345...", "67890...", "1"],
"pi_b": [["12345...", "67890..."], ["12345...", "67890..."], ["1", "0"]],
"pi_c": ["12345...", "67890...", "1"],
"protocol": "groth16",
"curve": "bn128"
},
"publicSignals": [
"pubkey_hash",
"sub_hash",
"nonce_hash",
"iss_hash",
"aud_hash",
"exp"
],
"claims": {
"pubkey_hash": "1234567890...",
"sub_hash": "9876543210...",
"nonce_hash": "1111111111...",
"iss_hash": "2222222222...",
"aud_hash": "3333333333...",
"exp": "1716000000"
},
"prover": "rapidsnark",
"provingTimeSeconds": 4.2
}PUBLIC_SIGNALS
[0] pubkey_hashPoseidon hash of the RSA modulus chunks[1] sub_hashHash of the JWT 'sub' claim (user ID)[2] nonce_hashHash of the 'nonce' claim (session binding)[3] iss_hashHash of the 'iss' claim (token issuer)[4] aud_hashHash of the 'aud' claim (client ID)[5] expJWT expiration timestamp (plaintext)ERROR_RESPONSES
CIRCUIT_DETAILS
The jwt_verify.circom circuit performs the following operations inside the ZK proof:
- >RSA signature verification (2048-bit, 17 x 121-bit limbs)
- >SHA-256 hash check of the signed JWT message
- >Base64url decoding of the JWT payload
- >Extraction and hashing of sub, iss, aud, nonce claims
- >Plaintext output of exp for on-chain expiration checks
SMART_CONTRACTS
On-chain verification uses a Garaga-generated Groth16 verifier contract deployed on Starknet. The contract exposes a single function:
fn verify_groth16_proof_bn254(
full_proof_with_hints: Span<felt252>
) -> Result<Span<u256>, felt252>DEPLOYED_ADDRESSES
0x03cb5a1c747cbdf80e6051f8c2a68bbbbf40b4bcfd8dc4efe3f84fd15dae77dd.0x02bd46d2be8ef3a804da2b38cfcfecdc04cf16f0f791d36780a6026f9c71fcb1INTEGRATION_GUIDE
1. FETCH_JWKS
Retrieve the RSA public key from the OIDC provider's JWKS endpoint and extract the modulus.
// Fetch Google's JWKS and extract the modulus
const jwks = await fetch(
"https://www.googleapis.com/oauth2/v3/certs"
).then(r => r.json());
const key = jwks.keys.find(k => k.kid === jwtHeader.kid);
const modulus = BigInt(
"0x" + Buffer.from(key.n, "base64url").toString("hex")
).toString();2. GENERATE_PROOF
const response = await fetch("https://api.cerebro.cavos.xyz/prove", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ jwt: idToken, modulus }),
});
const { proof, publicSignals, claims } = await response.json();3. VERIFY_ON_CHAIN
Serialize the proof using Garaga's calldata format and call the verifier contract:
// Garaga serialization order:
// [pi_a.x, pi_a.y, pi_b[0][1], pi_b[0][0],
// pi_b[1][1], pi_b[1][0], pi_c.x, pi_c.y]
// Then append public signals
const tx = await account.execute({
contractAddress: VERIFIER_ADDRESS,
entrypoint: "verify_groth16_proof_bn254",
calldata: fullProofWithHints,
});4. CURL_EXAMPLE
curl -X POST https://api.cerebro.cavos.xyz/prove \
-H "Content-Type: application/json" \
-d '{
"jwt": "eyJhbGciOiJSUzI1NiIs...",
"modulus": "21387645123490812039..."
}'