arnaucube
arnaucube/schnorrPublic Self-hosted repository
schnorr/src/schnorr_prover.rs
src/schnorr_prover.rsRaw
  1use anyhow::Result;
  2
  3use plonky2::iop::{
  4    generator::{GeneratedValues, SimpleGenerator},
  5    target::{BoolTarget, Target},
  6    witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite},
  7};
  8use plonky2::field::{
  9    extension::{Extendable, FieldExtension},
 10    goldilocks_field::GoldilocksField,
 11    types::{Field, PrimeField64},
 12};
 13use plonky2::hash::{
 14    hash_types::RichField,
 15    poseidon::PoseidonHash,
 16};
 17use plonky2::plonk::{
 18    circuit_builder::CircuitBuilder,
 19    circuit_data::{
 20        CircuitConfig, 
 21        CircuitData,
 22        CommonCircuitData, 
 23        VerifierCircuitData, 
 24        VerifierOnlyCircuitData
 25    },
 26    config::{GenericConfig, PoseidonGoldilocksConfig},
 27    proof::ProofWithPublicInputs,
 28};
 29use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
 30
 31use crate::schnorr::{SchnorrPublicKey, SchnorrSignature};
 32
 33type GoldF = GoldilocksField;
 34
 35#[derive(Debug, Default)]
 36pub struct Mod65537Generator {
 37    a: Target,
 38    q: Target,
 39    r: Target,
 40}
 41
 42impl SimpleGenerator<GoldF, 2> for Mod65537Generator {
 43    fn id(&self) -> String {
 44        "Mod65537Generator".to_string()
 45    }
 46    fn dependencies(&self) -> Vec<Target> {
 47        vec![self.a]
 48    }
 49
 50    fn run_once(
 51        &self,
 52        witness: &PartitionWitness<GoldF>,
 53        out_buffer: &mut GeneratedValues<GoldF>,
 54    ) -> Result<()> {
 55        let a = witness.get_target(self.a);
 56        let a64 = a.to_canonical_u64();
 57        let q64 = a64 / 65537;
 58        let r64 = a64 % 65537;
 59
 60        out_buffer.set_target(self.q, GoldF::from_canonical_u64(q64));
 61        out_buffer.set_target(self.r, GoldF::from_canonical_u64(r64));
 62
 63        Ok(())
 64    }
 65
 66    fn serialize(&self, dst: &mut Vec<u8>, common_data: &CommonCircuitData<GoldF, 2>) -> IoResult<()> {
 67        dst.write_target(self.a)?;
 68        dst.write_target(self.q)?;
 69        dst.write_target(self.r)?;
 70        Ok(())
 71    }
 72
 73    fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData<GoldF, 2>) -> IoResult<Self>
 74    where
 75        Self: Sized 
 76    {
 77            let a = src.read_target()?;
 78            let q = src.read_target()?;
 79            let r = src.read_target()?;
 80            Ok(Self { a, q, r })
 81    }
 82}
 83
 84pub struct Mod65537Builder {}
 85
 86impl Mod65537Builder {
 87    // Reduce a modulo the constant 65537
 88    // where a is the canonical representative for an element of the field
 89    // (meaning: 0 \leq a < p)
 90
 91    // To prove this, write
 92    // a = 65537 * q + r, and do range checks to check that:
 93    // 0 <= q <= floor(p / 65537)
 94    // 0 <= r < 65537
 95    // (these first two checks guarantee that a lies in the range [0, p + 65536])
 96    // if q = floor(p / 65537) then r = 0
 97    // (note that p % 65537 == 1 so this is the only possibility)
 98    pub(crate) fn mod_65537 (
 99        builder: &mut CircuitBuilder::<GoldF, 2>,
100        a: Target,
101    ) -> Target {
102        let q = builder.add_virtual_target();
103        let r = builder.add_virtual_target();
104
105        // the Mod65537Generator will assign values to q and r later
106        builder.add_simple_generator( Mod65537Generator { a, q, r } );
107
108        // impose four constraints
109        // 1. a = 65537 * q + r
110        let t65537 = builder.constant(GoldF::from_canonical_u64(65537));
111        let a_copy = builder.mul_add(t65537, q, r);
112        builder.connect(a, a_copy);
113
114        // 2. 0 <= q <= floor(p / 65537)
115        // max_q is 281470681743360 = floor(p / 65537) = (p-1) / 65537 = 2^48 - 2^32
116        let max_q = builder.constant(GoldF::from_canonical_u64(281470681743360));
117        builder.range_check(q, 48);
118        let diff_q = builder.sub(max_q, q);
119        builder.range_check(diff_q, 48);
120
121        // 3. 0 <= r < 65537
122        let max_r = builder.constant(GoldF::from_canonical_u64(65537));
123        builder.range_check(r, 17);
124        let diff_r = builder.sub(max_r, r);
125        builder.range_check(diff_r, 17);
126
127        // 4. if q = floor(p / 65537) then r = 0
128        let q_equals_max = builder.is_equal(q, max_q);
129        let prod_temp = builder.mul(q_equals_max.target, r);
130        let zero_temp = builder.zero();
131        builder.connect(prod_temp, zero_temp);
132
133        // throw in the Generator to tell builder how to compute r
134        builder.add_simple_generator( Mod65537Generator {a, q, r} );
135
136        r
137    }
138}
139
140pub struct MessageTarget {
141    msg: Vec<Target>,
142}
143
144impl MessageTarget {
145    fn new_with_size(builder: &mut CircuitBuilder<GoldF, 2>, n: usize) -> Self {
146        Self {
147            msg: builder.add_virtual_targets(n),
148        }
149    }
150
151    fn set_witness(&self, pw: &mut PartialWitness<GoldF>, msg: &Vec<GoldF>) -> Result<()> {
152        assert!(msg.len() == self.msg.len());
153        for (&t, &x) in self.msg.iter().zip(msg.iter()) {
154            pw.set_target(t, x)?;
155        }
156
157        Ok(())
158    }
159}
160
161pub struct SchnorrSignatureTarget {
162    s: Target,
163    e: Target,
164}
165
166impl SchnorrSignatureTarget {
167    fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
168        let s = builder.add_virtual_target();
169        let e = builder.add_virtual_target();
170        Self{ s, e }
171    }
172
173    fn set_witness(&self, pw: &mut PartialWitness<GoldF>, sig: &SchnorrSignature) -> Result<()> {
174        pw.set_target(self.s, GoldilocksField::from_canonical_u64(sig.s))?;
175        pw.set_target(self.e, GoldilocksField::from_canonical_u64(sig.e))?;
176        Ok(())
177    }
178}
179
180pub struct SchnorrPublicKeyTarget {
181    pk: Target,
182}
183
184impl SchnorrPublicKeyTarget {
185    fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
186        Self{ pk: builder.add_virtual_target() }
187    }
188
189    fn set_witness(&self, pw: &mut PartialWitness<GoldF>, pk: &SchnorrPublicKey) -> Result<()> {
190        pw.set_target(self.pk, pk.pk)?;
191        Ok(())
192    }
193}
194
195pub struct SchnorrBuilder {}
196
197impl SchnorrBuilder {
198
199
200    pub fn constrain_sig <
201        C: GenericConfig<2, F = GoldF>,
202    > (
203        &self,
204        builder: &mut CircuitBuilder::<GoldF, 2>,
205        sig: &SchnorrSignatureTarget,
206        msg: &MessageTarget,
207        pk: &SchnorrPublicKeyTarget,
208    ) -> () {
209        let PRIME_GROUP_GEN: Target = builder.constant(GoldF::from_canonical_u64(6612579038192137166));
210        let PRIME_GROUP_ORDER: Target = builder.constant(GoldF::from_canonical_u64(65537));
211        const num_bits_exp: usize = 32;
212
213        /* here's the direct verification calculation,
214        which we verify in-circuit
215        let r: GoldF = Self::pow(self.PRIME_GROUP_GEN, sig.s)
216            * Self::pow(pk.pk, sig.e);
217        let e_v: u64 = self.hash_insecure(&r, msg);
218        e_v == sig.e   */
219
220        let gs: Target = builder.exp(PRIME_GROUP_GEN, sig.s, num_bits_exp);
221        let pe: Target = builder.exp(pk.pk, sig.e, num_bits_exp);
222        let r: Target = builder.mul(gs, pe);
223
224        // compute hash
225        // note that it's safe to clone Targets since they just contain indices
226        let hash_input: Vec<Target> = std::iter::once(r)
227            .chain(msg.msg.iter().cloned()) 
228            .collect();
229        let hash_output: Target = builder.hash_n_to_hash_no_pad::<PoseidonHash>(
230            hash_input,
231        ).elements[0]; // whoops have to take mod group order;
232
233        let e: Target = Mod65537Builder::mod_65537(builder, hash_output);
234
235        // enforce equality
236        builder.connect(e, sig.e);
237    }
238}
239
240#[cfg(test)]
241mod tests{
242    use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature};
243    use crate::schnorr_prover::{MessageTarget, Mod65537Builder, SchnorrBuilder, SchnorrPublicKeyTarget, SchnorrSignatureTarget};
244    use plonky2::hash::poseidon::Poseidon;
245    use plonky2::iop::{
246        target::Target,
247        witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite},
248    };
249    use plonky2::plonk::{
250        circuit_builder::CircuitBuilder,
251        circuit_data::{
252            CircuitConfig, 
253            CircuitData, 
254            CommonCircuitData, 
255            VerifierCircuitData, 
256            VerifierOnlyCircuitData
257        },
258        config::{GenericConfig, PoseidonGoldilocksConfig},
259    };
260    use plonky2::field::{
261        goldilocks_field::GoldilocksField,
262        types::Field,
263    };
264    use rand;
265
266    #[test]
267    fn test_mod65537() -> () {
268        const D: usize = 2;
269        const p: u64 = 18446744069414584321;  // the Goldilocks prime
270        type C = PoseidonGoldilocksConfig;
271        type F = <C as GenericConfig<D>>::F;
272
273        let config = CircuitConfig::standard_recursion_config();
274        let mut builder = CircuitBuilder::<F, D>::new(config);
275
276        let a64: Vec<u64> = vec![0, 1, 2, 65535, 65536, 65537, p - 4, p - 3, p - 2, p - 1];
277
278        let a: Vec<Target> = a64
279            .iter()
280            .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x)))
281            .collect();
282
283        let r: Vec<Target> = a.iter()
284            .map(|targ| Mod65537Builder::mod_65537(&mut builder, *targ))
285            .collect();
286
287        // check that the outputs are correct,
288        // obviously you don't need this in your own code
289        let r_expected64: Vec<u64> = a64.iter().map(|x| x % 65537).collect();
290        println!("Expected residues mod 64: {:?}", r_expected64);
291        let r_expected: Vec<Target> = r_expected64.iter()
292            .map(|x| builder.constant(GoldilocksField::from_canonical_u64(*x)))
293            .collect();
294        r.iter().zip(r_expected.iter())
295            .for_each(|(x, y)| builder.connect(*x, *y));
296        
297        let mut pw: PartialWitness<F> = PartialWitness::new();
298        let data = builder.build::<C>();
299        let proof = data.prove(pw).unwrap();
300
301        ()
302    }
303
304    #[test]
305    fn test_schnorr() -> () {
306        const D: usize = 2;
307        type C = PoseidonGoldilocksConfig;
308        type F = <C as GenericConfig<D>>::F;
309
310        let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
311
312        let config = CircuitConfig::standard_recursion_config();
313        let mut builder = CircuitBuilder::<F, D>::new(config);
314
315        let sb: SchnorrBuilder = SchnorrBuilder{};
316
317        // create keypair, message, signature
318        let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
319        let ss = SchnorrSigner::new();
320        let pk: SchnorrPublicKey = ss.keygen(&sk);
321        let msg: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
322            vec![1500, 1600, 0, 0, 0]
323        );
324        let msg_size: usize = msg.len();
325        let sig: SchnorrSignature = ss.sign(&msg, &sk, &mut rng);
326
327        let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
328        let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
329        let msg_targ = MessageTarget::new_with_size(&mut builder, msg_size);
330
331        
332        // instead of verifying we're going to prove the verification
333        sb.constrain_sig::<PoseidonGoldilocksConfig> (
334            &mut builder,
335            &sig_targ,
336            &msg_targ,
337            &pk_targ
338        );
339
340        // assign witnesses for verification
341        let mut pw: PartialWitness<F> = PartialWitness::new();
342        pk_targ.set_witness(&mut pw, &pk);
343        sig_targ.set_witness(&mut pw, &sig);
344        msg_targ.set_witness(&mut pw, &msg);
345
346
347        let data = builder.build::<C>();
348        let proof = data.prove(pw).unwrap();
349    }
350}