Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Random isomorphism blinding for ecmult_gen #570

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/ecmult_gen.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
130 changes: 100 additions & 30 deletions src/ecmult_gen_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand Down
6 changes: 6 additions & 0 deletions src/group.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
10 changes: 10 additions & 0 deletions src/group_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
5 changes: 5 additions & 0 deletions src/tests.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down