arnaucube
arnaucube/schnorrPublic Self-hosted repository
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}