schnorr/src/schnorr_prover.rs
src/schnorr_prover.rsRaw
1use anyhow::Result;
2use plonky2::iop::generator::{GeneratedValues, SimpleGenerator};
3use plonky2::iop::target::{BoolTarget, Target};
4use plonky2::iop::witness::{PartialWitness, PartitionWitness, Witness, WitnessWrite};
5use plonky2::field::extension::{Extendable, FieldExtension};
6use plonky2::field::types::{Field, PrimeField64};
7use plonky2::field::goldilocks_field::GoldilocksField;
8use plonky2::hash::hash_types::RichField;
9use plonky2::hash::poseidon::PoseidonHash;
10use plonky2::plonk::circuit_builder::CircuitBuilder;
11use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierOnlyCircuitData};
12use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
13use plonky2::plonk::proof::ProofWithPublicInputs;
14use plonky2::util::serialization::{Buffer, IoResult, Read, Write};
15
16type GoldF = GoldilocksField;
17
18pub struct MessageTarget {
19 msg: Vec<Target>,
20}
21
22impl MessageTarget {
23 fn new_with_size(builder: &mut CircuitBuilder<GoldF, 2>, n: usize) -> Self {
24 Self {
25 msg: builder.add_virtual_targets(n),
26 }
27 }
28}
29
30pub struct SchnorrSignatureTarget {
31 s: Target,
32 e: Target,
33}
34
35impl SchnorrSignatureTarget {
36 fn new_virtual(builder: &mut CircuitBuilder<GoldF, 2>) -> Self {
37 let s = builder.add_virtual_target();
38 let e = builder.add_virtual_target();
39 Self{ s, e }
40 }
41}
42
43pub struct SchnorrPublicKeyTarget {
44 pk: Target,
45}
46
47#[derive(Debug, Default)]
48pub struct Mod65537Generator {
49 a: Target,
50 q: Target,
51 r: Target,
52}
53
54impl SimpleGenerator<GoldF, 2> for Mod65537Generator {
55 fn id(&self) -> String {
56 "Mod65537Generator".to_string()
57 }
58 fn dependencies(&self) -> Vec<Target> {
59 vec![self.a]
60 }
61
62 fn run_once(
63 &self,
64 witness: &PartitionWitness<GoldF>,
65 out_buffer: &mut GeneratedValues<GoldF>,
66 ) -> Result<()> {
67 let a = witness.get_target(self.a);
68 let a64 = a.to_canonical_u64();
69 let q64 = a64 / 65537;
70 let r64 = a64 % 65537;
71
72 out_buffer.set_target(self.q, GoldF::from_canonical_u64(q64));
73 out_buffer.set_target(self.r, GoldF::from_canonical_u64(r64));
74
75 Ok(())
76 }
77
78 fn serialize(&self, dst: &mut Vec<u8>, common_data: &CommonCircuitData<GoldF, 2>) -> IoResult<()> {
79 println!("SERIALIZATION! What is this good for?");
80 dst.write_target(self.a)?;
81 dst.write_target(self.q)?;
82 dst.write_target(self.r)?;
83 Ok(())
84 }
85
86 fn deserialize(src: &mut Buffer, common_data: &CommonCircuitData<GoldF, 2>) -> IoResult<Self>
87 where
88 Self: Sized
89 {
90 println!("DESERIALIZATION! What is this good for?");
91 let a = src.read_target()?;
92 let q = src.read_target()?;
93 let r = src.read_target()?;
94 OK(Self { a, q, r })
95 }
96}
97
98pub struct SchnorrBuilder {
99
100}
101
102impl SchnorrBuilder {
103 // Reduce a modulo the constant 65537
104 // where a is the canonical representative for an element of the field
105 // (meaning: 0 \leq a < p)
106
107 // To verify this, write
108 // a = 65537 * q + r, and do range checks to check that:
109 // 0 <= q <= floor(p / 65537)
110 // 0 <= r < 65537
111 // (these first two checks guarantee that a lies in the range [0, p + 65536])
112 // if q = floor(p / 65537) then r = 0
113 // (note that p % 65537 == 1 so this is the only possibility)
114 fn mod_65537 <
115 C: GenericConfig<2, F = GoldF>,
116 > (
117 builder: &mut CircuitBuilder::<GoldF, 2>,
118 a: &Target,
119 ) -> Target {
120 let q = builder.add_virtual_target();
121 let r = builder.add_virtual_target();
122
123 // the Mod65537Generator will assign values to q and r later
124 builder.add_simple_generator( Mod65537Generator { a, q, r } );
125
126 // impose four constraints
127 // 1. a = 65537 * q + r
128 let t65537 = builder.constant(GoldF::from_canonical_u64(65537));
129 let a_copy = builder.mul_add(t65537, q, r);
130 builder.connect(*a, a_copy);
131
132 // 2. 0 <= q <= floor(p / 65537)
133 // max_q is 281470681743360 = floor(p / 65537) = (p-1) / 65537 = 2^48 - 2^32
134 let max_q = builder.constant(GoldF::from_canonical_u64(281470681743360));
135 builder.range_check(q, 48);
136 builder.range_check(builder.sub(max_q, q), 48);
137
138 // 3. 0 <= r < 65537
139 let max_r = builder.constant(GoldF::from_canonical_u64(65537));
140 builder.range_check(r, 17);
141 builder.range_check(builder.sub(max_r, r), 17);
142
143 // 4. if q = floor(p / 65537) then r = 0
144 let q_equals_max = builder.is_equal(q, max_q);
145 builder.connect(builder.mul(q_equals_max.target, r), builder.zero());
146
147 r
148 }
149
150 fn constrain_sig <
151 C: GenericConfig<2, F = GoldF>,
152 > (
153 &self,
154 builder: &mut CircuitBuilder::<GoldF, 2>,
155 sig: &SchnorrSignatureTarget,
156 msg: &MessageTarget,
157 pk: &SchnorrPublicKeyTarget,
158 ) -> () {
159 let PRIME_GROUP_GEN: Target = builder.constant(GoldF::from_canonical_u64(6612579038192137166));
160 let PRIME_GROUP_ORDER: Target = builder.constant(GoldF::from_canonical_u64(65537));
161 const num_bits_exp: usize = 32;
162
163 /*
164 let r: GoldF = Self::pow(self.PRIME_GROUP_GEN, sig.s)
165 * Self::pow(pk.pk, sig.e);
166 let e_v: u64 = self.hash_insecure(&r, msg);
167 e_v == sig.e */
168
169 let gs: Target = builder.exp(PRIME_GROUP_GEN, sig.s, num_bits_exp);
170 let pe: Target = builder.exp(pk.pk, sig.e, num_bits_exp);
171 let r: Target = builder.mul(gs, pe);
172
173 // compute hash
174 // note that it's safe to clone Targets since they just contain indices
175 let hash_input: Vec<Target> = std::iter::once(r)
176 .chain(msg.msg.iter().cloned())
177 .collect();
178 let e: Target = builder.hash_n_to_hash_no_pad::<PoseidonHash>(
179 hash_input,
180 ).elements[0] // whoops have to take mod group order;
181
182 // enforce equality
183 builder.connect(e, sig.e);
184 }
185}
186
187#[cfg(test)]
188mod tests{
189 use crate::schnorr::{SchnorrPublicKey, SchnorrSecretKey, SchnorrSigner, SchnorrSignature};
190 use crate::schnorr_prover::SchnorrBuilder;
191 use plonky2::plonk::circuit_builder::CircuitBuilder;
192 use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData, CommonCircuitData, VerifierCircuitData, VerifierOnlyCircuitData};
193 use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig};
194 use plonky2::field::goldilocks_field::GoldilocksField;
195 use rand;
196
197 #[test]
198 fn test_schnorr() -> () {
199 const D: usize = 2;
200 type C = PoseidonGoldilocksConfig;
201 type F = <C as GenericConfig<D>>::F;
202
203 let mut rng: rand::rngs::ThreadRng = rand::thread_rng();
204
205 let config = CircuitConfig::standard_recursion_config();
206 let mut builder = CircuitBuilder::<F, D>::new(config);
207
208 builder.add_virtual_fri_proof(num_leaves_per_oracle, params)
209
210 let sb: SchnorrBuilder = SchnorrBuilder{};
211
212 // create keypair, message, signature
213 let sk: SchnorrSecretKey = SchnorrSecretKey{ sk: 133 };
214 let ss = SchnorrSigner::new();
215 let pk: SchnorrPublicKey = ss.keygen(&sk);
216 let msg: Vec<GoldilocksField> = ss.u64_into_goldilocks_vec(
217 vec![1500, 1600, 0, 0, 0]
218 );
219 let msg_size: usize = msg.len();
220 let sig: SchnorrSignature = ss.sign(&msg, &sk, &mut rng);
221
222 let sig_target = builder.constant(sig);
223 // instead of verifying we're going to prove the verification
224 sb.constrain_sig(
225 &mut builder,
226 &sig,
227 &msg,
228 &pk
229 );
230 }
231}