Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions crypto/stark/src/tests/air_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,45 @@ fn test_prove_fib() {
));
}

/// Soundness regression: a proof for a normal (non-preprocessed) AIR must be
/// rejected if it carries a precomputed trace commitment. That root is never
/// absorbed into the Fiat-Shamir transcript for normal tables, yet its
/// openings would later be authenticated and used — letting a malicious
/// prover choose those columns after learning the challenges.
#[test_log::test]
fn test_reject_unexpected_precomputed_commitment() {
let mut trace = simple_fibonacci::fibonacci_trace([Felt::from(1), Felt::from(1)], 8);

let proof_options = ProofOptions::default_test_options();

let pub_inputs = FibonacciPublicInputs {
a0: Felt::one(),
a1: Felt::one(),
};

let air = FibonacciAIR::<GoldilocksField>::new(&proof_options);
assert!(!air.is_preprocessed());

let mut proof = Prover::prove(
&air,
&mut trace,
&pub_inputs,
&mut DefaultTranscript::<F>::new(&[]),
)
.unwrap();
// An honest proof for a normal AIR carries no precomputed commitment.
assert!(proof.lde_trace_precomputed_merkle_root.is_none());

// A malicious prover attaches an unbound precomputed commitment.
proof.lde_trace_precomputed_merkle_root = Some([0x42; 32]);

assert!(!Verifier::verify(
&proof,
&air,
&mut DefaultTranscript::<F>::new(&[]),
));
}

#[test_log::test]
fn test_prove_fib_2_cols() {
let mut trace = fibonacci_2_columns::compute_trace([Felt::from(1), Felt::from(1)], 16);
Expand Down
12 changes: 11 additions & 1 deletion crypto/stark/src/verifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1208,7 +1208,17 @@ pub trait IsStarkVerifier<
transcript.append_bytes(&expected_precomputed);
transcript.append_bytes(proof.lde_trace_main_merkle_root());
} else {
// Normal table: use commitment from proof
// Normal table: use commitment from proof. A non-preprocessed
// AIR has no precomputed trace, so the proof must not carry a
// precomputed commitment: it is never absorbed into the
// transcript here, yet `verify_trace_openings` and the DEEP
// reconstruction would authenticate and use its openings,
// letting a malicious prover choose those columns after
// learning the Fiat-Shamir challenges. Reject instead.
if proof.lde_trace_precomputed_merkle_root().is_some() {
error!("Normal table {idx} proof carries an unexpected precomputed commitment");
return false;
}
transcript.append_bytes(proof.lde_trace_main_merkle_root());
}
}
Expand Down
Loading