zkAPI

Cryptography

Primitives, post-quantum posture, domain separation

Post-quantum security posture

The protocol is post-quantum wherever practical:

ComponentSchemePQ-secure
Proof systemCairo STARK proofsYes
Hash functionPoseidon over the Stark fieldYes
Merkle treePoseidon-basedYes
Server signaturesXMSS (WOTS+, h=20)Yes
NullifiersPoseidon-derivedYes
Balance commitmentPedersen on Stark curveNo

The Pedersen balance commitment is the single accepted non-PQ component in v1, required for homomorphic addition and rerandomization. This exception is isolated to the pedersen_balance module in Cairo and zkapi-crypto/pedersen in Rust.

Primitives

  • Field: Stark field felt252
  • Hash: Poseidon builtin, domain-separated
  • Balance commitment: Pedersen on Stark curve: E(B, r) = B·G_balance + r·H_blind
  • Server signatures: XMSS with WOTS+ (w=16, n=248 bits, tree height=20)
  • Nullifiers: x = Poseidon(domain("zkapi.null"), secret, anchor)

All Poseidon invocations use domain separation tags. No unlabeled hash invocation exists in the codebase.

Domain tags

LabelUsage
zkapi.regRegistration commitment
zkapi.leafActive note leaf
zkapi.nodeMerkle tree internal node
zkapi.nullNullifier derivation
zkapi.stateState signature message
zkapi.clearClearance signature message
zkapi.anchorNext anchor derivation
zkapi.blindBlind delta derivation
zkapi.xmss.*XMSS/WOTS+ internal hashing
zkapi.payloadPayload commitment

Public inputs on the wire

Visible to the verifier (and therefore to anyone):

protocol_version, chain_id, contract_address, active_root, XMSS epoch and root, x, E(B)_anon, expiry_ts, solvency_bound.

Kept private inside the STARK witness:

s, note_id, D, Merkle siblings, B, r, ε, τ, σ_srv.

Crypto wiring by step

StepPrimitiveWhere
Register / depositC = Poseidon("reg", s, 0), Merkle insertZkApiVault.deposit
Note confirm (local)store (s, note_id, D, expiry), set B=D, r=0, τ=1Wallet::confirm_deposit
Per-request proofx = Poseidon("null", s, τ); rerandomize E(B); STARK π_reqzkapi-proof::RequestProofBuilder
Server verifyproof check, root match, nullifier unseen, cap enforcementzkapi-serverd::RequestProcessor
Next-state signingE(B_new) = E(B)_anon − Δ·G + blind·H; fresh τ_new; XMSS signzkapi-serverd::signer
Client state updateverify XMSS sig, recompute commitment, atomic NoteState commitWallet::request_flow
Mutual-close withdrawalx_w = Poseidon("null", s, τ_cur); XMSS-sign Poseidon("clear", x_w); π_wd reveals B_finalPOST /v1/withdraw/clearance, ZkApiVault.mutualClose

On this page