@@ -228,4 +228,127 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons
228
228
secp256k1_fe_mul (& r -> z , & r -> z , & Z );
229
229
}
230
230
231
+ static int secp256k1_ecmult_const_xonly (secp256k1_fe * r , const secp256k1_fe * n , const secp256k1_fe * d , const secp256k1_scalar * q , int bits , int known_on_curve ) {
232
+
233
+ /* This algorithm is a generalization of Peter Dettman's technique for
234
+ * avoiding the square root in a random-basepoint x-only multiplication
235
+ * on a Weierstrass curve:
236
+ * https://mailarchive.ietf.org/arch/msg/cfrg/7DyYY6gg32wDgHAhgSb6XxMDlJA/
237
+ *
238
+ *
239
+ * === Background: the effective affine technique ===
240
+ *
241
+ * Let phi_u be the isomorphism that maps (x, y) on secp256k1 curve y^2 = x^3 + 7 to
242
+ * x' = u^2*x, y' = u^3*y on curve y'^2 = x'^3 + u^6*7. This new curve has the same order as
243
+ * the original (it is isomorphic), but moreover, has the same addition/doubling formulas, as
244
+ * the curve b=7 coefficient does not appear in those formulas (or at least does not appear in
245
+ * the formulas implemented in this codebase, both affine and Jacobian). See also Example 9.5.2
246
+ * in https://www.math.auckland.ac.nz/~sgal018/crypto-book/ch9.pdf.
247
+ *
248
+ * This means any linear combination of secp256k1 points can be computed by applying phi_u
249
+ * (with non-zero u) on all input points (including the generator, if used), computing the
250
+ * linear combination on the isomorphic curve (using the same group laws), and then applying
251
+ * phi_u^{-1} to get back to secp256k1.
252
+ *
253
+ * Switching to Jacobian coordinates, note that phi_u applied to (X, Y, Z) is simply
254
+ * (X, Y, Z/u). Thus, if we want to compute (X1, Y1, Z) + (X2, Y2, Z), with identical Z
255
+ * coordinates, we can use phi_Z to transform it to (X1, Y1, 1) + (X2, Y2, 1) on an isomorphic
256
+ * curve where the affine addition formula can be used instead.
257
+ * If (X3, Y3, Z3) = (X1, Y1) + (X2, Y2) on that curve, then our answer on secp256k1 is
258
+ * (X3, Y3, Z3*Z).
259
+ *
260
+ * This is the effective affine technique: if we have a linear combination of group elements
261
+ * to compute, and all those group elements have the same Z coordinate, we can simply pretend
262
+ * that all those Z coordinates are 1, perform the computation that way, and then multiply the
263
+ * original Z coordinate back in.
264
+ *
265
+ * The technique works on any a=0 short Weierstrass curve. It is possible to generalize it to
266
+ * other curves too, but there the isomorphic curves will have different 'a' coefficients,
267
+ * which typically does affect the group laws.
268
+ *
269
+ *
270
+ * === Avoiding the square root for x-only point multiplication ===
271
+ *
272
+ * In this function, we want to compute the X coordinate of q*(n/d, y), for
273
+ * y = +-sqrt((n/d)^3 + 7).
274
+ *
275
+ * Let g = y^2*d^3 = n^3 + 7*d^3. This also means y = +-sqrt(g/d^3). Further note that
276
+ * sqrt(d*g) must exist if the input was valid, as d*g = y^2*d^4 = (y*d^2)^2.
277
+ *
278
+ * The input point (n/d, y) also has Jacobian coordinates:
279
+ * (n/d, y, 1)
280
+ * = (n/d * sqrt^2(d*g), y * sqrt^3(d*g), sqrt(d*g))
281
+ * = (n/d * d*g, y * +-sqrt(d^3*g^3), sqrt(d*g))
282
+ * = (n/d * d*g, +-sqrt(g/d^3) * +-sqrt(d^3*g^3), sqrt(d*g))
283
+ * = (n*g, +-sqrt(g/d^3 * d^3*g^3), sqrt(d*g))
284
+ * = (n*g, +-sqrt(g^4), sqrt(d*g))
285
+ * = (n*g, +-g^2, sqrt(d*g))
286
+ *
287
+ * Both signs for the Y coordinate are valid (they satisfy Y^2 = X^3 + 7Z^6), and both
288
+ * cases correspond to affine X coordinate n/d. We choose the (n*g, g^2, sqrt(d*g)) version.
289
+ *
290
+ * Now switch to the effective affine curve using phi_{sqrt(d*g)}, where the input point has
291
+ * coordinates (n*g, g^2). Compute (X, Y, Z) = q*(n*g, g^2) there.
292
+ *
293
+ * Back on secp256k1, that means q*(n*g, g^2, sqrt(d*g)) = (X, Y, Z*sqrt(d*g)). This last
294
+ * point has affine X coordinate X/(d*g*Z^2). Determining the affine Y coordinate would involve
295
+ * a square root, but as long as we only care about the resulting X coordinate, no square root
296
+ * is needed anywhere in this computation.
297
+ */
298
+
299
+ secp256k1_fe g , i ;
300
+ secp256k1_ge p ;
301
+ secp256k1_gej rj ;
302
+
303
+ /* Compute g = (n^3 + B*d^3). */
304
+ secp256k1_fe_sqr (& g , n );
305
+ secp256k1_fe_mul (& g , & g , n );
306
+ if (d ) {
307
+ secp256k1_fe b ;
308
+ secp256k1_fe_sqr (& b , d );
309
+ secp256k1_fe_mul_int (& b , SECP256K1_B );
310
+ secp256k1_fe_mul (& b , & b , d );
311
+ secp256k1_fe_add (& g , & b );
312
+ if (!known_on_curve ) {
313
+ /* We need to determine whether (n/d)^3 + 7 is square.
314
+ *
315
+ * is_square((n/d)^3 + 7)
316
+ * <=> is_square(((n/d)^3 + 7) * d^4)
317
+ * <=> is_square((n^3 + 7*d^3) * d)
318
+ * <=> is_square(g * d)
319
+ */
320
+ secp256k1_fe c ;
321
+ secp256k1_fe_mul (& c , & g , d );
322
+ if (!secp256k1_fe_is_square_var (& c )) return 0 ;
323
+ }
324
+ } else {
325
+ secp256k1_fe_add (& g , & secp256k1_fe_const_b );
326
+ if (!known_on_curve ) {
327
+ /* g at this point equals x^3 + 7. Test if it is square. */
328
+ if (!secp256k1_fe_is_square_var (& g )) return 0 ;
329
+ }
330
+ }
331
+
332
+ /* Compute base point P = (n*g, g^2), the effective affine version of
333
+ * (n*g, g^2, sqrt(d*g)), which has corresponding affine X coordinate
334
+ * n/d. */
335
+ secp256k1_fe_mul (& p .x , & g , n );
336
+ secp256k1_fe_sqr (& p .y , & g );
337
+ p .infinity = 0 ;
338
+
339
+ /* Perform x-only EC multiplication of P with q. */
340
+ secp256k1_ecmult_const (& rj , & p , q , bits );
341
+
342
+ /* The resulting (X, Y, Z) point on the effective-affine isomorphic curve
343
+ * corresponds to (X, Y, Z*sqrt(d*g)) on the secp256k1 curve. The affine
344
+ * version of that has X coordinate (X / (Z^2*d*g)). */
345
+ secp256k1_fe_sqr (& i , & rj .z );
346
+ secp256k1_fe_mul (& i , & i , & g );
347
+ if (d ) secp256k1_fe_mul (& i , & i , d );
348
+ secp256k1_fe_inv (& i , & i );
349
+ secp256k1_fe_mul (r , & rj .x , & i );
350
+
351
+ return 1 ;
352
+ }
353
+
231
354
#endif /* SECP256K1_ECMULT_CONST_IMPL_H */
0 commit comments