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}