arnaucube
arnaucube/schnorrPublic Self-hosted repository
schnorr/src/schnorr_prover.rs
src/schnorr_prover.rsRaw
  1use anyhow::Result;
  2
  3use plonky2::iop::{
  4    target::Target,
  5    witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite},
  6};
  7use plonky2::field::{
  8    goldilocks_field::GoldilocksField,
  9    types::{Field, PrimeField64},
 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
 81impl SchnorrBuilder {
 82
 83
 84    pub fn constrain_sig <
 85        C: GenericConfig<2, F = GoldF>,
 86    > (
 87        &self,
 88        builder: &mut CircuitBuilder::<GoldF, 2>,
 89        sig: &SchnorrSignatureTarget,
 90        msg: &MessageTarget,
 91        pk: &SchnorrPublicKeyTarget,
 92    ) -> () {
 93        let PRIME_GROUP_GEN: Target = builder.constant(GoldF::from_canonical_u64(6612579038192137166));
 94        let PRIME_GROUP_ORDER: Target = builder.constant(GoldF::from_canonical_u64(65537));
 95        const num_bits_exp: usize = 32;
 96
 97        /* here's the direct verification calculation,
 98        which we verify in-circuit
 99        let r: GoldF = Self::pow(self.PRIME_GROUP_GEN, sig.s)
100            * Self::pow(pk.pk, sig.e);
101        let e_v: u64 = self.hash_insecure(&r, msg);
102        e_v == sig.e   */
103
104        let gs: Target = builder.exp(PRIME_GROUP_GEN, sig.s, num_bits_exp);
105        let pe: Target = builder.exp(pk.pk, sig.e, num_bits_exp);
106        let r: Target = builder.mul(gs, pe);
107
108        // compute hash
109        // note that it's safe to clone Targets since they just contain indices
110        let hash_input: Vec<Target> = std::iter::once(r)
111            .chain(msg.msg.iter().cloned()) 
112            .collect();
113        let hash_output: Target = builder.hash_n_to_hash_no_pad::<PoseidonHash>(
114            hash_input,
115        ).elements[0]; // whoops have to take mod group order;
116
117        let e: Target = Mod65537Builder::mod_65537(builder, hash_output);
118
119        // enforce equality
120        builder.connect(e, sig.e);
121    }
122}
123
124#[cfg(test)]
125mod tests{
126    use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature};
127    use crate::schnorr_prover::{MessageTarget, Mod65537Builder, SchnorrBuilder, SchnorrPublicKeyTarget, SchnorrSignatureTarget};
128    use plonky2::hash::poseidon::Poseidon;
129    use plonky2::iop::{
130        target::Target,
131        witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite},
132    };
133    use plonky2::plonk::{
134        circuit_builder::CircuitBuilder,
135        circuit_data::{
136            CircuitConfig, 
137            CircuitData, 
138            CommonCircuitData, 
139            VerifierCircuitData, 
140            VerifierOnlyCircuitData
141        },
142        config::{GenericConfig, PoseidonGoldilocksConfig},
143    };
144    use plonky2::field::{
145        goldilocks_field::GoldilocksField,
146        types::Field,
147    };
148    use rand;
149
150    #[test]
151    fn test_schnorr() -> () {
152        const D: usize = 2;
153        type C = PoseidonGoldilocksConfig;
154        type F = <C as GenericConfig<D>>::F;
155
156        let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
157
158        let config = CircuitConfig::standard_recursion_config();
159        let mut builder = CircuitBuilder::<F, D>::new(config);
160
161        let sb: SchnorrBuilder = SchnorrBuilder{};
162
163        // create keypair, message, signature
164        let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
165        let ss = SchnorrSigner::new();
166        let pk: SchnorrPublicKey = ss.keygen(&sk);
167        let msg: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
168            vec![1500, 1600, 0, 0, 0]
169        );
170        let msg_size: usize = msg.len();
171        let sig: SchnorrSignature = ss.sign(&msg, &sk, &mut rng);
172
173        let pk_targ = SchnorrPublicKeyTarget::new_virtual(&mut builder);
174        let sig_targ = SchnorrSignatureTarget::new_virtual(&mut builder);
175        let msg_targ = MessageTarget::new_with_size(&mut builder, msg_size);
176
177        
178        // instead of verifying we're going to prove the verification
179        sb.constrain_sig::<PoseidonGoldilocksConfig> (
180            &mut builder,
181            &sig_targ,
182            &msg_targ,
183            &pk_targ
184        );
185
186        // assign witnesses for verification
187        let mut pw: PartialWitness<F> = PartialWitness::new();
188        pk_targ.set_witness(&mut pw, &pk);
189        sig_targ.set_witness(&mut pw, &sig);
190        msg_targ.set_witness(&mut pw, &msg);
191
192
193        let data = builder.build::<C>();
194        let proof = data.prove(pw).unwrap();
195    }
196}