Skip to content

feat: pin proving key sha256 and insecure test setup flag in generated vks - #36

Open
ananas-block wants to merge 2 commits into
masterfrom
jorrit/vk-setup-metadata
Open

ananas-block wants to merge 2 commits into
masterfrom
jorrit/vk-setup-metadata

Conversation

@ananas-block

@ananas-block ananas-block commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Both vk generators (vk::circom, vk::gnark) now require a SetupKind (InsecureTest | Production) and a ProvingKeySource (File(path) | Sha256(digest)), and write next to the vk const:

  • <NAME>_PROVING_KEY_SHA256: SHA-256 of the proving key file (streamed). A prover compares it against vk::setup::proving_key_sha256(path) of the key it is about to load, so a stale or mismatched key fails before proving.

  • <NAME>_INSECURE_TEST_SETUP: true for seeded or untrusted setups (whoever knows the setup randomness can make the vk accept a proof for any public inputs; honest proofs still need a witness). Such a vk also gets a warning header and a compile_error! unless the including crate enables an insecure-test-setup feature, so a test vk cannot end up in a deployable program by accident.

  • <NAME>_SETUP_TXT: both values as an exported, delimited string (the security.txt technique). It is present in the SBF binary, so deploy tooling can read it with vk::setup::find_setup_txts after deployment, or by hand:

    solana program dump <PROGRAM_ID> program.so
    strings program.so | grep -A7 "BEGIN GROTH16 VK SETUP"

    The marker is set by whoever builds the program; it catches mistakes, not a malicious deployer.

    The export name includes a content hash, so several vks sharing a const name in different modules do not collide at link time.

Declaring Production for a vk with delta_g2 == gamma_g2 (snarkjs / gnark mpcsetup before any phase-2 contribution) is an error; test setups with that shape still generate. delta_equal_gamma_vk_accepts_forged_proof shows why: the crate's own unit-test vk has delta == gamma, and A = α, B = β, C = -L(x) verifies against it for arbitrary inputs.

Breaking

generate_vk_file, parse_vk_json_to_rust_string and generate_bsb22_vk_file take the new arguments. New error variants: VkParseError::ForgeableProductionVk, Groth16Error::ForgeableProductionVk (code 22).

Test crates

  • The gnark bench generator also writes {label}_pk.bin (covered by TestGenerateIsDeterministic).
  • tests/program and tests/rust-vk declare InsecureTest and enable insecure-test-setup by default; gnark-vk / circom-vk reach them only as dev-dependencies (the program's normal dependency carries only bsb22).
  • tests/program/tests/setup_txt.rs reads the 8 markers back from the built .so and checks each pk hash.
  • The rust-vk e2e test checks the zkey hash before proving.

Test plan

  • cargo test --workspace (incl. FFI differential tests)
  • cargo test -p groth16-solana --features "bsb22 gnark-vk circom-vk" (57 unit + 6 codegen)
  • cargo build-sbf + cargo test -p bsb22-integration-program --test failing --test setup_txt
  • cargo test -p rust-vk-integration-test
  • go test ./... in tests/gnark-ffi/gnark-fixture
  • fmt, clippy -D warnings, feature matrix, check-nostd, cargo rdme --check
  • bench: BENCHMARKS.md unchanged (no CU cost)

Follow-ups (zolana)

  • Replace the header setup_cli.rs prepends with SetupKind::InsecureTest; enable insecure-test-setup only in the example programs.
  • Have the prover's LoadKeys compare sha256(pk.bin) against the baked const.
  • Add a deploy check (xtask) that calls vk::setup::find_setup_txts on the .so and fails on an insecure marker or on no markers at all.
  • Not yet verified: that an exported *_SETUP_TXT living in a dependency crate (zolana keeps vks in interface crates) survives into the program .so. Verified here only for vk files included in the program crate itself.

…d vks

Both vk generators (vk::circom, vk::gnark) now take a required
SetupKind (InsecureTest | Production) and a ProvingKeySource (File |
Sha256), and emit next to the vk const:

- <NAME>_PROVING_KEY_SHA256: SHA-256 of the proving key file, so a
  prover can check the key it loads (vk::setup::proving_key_sha256)
  before proving.
- <NAME>_INSECURE_TEST_SETUP: true for seeded or untrusted setups.
  Such a vk also gets a warning header and a compile_error! unless the
  including crate enables an `insecure-test-setup` feature.
- <NAME>_SETUP_TXT: both values as an exported, delimited string
  (security.txt technique) that survives into the SBF binary.
  vk::setup::find_setup_txts and the vk_setup example read it back
  from a local .so or a `solana program dump`; --deny-insecure exits 2.

Declaring Production for a vk whose delta equals its gamma (no phase-2
contribution) is rejected: with gamma == delta anyone can build an
accepting proof for any public inputs from the vk alone.
delta_equal_gamma_vk_accepts_forged_proof demonstrates this against
the crate's own test vk.

Breaking: generate_vk_file, parse_vk_json_to_rust_string and
generate_bsb22_vk_file take the new arguments.

Test crates: the gnark bench generator also writes {label}_pk.bin;
tests/program and tests/rust-vk declare InsecureTest and enable
insecure-test-setup; tests/program/tests/setup_txt.rs reads the
markers back from the built .so; the rust-vk e2e test checks the zkey
hash before proving. No CU change (BENCHMARKS.md unchanged).
Remove examples/vk_setup.rs, its [[example]] entry and the vk-setup
just recipe; the repo has no examples and deploy tooling calls
vk::setup::find_setup_txts directly. The docs point to that function
and to `strings program.so | grep -A7 "BEGIN GROTH16 VK SETUP"` for a
manual check.

Reword the new doc comments, generated comments, compile error and
manifest comments: state the pairing identity instead of "collapses
to", replace "never" with "do not"/"does not", and drop vague verbs
(emit, carries, pins, refuses).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant