11//! Schnorr signature verification
22
33use crate :: ecc:: chip:: { AssignedEccPoint , EccChip , EccConfig , EccInstructions , ScalarVar } ;
4- use crate :: instructions:: MainGateInstructions ;
4+ use crate :: instructions:: { MainGateInstructions , Term } ;
5+ use num_integer:: Integer ;
6+
57use crate :: rescue:: {
68 RescueCrhfGate , RescueCrhfGateConfig , RescueCrhfInstructions , RescueParametersBls ,
79} ;
8- use crate :: util:: RegionCtx ;
10+ use crate :: util:: { big_to_fe , fe_to_big , RegionCtx } ;
911use crate :: AssignedValue ;
10- use ff:: Field ;
12+ use ff:: { Field , PrimeField } ;
1113use group:: prime:: PrimeCurveAffine ;
1214use group:: { Curve , Group } ;
1315use halo2_proofs:: circuit:: { Chip , Value } ;
@@ -74,6 +76,11 @@ impl SchnorrVerifierGate {
7476 let four_pk = self . ecc_gate . add ( ctx, & two_pk, & two_pk) ?;
7577 let eight_pk = self . ecc_gate . add ( ctx, & four_pk, & four_pk) ?;
7678
79+ let assigned_generator = self . ecc_gate . witness_point (
80+ ctx,
81+ & Value :: known ( ExtendedPoint :: from ( SubgroupPoint :: generator ( ) ) . to_affine ( ) ) ,
82+ ) ?;
83+
7784 let one = self
7885 . ecc_gate
7986 . main_gate
@@ -85,21 +92,197 @@ impl SchnorrVerifierGate {
8592 . assert_not_equal ( ctx, & eight_pk. y , & one) ?;
8693
8794 let input_hash = [ signature. 0 . x . clone ( ) , pk. x . clone ( ) , msg. clone ( ) ] ;
88- let challenge = self . rescue_hash_gate . hash ( ctx, & input_hash) ?;
95+ let challenge = self . rescue_hash_gate . hash ( ctx, & input_hash) ?; // larger than mod with high prob
8996
90- let lhs = self . ecc_gate . fixed_mul (
97+ let lhs = self . combined_mul (
9198 ctx,
92- & signature. 1 ,
93- ExtendedPoint :: from ( SubgroupPoint :: generator ( ) ) . to_affine ( ) ,
99+ & signature. 1 . 0 ,
100+ & challenge,
101+ & assigned_generator,
102+ pk,
94103 ) ?;
95- let rhs_1 = self . ecc_gate . mul ( ctx, & ScalarVar ( challenge) , pk) ?;
96- let rhs = self . ecc_gate . add ( ctx, & signature. 0 , & rhs_1) ?;
97104
98- self . ecc_gate . constrain_equal ( ctx, & lhs, & rhs ) ?;
105+ self . ecc_gate . constrain_equal ( ctx, & lhs, & signature . 0 ) ?;
99106
100107 Ok ( ( ) )
101108 }
102109
110+ // We need to negate the second scalar prior to the addition
111+ fn combined_mul (
112+ & self ,
113+ ctx : & mut RegionCtx < ' _ , Base > ,
114+ scalar_1 : & AssignedValue < Base > ,
115+ scalar_2 : & AssignedValue < Base > ,
116+ base_1 : & AssignedEccPoint ,
117+ base_2 : & AssignedEccPoint ,
118+ ) -> Result < AssignedEccPoint , Error > {
119+ // We compute a combined mul for `signature.1 * generator - challenge * pk`. For that
120+ // we first negate the challenge, and the compute use combined_mul. We negate with
121+ // respect to the Scalar modulus bytes. However, we allow the challenge to be above the
122+ // modulus, because the loss in security is not a concern. However, this slightly complicates
123+ // this negation.
124+ // We need to find (a,b) = challenge.div_remainder(modulus), then compute
125+ // neg_challenge = modulus - b, and prove that challenge + neg_challenge = (a + 1) * modulus
126+ // With this we should save ~250 constraints, so probably worth it.
127+
128+ let jub_jub_scalar_bytes = [
129+ 183 , 44 , 247 , 214 , 94 , 14 , 151 , 208 , 130 , 16 , 200 , 204 , 147 , 32 , 104 , 166 , 0 , 59 , 52 ,
130+ 1 , 1 , 59 , 103 , 6 , 169 , 175 , 51 , 101 , 234 , 180 , 125 , 14 ,
131+ ] ;
132+
133+ let jubjub_mod =
134+ Base :: from_bytes ( & jub_jub_scalar_bytes) . expect ( "Failed to deserialise modulus" ) ; // This will not fail as jubjub mod is smaller than BLS
135+
136+ let mult_remainder = scalar_2. value ( ) . map ( |& val| {
137+ let ( mult, remainder) = fe_to_big ( val) . div_rem ( & fe_to_big ( jubjub_mod) ) ;
138+ [ big_to_fe ( mult) , big_to_fe ( remainder) ]
139+ } ) . transpose_vec ( 2 ) ;
140+
141+ self . ecc_gate . main_gate . assert_zero_sum (
142+ ctx,
143+ & [ Term :: Assigned ( scalar_2, - Base :: ONE ) , Term :: Unassigned ( mult_remainder[ 0 ] , jubjub_mod) , Term :: Unassigned ( mult_remainder[ 1 ] , Base :: ONE ) ] ,
144+ Base :: ZERO
145+ ) ?;
146+
147+ let neg_scalar_2 = self
148+ . ecc_gate
149+ . main_gate
150+ . assign_value ( ctx, mult_remainder[ 1 ] . map ( |val| jubjub_mod - val) ) ?;
151+
152+ self . ecc_gate . main_gate . assert_zero_sum (
153+ ctx,
154+ & [ Term :: Assigned ( scalar_2, Base :: ONE ) , Term :: Assigned ( & neg_scalar_2, Base :: ONE ) , Term :: Unassigned ( mult_remainder[ 0 ] + Value :: known ( Base :: ONE ) , - jubjub_mod) ] ,
155+ Base :: ZERO
156+ ) ?;
157+
158+ // Decompose scalar into bits
159+ let mut decomposition_1 =
160+ self . ecc_gate
161+ . main_gate
162+ . to_bits ( ctx, & scalar_1, Base :: NUM_BITS as usize ) ?;
163+ decomposition_1. reverse ( ) ; // to get MSB first
164+
165+ let mut decomposition_2 =
166+ self . ecc_gate
167+ . main_gate
168+ . to_bits ( ctx, & neg_scalar_2, Base :: NUM_BITS as usize ) ?;
169+ decomposition_2. reverse ( ) ; // to get MSB first
170+
171+ // Initialise the aggregator at zero
172+ let assigned_0x = ctx. assign_advice (
173+ || "x of zero" ,
174+ self . ecc_gate . config . add . x_pr ,
175+ Value :: known ( Base :: ZERO ) ,
176+ ) ?;
177+ ctx. next ( ) ;
178+
179+ let assigned_0y = ctx. assign_advice (
180+ || "y of zero" ,
181+ self . ecc_gate . config . add . y_pr ,
182+ Value :: known ( Base :: ONE ) ,
183+ ) ?;
184+ ctx. assign_fixed ( || "base" , self . ecc_gate . main_gate . config . sb , Base :: ONE ) ?;
185+ ctx. assign_fixed (
186+ || "s_constant" ,
187+ self . ecc_gate . main_gate . config . s_constant ,
188+ -Base :: ONE ,
189+ ) ?;
190+ ctx. next ( ) ;
191+
192+ // We copy the aggregator to its right position
193+ let assigned_aggr_x = ctx. copy_advice (
194+ || "x aggregator" ,
195+ self . ecc_gate . config . add . x_pr ,
196+ assigned_0x,
197+ ) ?;
198+ let assigned_aggr_y = ctx. copy_advice (
199+ || "y aggregator" ,
200+ self . ecc_gate . config . add . y_pr ,
201+ assigned_0y. clone ( ) ,
202+ ) ?;
203+
204+ let mut assigned_aggr = AssignedEccPoint {
205+ x : assigned_aggr_x,
206+ y : assigned_aggr_y,
207+ } ;
208+
209+ for ( bit_1, bit_2) in decomposition_1. into_iter ( ) . zip ( decomposition_2. into_iter ( ) ) {
210+ // We copy the aggregator into the `q` cell for doubling
211+ let assigned_aggr_x = ctx. copy_advice (
212+ || "x aggregator double" ,
213+ self . ecc_gate . config . add . x_q ,
214+ assigned_aggr. x . clone ( ) ,
215+ ) ?;
216+ let assigned_aggr_y = ctx. copy_advice (
217+ || "y aggregator double" ,
218+ self . ecc_gate . config . add . y_q ,
219+ assigned_aggr. y . clone ( ) ,
220+ ) ?;
221+
222+ let assigned_aggr_q = AssignedEccPoint {
223+ x : assigned_aggr_x,
224+ y : assigned_aggr_y,
225+ } ;
226+
227+ // We copy one for always performing doubling
228+ let b = ctx. copy_advice ( || "one" , self . ecc_gate . config . add . b , assigned_0y. clone ( ) ) ?;
229+
230+ // We perform doubling
231+ assigned_aggr = self
232+ . ecc_gate
233+ . cond_add ( ctx, & assigned_aggr, & assigned_aggr_q, & b) ?;
234+
235+ // Now we conditionally perform addition of the first point. We begin by copying the base point to the `q` cell
236+ let base_x = ctx. copy_advice (
237+ || "x point cond add" ,
238+ self . ecc_gate . config . add . x_q ,
239+ base_1. x . clone ( ) ,
240+ ) ?;
241+ let base_y = ctx. copy_advice (
242+ || "y point cond add" ,
243+ self . ecc_gate . config . add . y_q ,
244+ base_1. y . clone ( ) ,
245+ ) ?;
246+
247+ let base_q = AssignedEccPoint {
248+ x : base_x,
249+ y : base_y,
250+ } ;
251+
252+ // We now copy the bit to its right position
253+ let b = ctx. copy_advice ( || "b1" , self . ecc_gate . config . add . b , bit_1) ?;
254+
255+ // Aggr = Aggr + cond_add
256+ assigned_aggr = self . ecc_gate . cond_add ( ctx, & assigned_aggr, & base_q, & b) ?;
257+
258+ // Now we conditionally perform addition of the second point. We begin by copying the base point to the `q` cell
259+ let base_x = ctx. copy_advice (
260+ || "x point cond add" ,
261+ self . ecc_gate . config . add . x_q ,
262+ base_2. x . clone ( ) ,
263+ ) ?;
264+ let base_y = ctx. copy_advice (
265+ || "y point cond add" ,
266+ self . ecc_gate . config . add . y_q ,
267+ base_2. y . clone ( ) ,
268+ ) ?;
269+
270+ let base_q = AssignedEccPoint {
271+ x : base_x,
272+ y : base_y,
273+ } ;
274+
275+ // We now copy the bit to its right position
276+ let b = ctx. copy_advice ( || "b2" , self . ecc_gate . config . add . b , bit_2) ?;
277+
278+ // Aggr = Aggr + cond_add
279+ assigned_aggr = self . ecc_gate . cond_add ( ctx, & assigned_aggr, & base_q, & b) ?;
280+ }
281+ ctx. next ( ) ;
282+
283+ Ok ( assigned_aggr)
284+ }
285+
103286 /// Assign a schnorr signature
104287 pub fn assign_sig (
105288 & self ,
@@ -189,6 +372,7 @@ mod tests {
189372 let mut ctx = RegionCtx :: new ( region, offset) ;
190373 let assigned_sig =
191374 schnorr_gate. assign_sig ( & mut ctx, & Value :: known ( self . signature ) ) ?;
375+
192376 let assigned_msg = schnorr_gate
193377 . ecc_gate
194378 . main_gate
@@ -234,6 +418,7 @@ mod tests {
234418 let prover =
235419 MockProver :: run ( K , & circuit, pi) . expect ( "Failed to run Schnorr verifier mock prover" ) ;
236420
421+ prover. assert_satisfied ( ) ;
237422 assert ! ( prover. verify( ) . is_ok( ) ) ;
238423
239424 // We try to verify for a different message (the hash of the PI
0 commit comments