@@ -137,24 +137,135 @@ static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *p
137137 secp256k1_ge_globalz_set_table_gej (ECMULT_TABLE_SIZE (WINDOW_A ), pre , globalz , prej , zr );
138138}
139139
140- static void secp256k1_ecmult_odd_multiples_table_storage_var (int n , secp256k1_ge_storage * pre , const secp256k1_gej * a , const secp256k1_callback * cb ) {
141- secp256k1_gej * prej = (secp256k1_gej * )checked_malloc (cb , sizeof (secp256k1_gej ) * n );
142- secp256k1_ge * prea = (secp256k1_ge * )checked_malloc (cb , sizeof (secp256k1_ge ) * n );
143- secp256k1_fe * zr = (secp256k1_fe * )checked_malloc (cb , sizeof (secp256k1_fe ) * n );
140+ static void secp256k1_ecmult_odd_multiples_table_storage_var (const int n , secp256k1_ge_storage * pre , const secp256k1_gej * a ) {
141+ secp256k1_gej d ;
142+ secp256k1_ge d_ge , p_ge ;
143+ secp256k1_gej pj ;
144+ secp256k1_fe zi ;
145+ secp256k1_fe zr ;
146+ secp256k1_fe dx_over_dz_squared ;
144147 int i ;
145148
146- /* Compute the odd multiples in Jacobian form. */
147- secp256k1_ecmult_odd_multiples_table (n , prej , zr , a );
148- /* Convert them in batch to affine coordinates. */
149- secp256k1_ge_set_table_gej_var (prea , prej , zr , n );
150- /* Convert them to compact storage form. */
151- for (i = 0 ; i < n ; i ++ ) {
152- secp256k1_ge_to_storage (& pre [i ], & prea [i ]);
149+ VERIFY_CHECK (!a -> infinity );
150+
151+ secp256k1_gej_double_var (& d , a , NULL );
152+
153+ /* First, we perform all the additions in an isomorphic curve obtained by multiplying
154+ * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use
155+ * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store
156+ * the resulting y-coordinate and the z-ratio, since we only have enough memory to
157+ * store two field elements. These are sufficient to efficiently undo the isomorphism
158+ * and recompute all the `x`s.
159+ */
160+ d_ge .x = d .x ;
161+ d_ge .y = d .y ;
162+ d_ge .infinity = 0 ;
163+
164+ secp256k1_ge_set_gej_zinv (& p_ge , a , & d .z );
165+ pj .x = p_ge .x ;
166+ pj .y = p_ge .y ;
167+ pj .z = a -> z ;
168+ pj .infinity = 0 ;
169+
170+ for (i = 0 ; i < (n - 1 ); i ++ ) {
171+ secp256k1_fe_normalize_var (& pj .y );
172+ secp256k1_fe_to_storage (& pre [i ].y , & pj .y );
173+ secp256k1_gej_add_ge_var (& pj , & pj , & d_ge , & zr );
174+ secp256k1_fe_normalize_var (& zr );
175+ secp256k1_fe_to_storage (& pre [i ].x , & zr );
153176 }
154177
155- free (prea );
156- free (prej );
157- free (zr );
178+ /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */
179+ secp256k1_fe_mul (& zi , & pj .z , & d .z );
180+ secp256k1_fe_inv_var (& zi , & zi );
181+
182+ /* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so
183+ * that we can combine it with the saved z-ratios to compute the other zs
184+ * without any more inversions. */
185+ secp256k1_ge_set_gej_zinv (& p_ge , & pj , & zi );
186+ secp256k1_ge_to_storage (& pre [n - 1 ], & p_ge );
187+
188+ /* Compute the actual x-coordinate of D, which will be needed below. */
189+ secp256k1_fe_mul (& d .z , & zi , & pj .z ); /* d.z = 1/d.z */
190+ secp256k1_fe_sqr (& dx_over_dz_squared , & d .z );
191+ secp256k1_fe_mul (& dx_over_dz_squared , & dx_over_dz_squared , & d .x );
192+
193+ /* Going into the second loop, we have set `pre[n-1]` to its final affine
194+ * form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We
195+ * have `zi = (p.z * d.z)^-1`, where
196+ *
197+ * `p.z` is the z-coordinate of the point on the isomorphic curve
198+ * which was ultimately assigned to `pre[n-1]`.
199+ * `d.z` is the multiplier that must be applied to all z-coordinates
200+ * to move from our isomorphic curve back to secp256k1; so the
201+ * product `p.z * d.z` is the z-coordinate of the secp256k1
202+ * point assigned to `pre[n-1]`.
203+ *
204+ * All subsequent inverse-z-coordinates can be obtained by multiplying this
205+ * factor by successive z-ratios, which is much more efficient than directly
206+ * computing each one.
207+ *
208+ * Importantly, these inverse-zs will be coordinates of points on secp256k1,
209+ * while our other stored values come from computations on the isomorphic
210+ * curve. So in the below loop, we will take care not to actually use `zi`
211+ * or any derived values until we're back on secp256k1.
212+ */
213+ i = n - 1 ;
214+ while (i > 0 ) {
215+ secp256k1_fe zi2 , zi3 ;
216+ const secp256k1_fe * rzr ;
217+ i -- ;
218+
219+ secp256k1_ge_from_storage (& p_ge , & pre [i ]);
220+
221+ /* For each remaining point, we extract the z-ratio from the stored
222+ * x-coordinate, compute its z^-1 from that, and compute the full
223+ * point from that. */
224+ rzr = & p_ge .x ;
225+ secp256k1_fe_mul (& zi , & zi , rzr );
226+ secp256k1_fe_sqr (& zi2 , & zi );
227+ secp256k1_fe_mul (& zi3 , & zi2 , & zi );
228+ /* To compute the actual x-coordinate, we use the stored z ratio and
229+ * y-coordinate, which we obtained from `secp256k1_gej_add_ge_var`
230+ * in the loop above, as well as the inverse of the square of its
231+ * z-coordinate. We store the latter in the `zi2` variable, which is
232+ * computed iteratively starting from the overall Z inverse then
233+ * multiplying by each z-ratio in turn.
234+ *
235+ * Denoting the z-ratio as `rzr`, we observe that it is equal to `h`
236+ * from the inside of the above `gej_add_ge_var` call. This satisfies
237+ *
238+ * rzr = d_x * z^2 - x * d_z^2
239+ *
240+ * where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)`
241+ * are Jacobian coordinates of our desired point -- except both are on
242+ * the isomorphic curve that we were using when we called `gej_add_ge_var`.
243+ * To get back to secp256k1, we must multiply both `z`s by `d_z`, or
244+ * equivalently divide both `x`s by `d_z^2`. Our equation then becomes
245+ *
246+ * rzr = d_x * z^2 / d_z^2 - x
247+ *
248+ * (The left-hand-side, being a ratio of z-coordinates, is unaffected
249+ * by the isomorphism.)
250+ *
251+ * Rearranging to solve for `x`, we have
252+ *
253+ * x = d_x * z^2 / d_z^2 - rzr
254+ *
255+ * But what we actually want is the affine coordinate `X = x/z^2`,
256+ * which will satisfy
257+ *
258+ * X = d_x / d_z^2 - rzr / z^2
259+ * = dx_over_dz_squared - rzr * zi2
260+ */
261+ secp256k1_fe_mul (& p_ge .x , rzr , & zi2 );
262+ secp256k1_fe_negate (& p_ge .x , & p_ge .x , 1 );
263+ secp256k1_fe_add (& p_ge .x , & dx_over_dz_squared );
264+ /* y is stored_y/z^3, as we expect */
265+ secp256k1_fe_mul (& p_ge .y , & p_ge .y , & zi3 );
266+ /* Store */
267+ secp256k1_ge_to_storage (& pre [i ], & p_ge );
268+ }
158269}
159270
160271/** The following two macro retrieves a particular odd multiple from a table
@@ -202,7 +313,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
202313 ctx -> pre_g = (secp256k1_ge_storage (* )[])checked_malloc (cb , sizeof ((* ctx -> pre_g )[0 ]) * ECMULT_TABLE_SIZE (WINDOW_G ));
203314
204315 /* precompute the tables with odd multiples */
205- secp256k1_ecmult_odd_multiples_table_storage_var (ECMULT_TABLE_SIZE (WINDOW_G ), * ctx -> pre_g , & gj , cb );
316+ secp256k1_ecmult_odd_multiples_table_storage_var (ECMULT_TABLE_SIZE (WINDOW_G ), * ctx -> pre_g , & gj );
206317
207318#ifdef USE_ENDOMORPHISM
208319 {
@@ -216,7 +327,7 @@ static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const
216327 for (i = 0 ; i < 128 ; i ++ ) {
217328 secp256k1_gej_double_var (& g_128j , & g_128j , NULL );
218329 }
219- secp256k1_ecmult_odd_multiples_table_storage_var (ECMULT_TABLE_SIZE (WINDOW_G ), * ctx -> pre_g_128 , & g_128j , cb );
330+ secp256k1_ecmult_odd_multiples_table_storage_var (ECMULT_TABLE_SIZE (WINDOW_G ), * ctx -> pre_g_128 , & g_128j );
220331 }
221332#endif
222333}
0 commit comments