Summary
ProverState::new(permutation) initializes a fresh DuplexChallenger with zero state and does not absorb public inputs. If a caller omits the public-input absorption step, the transcript is identical regardless of the statement being proved, enabling proof reuse across different statements.
Severity
HIGH -- Materially weakens security guarantees.
Location
src/prover.rs:27-35 -- ProverState::new() creates a DuplexChallenger with an all-zero sponge state; no public inputs are absorbed.
The caller is expected to call add_base_scalars with public inputs, but the API does not enforce this. If a caller omits the public-input absorption step, the transcript is identical regardless of the statement being proved.
Impact
A malicious prover can take a valid proof for statement A and present it as valid for statement B, since the verifier's challenges will be identical. This breaks the binding property of the interactive-to-non-interactive transformation.
Suggested Fix
Require public inputs in the constructor:
pub fn new(permutation: P, public_inputs: &[PF<EF>]) -> Self {
let mut state = Self {
challenger: DuplexChallenger::new(permutation),
transcript: Vec::new(),
n_zeros: 0,
_extension_field: std::marker::PhantomData,
};
assert!(!public_inputs.is_empty(), "public inputs must be non-empty");
state.add_base_scalars(public_inputs);
state
}
Alternatively, add a #[must_use] builder pattern that requires with_public_inputs() before build().
References
Summary
ProverState::new(permutation)initializes a freshDuplexChallengerwith zero state and does not absorb public inputs. If a caller omits the public-input absorption step, the transcript is identical regardless of the statement being proved, enabling proof reuse across different statements.Severity
HIGH -- Materially weakens security guarantees.
Location
src/prover.rs:27-35--ProverState::new()creates aDuplexChallengerwith an all-zero sponge state; no public inputs are absorbed.The caller is expected to call
add_base_scalarswith public inputs, but the API does not enforce this. If a caller omits the public-input absorption step, the transcript is identical regardless of the statement being proved.Impact
A malicious prover can take a valid proof for statement A and present it as valid for statement B, since the verifier's challenges will be identical. This breaks the binding property of the interactive-to-non-interactive transformation.
Suggested Fix
Require public inputs in the constructor:
Alternatively, add a
#[must_use]builder pattern that requireswith_public_inputs()beforebuild().References