Skip to content

Commit 849e6e6

Browse files
committed
make SignatureVerifyGadget conditional on the selector input
1 parent a58411d commit 849e6e6

File tree

2 files changed

+109
-15
lines changed

2 files changed

+109
-15
lines changed

src/backends/plonky2/primitives/signature.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,12 @@ lazy_static! {
2727
pub static ref PP: ProverParams = Signature::prover_params().unwrap();
2828
/// Signature verifier parameters
2929
pub static ref VP: VerifierParams = Signature::verifier_params().unwrap();
30+
31+
/// DUMMY_SIGNATURE is used for conditionals where we want to use a `selector` to enable or
32+
/// disable signature verification.
33+
pub static ref DUMMY_SIGNATURE: Signature = dummy_signature().unwrap();
34+
/// DUMMY_PUBLIC_INPUTS accompanies the DUMMY_SIGNATURE.
35+
pub static ref DUMMY_PUBLIC_INPUTS: Vec<F> = dummy_public_inputs().unwrap();
3036
}
3137

3238
pub struct ProverParams {
@@ -38,7 +44,7 @@ pub struct ProverParams {
3844
pub struct VerifierParams(pub(crate) VerifierCircuitData<F, C, D>);
3945

4046
#[derive(Clone, Debug)]
41-
pub struct SecretKey(Value);
47+
pub struct SecretKey(pub(crate) Value);
4248

4349
#[derive(Clone, Debug)]
4450
pub struct PublicKey(pub(crate) Value);
@@ -114,6 +120,20 @@ impl Signature {
114120
}
115121
}
116122

123+
fn dummy_public_inputs() -> Result<Vec<F>> {
124+
let sk = SecretKey(Value::from(0));
125+
let pk = sk.public_key();
126+
let msg = Value::from(0);
127+
let s = Value(PoseidonHash::hash_no_pad(&[pk.0 .0, msg.0].concat()).elements);
128+
Ok([pk.0 .0, msg.0, s.0].concat())
129+
}
130+
131+
fn dummy_signature() -> Result<Signature> {
132+
let sk = SecretKey(Value::from(0));
133+
let msg = Value::from(0);
134+
sk.sign(msg)
135+
}
136+
117137
/// The SignatureInternalCircuit implements the circuit used for the proof of
118138
/// the argument described at https://eprint.iacr.org/2024/1553.
119139
///
@@ -183,7 +203,6 @@ pub mod tests {
183203

184204
use super::*;
185205

186-
// Note: this test must be run with the `--release` flag.
187206
#[test]
188207
fn test_signature() -> Result<()> {
189208
let sk = SecretKey::new();
@@ -204,4 +223,14 @@ pub mod tests {
204223

205224
Ok(())
206225
}
226+
227+
#[test]
228+
fn test_dummy_signature() -> Result<()> {
229+
let sk = SecretKey(Value::from(0));
230+
let pk = sk.public_key();
231+
let msg = Value::from(0);
232+
DUMMY_SIGNATURE.clone().verify(&pk, msg)?;
233+
234+
Ok(())
235+
}
207236
}

src/backends/plonky2/primitives/signature_circuit.rs

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ use plonky2::{
1919
plonk::proof::{ProofWithPublicInputs, ProofWithPublicInputsTarget},
2020
};
2121

22-
use crate::backends::plonky2::basetypes::{Hash, Value, C, D, EMPTY_HASH, EMPTY_VALUE, F};
22+
use super::signature::{PublicKey, SecretKey, Signature, DUMMY_PUBLIC_INPUTS, DUMMY_SIGNATURE};
23+
use crate::backends::plonky2::basetypes::{Hash, Proof, Value, C, D, EMPTY_HASH, EMPTY_VALUE, F};
2324
use crate::backends::plonky2::circuits::common::{CircuitBuilderPod, ValueTarget};
24-
use crate::backends::plonky2::primitives::signature::{PublicKey, Signature};
2525

2626
lazy_static! {
2727
/// SignatureVerifyGadget VerifierCircuitData
@@ -34,7 +34,9 @@ pub struct SignatureTarget {
3434
verifier_data_targ: VerifierCircuitTarget,
3535
selector: BoolTarget,
3636
pk: ValueTarget,
37+
// proof of the SignatureInternalCircuit (=signature::Signature.0)
3738
proof: ProofWithPublicInputsTarget<D>,
39+
dummy_proof: ProofWithPublicInputsTarget<D>,
3840
}
3941

4042
impl SignatureVerifyGadget {
@@ -62,23 +64,29 @@ impl SignatureVerifyGadget {
6264
builder.add_virtual_verifier_data(common_data.config.fri_config.cap_height);
6365

6466
let proof_targ = builder.add_virtual_proof_with_pis(&common_data);
65-
builder.verify_proof::<C>(&proof_targ, &verifier_data_targ, &common_data);
67+
6668
// NOTE: we would use the `conditional_verify_proof_or_dummy` method,
6769
// but since we're using the `standard_recursion_zk_config` (with zk),
68-
// internally it fails to generate the `dummy_circuit`. So for the
69-
// moment we use `verify_proof` (not-conditional).
70-
// builder.conditionally_verify_proof_or_dummy::<C>(
71-
// selector,
72-
// &proof_targ,
73-
// &verifier_data_targ,
74-
// &common_data,
75-
// )?;
70+
// internally it fails to generate the `dummy_circuit`, which mentions
71+
// that degree calculation could be off if zk is enabled. So we use
72+
// `conditional_verify_proof` feeding in our own dummy_proof
73+
// (signature::DUMMY_PROOF).
74+
let dummy_proof_targ = builder.add_virtual_proof_with_pis(&common_data);
75+
builder.conditionally_verify_proof::<C>(
76+
selector,
77+
&proof_targ,
78+
&verifier_data_targ,
79+
&dummy_proof_targ,
80+
&verifier_data_targ,
81+
&common_data,
82+
);
7683

7784
Ok(SignatureTarget {
7885
selector,
7986
proof: proof_targ,
8087
pk: pk_targ,
8188
verifier_data_targ,
89+
dummy_proof: dummy_proof_targ,
8290
})
8391
}
8492
}
@@ -108,6 +116,14 @@ impl SignatureTarget {
108116
},
109117
)?;
110118

119+
pw.set_proof_with_pis_target(
120+
&self.dummy_proof,
121+
&ProofWithPublicInputs {
122+
proof: DUMMY_SIGNATURE.0.clone(),
123+
public_inputs: DUMMY_PUBLIC_INPUTS.clone(),
124+
},
125+
)?;
126+
111127
pw.set_verifier_data_target(
112128
&self.verifier_data_targ,
113129
&super::signature::VP.0.verifier_only,
@@ -124,9 +140,8 @@ pub mod tests {
124140

125141
use super::*;
126142

127-
// Note: this test must be run with the `--release` flag.
128143
#[test]
129-
fn test_signature_gate() -> Result<()> {
144+
fn test_signature_gadget() -> Result<()> {
130145
// generate a valid signature
131146
let sk = SecretKey::new();
132147
let pk = sk.public_key();
@@ -155,4 +170,54 @@ pub mod tests {
155170

156171
Ok(())
157172
}
173+
174+
#[test]
175+
fn test_signature_gadget_disabled() -> Result<()> {
176+
// generate a valid signature
177+
let sk = SecretKey::new();
178+
let pk = sk.public_key();
179+
let msg = Value::from(42);
180+
let sig = sk.sign(msg)?;
181+
// verification should pass
182+
sig.verify(&pk, msg)?;
183+
184+
// replace the message, so that verifications should fail
185+
let msg = Value::from(24);
186+
// expect signature native verification to fail
187+
let v = sig.verify(&pk, Value::from(24));
188+
assert!(v.is_err(), "should fail to verify");
189+
190+
// circuit
191+
let config = CircuitConfig::standard_recursion_zk_config();
192+
let mut builder = CircuitBuilder::<F, D>::new(config);
193+
let mut pw = PartialWitness::<F>::new();
194+
let targets = SignatureVerifyGadget {}.eval(&mut builder)?;
195+
targets.set_targets(&mut pw, true, pk.clone(), msg, sig.clone())?; // selector=true
196+
197+
// generate proof, and expect it to fail
198+
let data = builder.build::<C>();
199+
assert!(data.prove(pw).is_err()); // expect prove to fail
200+
201+
// build the circuit again, but now disable the selector that disables
202+
// the in-circuit signature verification
203+
let config = CircuitConfig::standard_recursion_zk_config();
204+
let mut builder = CircuitBuilder::<F, D>::new(config);
205+
let mut pw = PartialWitness::<F>::new();
206+
207+
let targets = SignatureVerifyGadget {}.eval(&mut builder)?;
208+
targets.set_targets(&mut pw, false, pk, msg, sig)?; // selector=false
209+
210+
// generate & verify proof
211+
let data = builder.build::<C>();
212+
let proof = data.prove(pw)?;
213+
data.verify(proof.clone())?;
214+
215+
// verify the proof with the lazy_static loaded verifier_data (S_VD)
216+
S_VD.verify(ProofWithPublicInputs {
217+
proof: proof.proof.clone(),
218+
public_inputs: vec![],
219+
})?;
220+
221+
Ok(())
222+
}
158223
}

0 commit comments

Comments
 (0)