diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index 7564b7015f..b73aaae36e 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -26,6 +26,9 @@ typedef struct { secp256k1_ge_storage (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ secp256k1_scalar blind; secp256k1_gej initial; +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_fe iso; +#endif } secp256k1_ecmult_gen_context; static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context* ctx); diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 714f02e94c..ba4933b449 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -84,10 +84,16 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context *ctx secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); } } + secp256k1_fe_set_int(&ctx->iso, 1); #else (void)cb; ctx->prec = (secp256k1_ge_storage (*)[64][16])secp256k1_ecmult_static_context; #endif + + secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); + secp256k1_gej_neg(&ctx->initial, &ctx->initial); + secp256k1_scalar_set_int(&ctx->blind, 1); + secp256k1_ecmult_gen_blind(ctx, NULL); } @@ -103,6 +109,7 @@ static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst #ifndef USE_ECMULT_STATIC_PRECOMPUTATION dst->prec = (secp256k1_ge_storage (*)[64][16])checked_malloc(cb, sizeof(*dst->prec)); memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + dst->iso = src->iso; #else (void)cb; dst->prec = src->prec; @@ -115,6 +122,7 @@ static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context *dst static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) { #ifndef USE_ECMULT_STATIC_PRECOMPUTATION free(ctx->prec); + secp256k1_fe_clear(&ctx->iso); #endif secp256k1_scalar_clear(&ctx->blind); secp256k1_gej_clear(&ctx->initial); @@ -150,6 +158,9 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 secp256k1_ge_from_storage(&add, &adds); secp256k1_gej_add_ge(r, r, &add); } +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_fe_mul(&r->z, &r->z, &ctx->iso); +#endif bits = 0; secp256k1_ge_clear(&add); secp256k1_scalar_clear(&gnb); @@ -159,50 +170,109 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25 static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) { secp256k1_scalar b; secp256k1_gej gb; - secp256k1_fe s; + secp256k1_fe iso; unsigned char nonce32[32]; secp256k1_rfc6979_hmac_sha256 rng; int retry; +#ifdef USE_ECMULT_STATIC_PRECOMPUTATION unsigned char keydata[64] = {0}; - if (seed32 == NULL) { - /* When seed is NULL, reset the initial point and blinding value. */ - secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g); - secp256k1_gej_neg(&ctx->initial, &ctx->initial); - secp256k1_scalar_set_int(&ctx->blind, 1); - } - /* The prior blinding value (if not reset) is chained forward by including it in the hash. */ - secp256k1_scalar_get_b32(nonce32, &ctx->blind); - /** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data, - * and guards against weak or adversarial seeds. This is a simpler and safer interface than - * asking the caller for blinding values directly and expecting them to retry on failure. - */ - memcpy(keydata, nonce32, 32); - if (seed32 != NULL) { - memcpy(keydata + 32, seed32, 32); +#else + secp256k1_ge tmp; + secp256k1_fe iso2, iso3; + int i, j; + unsigned char keydata[96] = {0}; +#endif + + /** Initialize the RNG. Using a CSPRNG allows a failure free interface, avoids needing large + * amounts of random data, and guards against weak or adversarial seeds. This is a simpler + * and safer interface than asking the caller for blinding values directly and expecting them + * to retry on failure. */ + { + /* The prior blinding values are chained forward by including them in the hash. */ + secp256k1_scalar_get_b32(keydata, &ctx->blind); +#ifdef USE_ECMULT_STATIC_PRECOMPUTATION + if (seed32 != NULL) { + memcpy(keydata + 32, seed32, 32); + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); +#else + secp256k1_fe_get_b32(keydata + 32, &ctx->iso); + if (seed32 != NULL) { + memcpy(keydata + 64, seed32, 32); + } + secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 96 : 64); +#endif + memset(keydata, 0, sizeof(keydata)); } - secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32); - memset(keydata, 0, sizeof(keydata)); - /* Retry for out of range results to achieve uniformity. */ + + /* Choose a random isomorphism, defined by some non-zero field element. */ do { secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); - retry = !secp256k1_fe_set_b32(&s, nonce32); - retry |= secp256k1_fe_is_zero(&s); - } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ - /* Randomize the projection to defend against multiplier sidechannels. */ - secp256k1_gej_rescale(&ctx->initial, &s); - secp256k1_fe_clear(&s); + + /* Retry for out of range results to achieve uniformity. */ + retry = !secp256k1_fe_set_b32(&iso, nonce32); + retry |= secp256k1_fe_is_zero(&iso); + } + /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */ + while (retry); + + /* Map precomputed points onto the random isomorphism to defend against multiplier sidechannels. */ + { +#ifdef USE_ECMULT_STATIC_PRECOMPUTATION + /* For static case, we settle for randomizing the projective coordinate of ctx->initial. */ + secp256k1_gej_rescale(&ctx->initial, &iso); +#else + secp256k1_fe_sqr(&iso2, &iso); + secp256k1_fe_mul(&iso3, &iso2, &iso); + + secp256k1_gej_to_iso(&ctx->initial, &iso2, &iso3); + + for (j = 0; j < 64; j++) { + for (i = 0; i < 16; i++) { + secp256k1_ge_from_storage(&tmp, &(*ctx->prec)[j][i]); + secp256k1_ge_to_iso(&tmp, &iso2, &iso3); + secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &tmp); + } + } + + secp256k1_fe_clear(&iso2); + secp256k1_fe_clear(&iso3); + secp256k1_ge_clear(&tmp); +#endif + } + + /* Choose a random scalar 'blind'. */ do { secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); secp256k1_scalar_set_b32(&b, nonce32, &retry); /* A blinding value of 0 works, but would undermine the projection hardening. */ retry |= secp256k1_scalar_is_zero(&b); - } while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ + } + /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */ + while (retry); + secp256k1_rfc6979_hmac_sha256_finalize(&rng); memset(nonce32, 0, 32); - secp256k1_ecmult_gen(ctx, &gb, &b); - secp256k1_scalar_negate(&b, &b); - ctx->blind = b; - ctx->initial = gb; + + /* Calculate the 'initial' point corresponding to the chosen scalar 'blind'. */ + { +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_fe_mul(&iso, &iso, &ctx->iso); + secp256k1_fe_set_int(&ctx->iso, 1); +#endif + + secp256k1_ecmult_gen(ctx, &gb, &b); + secp256k1_scalar_negate(&b, &b); + ctx->blind = b; + ctx->initial = gb; + +#ifndef USE_ECMULT_STATIC_PRECOMPUTATION + secp256k1_fe_normalize(&iso); + ctx->iso = iso; + secp256k1_fe_clear(&iso); +#endif + } + secp256k1_scalar_clear(&b); secp256k1_gej_clear(&gb); } diff --git a/src/group.h b/src/group.h index 3947ea2dda..4ed6672e5c 100644 --- a/src/group.h +++ b/src/group.h @@ -144,4 +144,10 @@ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage *r, const secp256k1_g /** Rescale a jacobian point by b which must be non-zero. Constant-time. */ static void secp256k1_gej_rescale(secp256k1_gej *r, const secp256k1_fe *b); +/** Map an affine point to an isomorphism. iso2, iso3 must be non-zero. Constant-time. */ +static void secp256k1_ge_to_iso(secp256k1_ge *r, const secp256k1_fe *iso2, const secp256k1_fe *iso3); + +/** Map a jacobian point to an isomorphism. iso2, iso3 must be non-zero. Constant-time. */ +static void secp256k1_gej_to_iso(secp256k1_gej *r, const secp256k1_fe *iso2, const secp256k1_fe *iso3); + #endif /* SECP256K1_GROUP_H */ diff --git a/src/group_impl.h b/src/group_impl.h index b1ace87b6f..6ccacd9ada 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -703,4 +703,14 @@ static int secp256k1_gej_has_quad_y_var(const secp256k1_gej *a) { return secp256k1_fe_is_quad_var(&yz); } +static void secp256k1_ge_to_iso(secp256k1_ge *r, const secp256k1_fe *iso2, const secp256k1_fe *iso3) { + secp256k1_fe_mul(&r->x, &r->x, iso2); + secp256k1_fe_mul(&r->y, &r->y, iso3); +} + +static void secp256k1_gej_to_iso(secp256k1_gej *r, const secp256k1_fe *iso2, const secp256k1_fe *iso3) { + secp256k1_fe_mul(&r->x, &r->x, iso2); + secp256k1_fe_mul(&r->y, &r->y, iso3); +} + #endif /* SECP256K1_GROUP_IMPL_H */ diff --git a/src/tests.c b/src/tests.c index 15f44914b2..81d3168130 100644 --- a/src/tests.c +++ b/src/tests.c @@ -3204,6 +3204,10 @@ void test_ecmult_gen_blind(void) { } void test_ecmult_gen_blind_reset(void) { + /* Calling ecmult_gen_blind without a seed value no longer resets the blinding */ +#if 1 + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); +#else /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ secp256k1_scalar b; secp256k1_gej initial; @@ -3213,6 +3217,7 @@ void test_ecmult_gen_blind_reset(void) { secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial)); +#endif } void run_ecmult_gen_blind(void) {