schnorr/src/schnorr_prover.rs
src/schnorr_prover.rsRaw
1use anyhow::Result;
2
3use plonky2::iop::{
4 target::{BoolTarget, Target},
5 witness::{PartialWitness, WitnessWrite},
6};
7use plonky2::field::{
8 goldilocks_field::GoldilocksField,
9 types::Field,
10};
11use plonky2::hash::poseidon::PoseidonHash;
12use plonky2::plonk::{
13 circuit_builder::CircuitBuilder,
14 config::GenericConfig,
15};
16
17use crate::{
18 mod65537::Mod65537Builder,
19 schnorr::{SchnorrPublicKey, SchnorrSignature},
20};
21
22type GoldF = GoldilocksField;
23
24pub struct MessageTarget {
25 msg: Vec<Target>,
26}
27
28impl MessageTarget {
29 fn new_with_size(builder: &mut CircuitBuilder<GoldF, 2>, n: usize) -> Self {
30 Self {
31 msg: builder.add_virtual_targets(n),
32 }
33 }
34
35 fn set_witness(&self, pw: &mut PartialWitness<GoldF>, msg: &Vec<GoldF>) -> Result<()> {
36 assert!(msg.len() == self.msg.len());
37 for (&t, &x) in self.msg.iter().zip(msg.iter()) {
38 pw.set_target(t, x)?;
39 }
40
41 Ok(())
42 }
43}
44
45pub struct SchnorrSignatureTarget {
46 s: Target,
47 e: Target,
48}
49
50impl SchnorrSignatureTarget {
51 fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
52 let s = builder.add_virtual_target();
53 let e = builder.add_virtual_target();
54 Self{ s, e }
55 }
56
57 fn set_witness(&self, pw: &mut PartialWitness<GoldF>, sig: &SchnorrSignature) -> Result<()> {
58 pw.set_target(self.s, GoldilocksField::from_canonical_u64(sig.s))?;
59 pw.set_target(self.e, GoldilocksField::from_canonical_u64(sig.e))?;
60 Ok(())
61 }
62}
63
64pub struct SchnorrPublicKeyTarget {
65 pk: Target,
66}
67
68impl SchnorrPublicKeyTarget {
69 fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
70 Self{ pk: builder.add_virtual_target() }
71 }
72
73 fn set_witness(&self, pw: &mut PartialWitness<GoldF>, pk: &SchnorrPublicKey) -> Result<()> {
74 pw.set_target(self.pk, pk.pk)?;
75 Ok(())
76 }
77}
78
79pub struct SchnorrBuilder {}
80
81pub trait SignatureVerifierBuilder {
82 fn constrain_sig <
83 C: GenericConfig<2, F = GoldF>,
84 > (
85 &self,
86 builder: &mut CircuitBuilder::<GoldF, 2>,
87 sig: &SchnorrSignatureTarget,
88 msg: &MessageTarget,
89 pk: &SchnorrPublicKeyTarget,
90 );
91
92 fn verify_sig <
93 C: GenericConfig<2, F = GoldF>,
94 > (
95 &self,
96 builder: &mut CircuitBuilder::<GoldF, 2>,
97 sig: &SchnorrSignatureTarget,
98 msg: &MessageTarget,
99 pk: &SchnorrPublicKeyTarget,
100 ) -> BoolTarget;
101}
102
103impl SignatureVerifierBuilder for SchnorrBuilder {
104 fn constrain_sig <
105 C: GenericConfig<2, F = GoldF>,
106 > (
107 &self,
108 builder: &mut CircuitBuilder::<GoldF, 2>,
109 sig: &SchnorrSignatureTarget,
110 msg: &MessageTarget,
111 pk: &SchnorrPublicKeyTarget,
112 ) -> () {
113 let verification_output = self.verify_sig::<C>(builder, sig, msg, pk);
114 let true_target = builder._true();
115 builder.connect(verification_output.target, true_target.target);
116 }
117
118 fn verify_sig <
119 C: GenericConfig<2, F = GoldF>,
120 > (
121 &self,
122 builder: &mut CircuitBuilder::<GoldF, 2>,
123 sig: &SchnorrSignatureTarget,
124 msg: &MessageTarget,
125 pk: &SchnorrPublicKeyTarget,
126 ) -> BoolTarget {
127 let PRIME_GROUP_GEN: Target = builder.constant(GoldF::from_canonical_u64(6612579038192137166));
128 let PRIME_GROUP_ORDER: Target = builder.constant(GoldF::from_canonical_u64(65537));
129 const num_bits_exp: usize = 32;
130
131 /* here's the direct verification calculation,
132 which we verify in-circuit
133 let r: GoldF = Self::pow(self.PRIME_GROUP_GEN, sig.s)
134 * Self::pow(pk.pk, sig.e);
135 let e_v: u64 = self.hash_insecure(&r, msg);
136 e_v == sig.e */
137
138 let gs: Target = builder.exp(PRIME_GROUP_GEN, sig.s, num_bits_exp);
139 let pe: Target = builder.exp(pk.pk, sig.e, num_bits_exp);
140 let r: Target = builder.mul(gs, pe);
141
142 // compute hash
143 // note that it's safe to clone Targets since they just contain indices
144 let hash_input: Vec<Target> = std::iter::once(r)
145 .chain(msg.msg.iter().cloned())
146 .collect();
147 let hash_output: Target = builder.hash_n_to_hash_no_pad::<PoseidonHash>(
148 hash_input,
149 ).elements[0]; // whoops have to take mod group order;
150
151 let e: Target = Mod65537Builder::mod_65537(builder, hash_output);
152
153 // test equality
154 builder.is_equal(e, sig.e)
155 }
156}
157
158#[cfg(test)]
159mod tests{
160 use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature};
161 use crate::schnorr_prover::{MessageTarget, SchnorrBuilder, SchnorrPublicKeyTarget, SchnorrSignatureTarget};
162 use plonky2::iop::witness::PartialWitness;
163 use plonky2::plonk::{
164 circuit_builder::CircuitBuilder,
165 circuit_data::CircuitConfig,
166 config::{GenericConfig, PoseidonGoldilocksConfig},
167 };
168 use plonky2::field::goldilocks_field::GoldilocksField;
169 use rand;
170
171 #[test]
172 fn test_schnorr() -> () {
173 const D: usize = 2;
174 type C = PoseidonGoldilocksConfig;
175 type F = <C as GenericConfig<D>>::F;
176
177 let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
178
179 let config = CircuitConfig::standard_recursion_config();
180 let mut builder = CircuitBuilder::<F, D>::new(config);
181
182 let sb: SchnorrBuilder = SchnorrBuilder{};
183
184 // create keypair, message, signature
185 let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
186 let ss = SchnorrSigner::new();
187 let pk: SchnorrPublicKey = ss.keygen(&sk);
188 let msg: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
189 vec![1500, 1600, 0, 0, 0]
190 );
191 let msg_size: usize = msg.len();
192 let sig: SchnorrSignature = ss.sign(&msg, &sk, &mut rng);
193
194 let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
195 let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
196 let msg_targ = MessageTarget::new_with_size(&mut builder, msg_size);
197
198 sb.constrain_sig::<PoseidonGoldilocksConfig> (
199 &mut builder,
200 &sig_targ,
201 &msg_targ,
202 &pk_targ
203 );
204
205 // assign witnesses for verification
206 let mut pw: PartialWitness<F> = PartialWitness::new();
207 pk_targ.set_witness(&mut pw, &pk).unwrap();
208 sig_targ.set_witness(&mut pw, &sig).unwrap();
209 msg_targ.set_witness(&mut pw, &msg).unwrap();
210
211
212 let data = builder.build::<C>();
213 let proof = data.prove(pw).unwrap();
214 }
215
216 #[test]
217 fn test_schnorr_fails() {
218 const D: usize = 2;
219 type C = PoseidonGoldilocksConfig;
220 type F = <C as GenericConfig<D>>::F;
221
222 let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
223
224 let config = CircuitConfig::standard_recursion_config();
225 let mut builder = CircuitBuilder::<F, D>::new(config);
226
227 let sb: SchnorrBuilder = SchnorrBuilder{};
228
229 // create keypair, message, signature
230 let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
231 let ss = SchnorrSigner::new();
232 let pk: SchnorrPublicKey = ss.keygen(&sk);
233 let msg0: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
234 vec![1500, 1600, 0, 0, 0]
235 );
236 let msg_size: usize = msg0.len();
237 let sig: SchnorrSignature = ss.sign(&msg0, &sk, &mut rng);
238
239 let msg1: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
240 vec![1510, 1600, 0, 0, 0]
241 );
242
243 let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
244 let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
245 let msg_targ = MessageTarget::new_with_size(&mut builder, msg_size);
246
247 let verification_result = sb.verify_sig::<PoseidonGoldilocksConfig> (
248 &mut builder,
249 &sig_targ,
250 &msg_targ,
251 &pk_targ
252 );
253
254 // assign witnesses for verification
255 let mut pw: PartialWitness<F> = PartialWitness::new();
256 pk_targ.set_witness(&mut pw, &pk).unwrap();
257 sig_targ.set_witness(&mut pw, &sig).unwrap();
258 msg_targ.set_witness(&mut pw, &msg1).unwrap();
259
260 // check value of verification result
261 let false_target = builder._false();
262 builder.connect(verification_result.target, false_target.target);
263
264 let data = builder.build::<C>();
265 let proof = data.prove(pw).unwrap();
266 }
267
268 #[test]
269 #[should_panic]
270 fn test_schnorr_panics() {
271 const D: usize = 2;
272 type C = PoseidonGoldilocksConfig;
273 type F = <C as GenericConfig<D>>::F;
274
275 let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
276
277 let config = CircuitConfig::standard_recursion_config();
278 let mut builder = CircuitBuilder::<F, D>::new(config);
279
280 let sb: SchnorrBuilder = SchnorrBuilder{};
281
282 // create keypair, message, signature
283 let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
284 let ss = SchnorrSigner::new();
285 let pk: SchnorrPublicKey = ss.keygen(&sk);
286 let msg0: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
287 vec![1500, 1600, 0, 0, 0]
288 );
289 let msg_size: usize = msg0.len();
290 let sig: SchnorrSignature = ss.sign(&msg0, &sk, &mut rng);
291
292 let msg1: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
293 vec![1510, 1600, 0, 0, 0]
294 );
295
296 let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
297 let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
298 let msg_targ = MessageTarget::new_with_size(&mut builder, msg_size);
299
300 let verification_result = sb.verify_sig::<PoseidonGoldilocksConfig> (
301 &mut builder,
302 &sig_targ,
303 &msg_targ,
304 &pk_targ
305 );
306
307 // assign witnesses for verification
308 let mut pw: PartialWitness<F> = PartialWitness::new();
309 pk_targ.set_witness(&mut pw, &pk).unwrap();
310 sig_targ.set_witness(&mut pw, &sig).unwrap();
311 msg_targ.set_witness(&mut pw, &msg1).unwrap();
312
313 // value of verification result should be false
314 let true_target = builder._true();
315 builder.connect(verification_result.target, true_target.target);
316
317 let data = builder.build::<C>();
318 let proof = data.prove(pw).unwrap();
319 }
320}