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::{
 32    mod65537::{Mod65537Builder, Mod65537Generator},
 33    schnorr::{SchnorrPublicKey, SchnorrSignature},
 34};
 35
 36type GoldF = GoldilocksField;
 37
 38pub struct MessageTarget {
 39    msg: Vec<Target>,
 40}
 41
 42impl MessageTarget {
 43    fn new_with_size(builder: &mut CircuitBuilder<GoldF, 2>, n: usize) -> Self {
 44        Self {
 45            msg: builder.add_virtual_targets(n),
 46        }
 47    }
 48
 49    fn set_witness(&self, pw: &mut PartialWitness<GoldF>, msg: &Vec<GoldF>) -> Result<()> {
 50        assert!(msg.len() == self.msg.len());
 51        for (&t, &x) in self.msg.iter().zip(msg.iter()) {
 52            pw.set_target(t, x)?;
 53        }
 54
 55        Ok(())
 56    }
 57}
 58
 59pub struct SchnorrSignatureTarget {
 60    s: Target,
 61    e: Target,
 62}
 63
 64impl SchnorrSignatureTarget {
 65    fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
 66        let s = builder.add_virtual_target();
 67        let e = builder.add_virtual_target();
 68        Self{ s, e }
 69    }
 70
 71    fn set_witness(&self, pw: &mut PartialWitness<GoldF>, sig: &SchnorrSignature) -> Result<()> {
 72        pw.set_target(self.s, GoldilocksField::from_canonical_u64(sig.s))?;
 73        pw.set_target(self.e, GoldilocksField::from_canonical_u64(sig.e))?;
 74        Ok(())
 75    }
 76}
 77
 78pub struct SchnorrPublicKeyTarget {
 79    pk: Target,
 80}
 81
 82impl SchnorrPublicKeyTarget {
 83    fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
 84        Self{ pk: builder.add_virtual_target() }
 85    }
 86
 87    fn set_witness(&self, pw: &mut PartialWitness<GoldF>, pk: &SchnorrPublicKey) -> Result<()> {
 88        pw.set_target(self.pk, pk.pk)?;
 89        Ok(())
 90    }
 91}
 92
 93pub struct SchnorrBuilder {}
 94
 95impl SchnorrBuilder {
 96
 97
 98    pub fn constrain_sig <
 99        C: GenericConfig<2, F = GoldF>,
100    > (
101        &self,
102        builder: &mut CircuitBuilder::<GoldF, 2>,
103        sig: &SchnorrSignatureTarget,
104        msg: &MessageTarget,
105        pk: &SchnorrPublicKeyTarget,
106    ) -> () {
107        let PRIME_GROUP_GEN: Target = builder.constant(GoldF::from_canonical_u64(6612579038192137166));
108        let PRIME_GROUP_ORDER: Target = builder.constant(GoldF::from_canonical_u64(65537));
109        const num_bits_exp: usize = 32;
110
111        /* here's the direct verification calculation,
112        which we verify in-circuit
113        let r: GoldF = Self::pow(self.PRIME_GROUP_GEN, sig.s)
114            * Self::pow(pk.pk, sig.e);
115        let e_v: u64 = self.hash_insecure(&r, msg);
116        e_v == sig.e   */
117
118        let gs: Target = builder.exp(PRIME_GROUP_GEN, sig.s, num_bits_exp);
119        let pe: Target = builder.exp(pk.pk, sig.e, num_bits_exp);
120        let r: Target = builder.mul(gs, pe);
121
122        // compute hash
123        // note that it's safe to clone Targets since they just contain indices
124        let hash_input: Vec<Target> = std::iter::once(r)
125            .chain(msg.msg.iter().cloned()) 
126            .collect();
127        let hash_output: Target = builder.hash_n_to_hash_no_pad::<PoseidonHash>(
128            hash_input,
129        ).elements[0]; // whoops have to take mod group order;
130
131        let e: Target = Mod65537Builder::mod_65537(builder, hash_output);
132
133        // enforce equality
134        builder.connect(e, sig.e);
135    }
136}
137
138#[cfg(test)]
139mod tests{
140    use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature};
141    use crate::schnorr_prover::{MessageTarget, Mod65537Builder, SchnorrBuilder, SchnorrPublicKeyTarget, SchnorrSignatureTarget};
142    use plonky2::hash::poseidon::Poseidon;
143    use plonky2::iop::{
144        target::Target,
145        witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite},
146    };
147    use plonky2::plonk::{
148        circuit_builder::CircuitBuilder,
149        circuit_data::{
150            CircuitConfig, 
151            CircuitData, 
152            CommonCircuitData, 
153            VerifierCircuitData, 
154            VerifierOnlyCircuitData
155        },
156        config::{GenericConfig, PoseidonGoldilocksConfig},
157    };
158    use plonky2::field::{
159        goldilocks_field::GoldilocksField,
160        types::Field,
161    };
162    use rand;
163
164    #[test]
165    fn test_schnorr() -> () {
166        const D: usize = 2;
167        type C = PoseidonGoldilocksConfig;
168        type F = <C as GenericConfig<D>>::F;
169
170        let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
171
172        let config = CircuitConfig::standard_recursion_config();
173        let mut builder = CircuitBuilder::<F, D>::new(config);
174
175        let sb: SchnorrBuilder = SchnorrBuilder{};
176
177        // create keypair, message, signature
178        let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
179        let ss = SchnorrSigner::new();
180        let pk: SchnorrPublicKey = ss.keygen(&sk);
181        let msg: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
182            vec![1500, 1600, 0, 0, 0]
183        );
184        let msg_size: usize = msg.len();
185        let sig: SchnorrSignature = ss.sign(&msg, &sk, &mut rng);
186
187        let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
188        let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
189        let msg_targ = MessageTarget::new_with_size(&mut builder, msg_size);
190
191        
192        // instead of verifying we're going to prove the verification
193        sb.constrain_sig::<PoseidonGoldilocksConfig> (
194            &mut builder,
195            &sig_targ,
196            &msg_targ,
197            &pk_targ
198        );
199
200        // assign witnesses for verification
201        let mut pw: PartialWitness<F> = PartialWitness::new();
202        pk_targ.set_witness(&mut pw, &pk);
203        sig_targ.set_witness(&mut pw, &sig);
204        msg_targ.set_witness(&mut pw, &msg);
205
206
207        let data = builder.build::<C>();
208        let proof = data.prove(pw).unwrap();
209    }
210}