diff --git a/src/group.h b/src/group.h index 992ff5c98c..05ae0d203c 100644 --- a/src/group.h +++ b/src/group.h @@ -80,7 +80,11 @@ static void secp256k1_ge_set_gej(secp256k1_ge *r, secp256k1_gej *a); /** Set a group element equal to another which is given in jacobian coordinates. */ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a); -/** Set a batch of group elements equal to the inputs given in jacobian coordinates */ +/** Set group elements r[0:len] (affine) equal to group elements a[0:len] (jacobian). + * None of the group elements in a[0:len] may be infinity. Constant time. */ +static void secp256k1_ge_set_all_gej(secp256k1_ge *r, const secp256k1_gej *a, size_t len); + +/** Set group elements r[0:len] (affine) equal to group elements a[0:len] (jacobian). */ static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len); /** Bring a batch of inputs to the same global z "denominator", based on ratios between diff --git a/src/group_impl.h b/src/group_impl.h index 2e096f4147..2e612dfb0c 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -195,6 +195,52 @@ static void secp256k1_ge_set_gej_var(secp256k1_ge *r, secp256k1_gej *a) { SECP256K1_GE_VERIFY(r); } +static void secp256k1_ge_set_all_gej(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { + secp256k1_fe u; + size_t i; + size_t last_i = SIZE_MAX; +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GEJ_VERIFY(&a[i]); + VERIFY_CHECK(!secp256k1_gej_is_infinity(&a[i])); + } +#endif + + for (i = 0; i < len; i++) { + /* Use destination's x coordinates as scratch space */ + if (last_i == SIZE_MAX) { + r[i].x = a[i].z; + } else { + secp256k1_fe_mul(&r[i].x, &r[last_i].x, &a[i].z); + } + last_i = i; + } + if (last_i == SIZE_MAX) { + return; + } + secp256k1_fe_inv(&u, &r[last_i].x); + + i = last_i; + while (i > 0) { + i--; + secp256k1_fe_mul(&r[last_i].x, &r[i].x, &u); + secp256k1_fe_mul(&u, &u, &a[last_i].z); + last_i = i; + } + VERIFY_CHECK(!a[last_i].infinity); + r[last_i].x = u; + + for (i = 0; i < len; i++) { + secp256k1_ge_set_gej_zinv(&r[i], &a[i], &r[i].x); + } + +#ifdef VERIFY + for (i = 0; i < len; i++) { + SECP256K1_GE_VERIFY(&r[i]); + } +#endif +} + static void secp256k1_ge_set_all_gej_var(secp256k1_ge *r, const secp256k1_gej *a, size_t len) { secp256k1_fe u; size_t i; diff --git a/src/tests.c b/src/tests.c index dba4097b1a..61e7581506 100644 --- a/src/tests.c +++ b/src/tests.c @@ -3822,14 +3822,27 @@ static void test_ge(void) { /* Test batch gej -> ge conversion without known z ratios. */ { + secp256k1_ge *ge_set_all_var = (secp256k1_ge *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); secp256k1_ge *ge_set_all = (secp256k1_ge *)checked_malloc(&CTX->error_callback, (4 * runs + 1) * sizeof(secp256k1_ge)); - secp256k1_ge_set_all_gej_var(ge_set_all, gej, 4 * runs + 1); + secp256k1_ge_set_all_gej_var(&ge_set_all_var[0], &gej[0], 4 * runs + 1); for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_fe s; + testutil_random_fe_non_zero(&s); + secp256k1_gej_rescale(&gej[i], &s); + CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all_var[i])); + } + + /* Skip infinity at &gej[0]. */ + secp256k1_ge_set_all_gej(&ge_set_all[1], &gej[1], 4 * runs); + for (i = 1; i < 4 * runs + 1; i++) { secp256k1_fe s; testutil_random_fe_non_zero(&s); secp256k1_gej_rescale(&gej[i], &s); CHECK(secp256k1_gej_eq_ge_var(&gej[i], &ge_set_all[i])); + CHECK(secp256k1_ge_eq_var(&ge_set_all_var[i], &ge_set_all[i])); } + + free(ge_set_all_var); free(ge_set_all); }