Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Jolt ZK Dory Opening-Point Binding PoC + Weak Ordering Fiat-Shamir in Sumcheck

This PoC contains a PoC for a soundness bug in Jolt's zk verifier. It chains 2 bugs together to forge a proof for the following program:

#[jolt::provable(heap_size = 128, max_trace_length = 1024)]
fn is_seven(x: u64) -> bool {
    x == 7
}
honest.bin: input 6, output false   (is_seven(6) == false, truth)
poc.bin:    input 6, output true    (is_seven(6) == true, forgery)

This affects ZK mode only (--features zk). The non-ZK verifier is not affected by this issue.

Reproduction

Run:

./run_repro.sh

The script:

  1. Installs Rust.
  2. Clones https://github.com/a16z/jolt.
  3. Checks out commit 78807efcd1f7a5279da4e8ebfa00f7c30d572afa (latest commit at the time of writing)
  4. Builds the PoC with --features zk and runs it against the verifier.

Expected output:

honest.bin: verified  (is_seven(6) == false)
poc.bin: verified  (is_seven(6) == true)

poc.bin verifying is the soundness break.

The proofs were produced offline by a malicious prover and are checked here by the stock, unmodified jolt_verifier::verify. verifier_preprocessing.bin is the shared verifier preprocessing (same SRS for both proofs).

Bug Summary

Vulnerability: verify_zk does not bind the opening point

The Stage 8 batch opening is the single check that ties the committed witness polynomials to their claimed evaluations at the verifier's Fiat–Shamir point.

In ZK mode this goes through PCS::verify_zk in crates/jolt-verifier/src/stages/stage8/verify.rs:136:

let hiding_evaluation_commitment = PCS::verify_zk(
    &joint_commitment,
    pcs_opening_point.as_slice(),   // honest Fiat–Shamir point
    &proof.joint_opening_proof,
    &preprocessing.pcs_setup,
    transcript,
)?;

The Dory ZK verification does not enforce that proof was actually produced at pcs_opening_point. A proof generated at a chosen point P' is accepted when the verifier checks it at the honest point P (P' != P). The point is appended to the transcript afterwards via bind_zk_opening_inputs, so Fiat–Shamir stays consistent but the proof's evaluation relation is never constrained to that point.

Contrast the clear path (stage8/verify.rs:203), which is sound:

PCS::verify(
    &joint_commitment,
    pcs_opening_point.as_slice(),
    joint_claim,                    // claimed value IS passed and checked
    &proof.joint_opening_proof,
    &preprocessing.pcs_setup,
    transcript,
)?;

Here Dory binds (commitment, point, value) together. A wrong point or wrong value is rejected.

It is important to note that since most of the Dory implementation is in a dependency, this can also be patched there. However, it does seem that there is a draft PR in the Dory repo from April 29th that claims this should be patched on the caller side:

a16z/dory#21

Exploit Path

The end-to-end forgery (poc.bin) chains the point binding gap with the fact that, in ZK mode, opening claims are carried as hiding commitments and verified only relationally by BlindFold:

  1. Run is_seven(6) honestly (output false), flip the public-output byte to true, and rewrite the single store instruction that writes it. The rest of the execution still computes false, so the witness is now internally inconsistent. The public input region is untouched.
  2. Adjust one affine output opening so the BlindFold output constraint stays satisfiable after the flip (the hidden ZK opening claims give the prover this freedom).
  3. Prove the Stage 8 opening at an attacker chosen point where the joint polynomial evaluates to the forged claim, and rely on the missing point binding so the verifier accepts it against the honest point.

The tampered trace alone is unprovable (honest proving fails the BlindFold R1CS). The steps above turn it into an accepting proof. The forgery lives entirely on the prover side. Nothing in the verifier is modified.

Timeline

This has now been fixed in: a16z/dory#23 and a16z/jolt#1664

About

A proof forgery in Jolt

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages