Initial gej blinding -> final ge blinding

Instead of having the starting point of the ecmult_gen computation be
offset, do it with the final point. This enables reasoning over the
set of points reachable in intermediary computations, which can be
leveraged by potential future optimization.

Because the final point is in affine coordinates, its projective
blinding is no longer possible. It will be reintroduced again in
a different way, in a later commit.

Also introduce some more comments and more descriptive names.
This commit is contained in:
Pieter Wuille
2021-12-26 18:21:57 -05:00
parent aa00a6b892
commit ab45c3e089
3 changed files with 43 additions and 39 deletions

View File

@@ -32,9 +32,9 @@ typedef struct {
/* Whether the context has been built. */
int built;
/* Blinding values used when computing (n-b)G + bG. */
secp256k1_scalar blind; /* -b */
secp256k1_gej initial; /* bG */
/* Blinding values used when computing nG as (n-b)G + bG. */
secp256k1_scalar scalar_offset; /* -b */
secp256k1_ge ge_offset; /* bG */
} secp256k1_ecmult_gen_context;
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx);

View File

@@ -25,8 +25,8 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
ctx->built = 0;
secp256k1_scalar_clear(&ctx->blind);
secp256k1_gej_clear(&ctx->initial);
secp256k1_scalar_clear(&ctx->scalar_offset);
secp256k1_ge_clear(&ctx->ge_offset);
}
/* For accelerating the computation of a*G:
@@ -51,12 +51,13 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
secp256k1_ge_storage adds;
secp256k1_scalar gnb;
int i, j, n_i;
memset(&adds, 0, sizeof(adds));
*r = ctx->initial;
/* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
secp256k1_scalar_add(&gnb, gn, &ctx->blind);
add.infinity = 0;
secp256k1_gej_set_infinity(r);
/* Blind scalar/point multiplication by computing (gn-b)*G + b*G instead of gn*G. */
secp256k1_scalar_add(&gnb, gn, &ctx->scalar_offset);
for (i = 0; i < n; i++) {
n_i = secp256k1_scalar_get_bits(&gnb, i * bits, bits);
for (j = 0; j < g; j++) {
@@ -76,6 +77,11 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context *ctx, secp25
secp256k1_gej_add_ge(r, r, &add);
}
n_i = 0;
/* Correct for the scalar_offset added at the start (ge_offset = b*G, while b was
* subtracted from the input scalar gn). */
secp256k1_gej_add_ge(r, r, &ctx->ge_offset);
secp256k1_ge_clear(&add);
secp256k1_scalar_clear(&gnb);
}
@@ -84,19 +90,17 @@ 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;
unsigned char nonce32[32];
secp256k1_rfc6979_hmac_sha256 rng;
unsigned char keydata[64];
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);
/* When seed is NULL, reset the final point and blinding value. */
secp256k1_ge_neg(&ctx->ge_offset, &secp256k1_ge_const_g);
ctx->scalar_offset = secp256k1_scalar_one;
return;
}
/* The prior blinding value (if not reset) is chained forward by including it in the hash. */
secp256k1_scalar_get_b32(keydata, &ctx->blind);
secp256k1_scalar_get_b32(keydata, &ctx->scalar_offset);
/** 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.
@@ -105,24 +109,23 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
memcpy(keydata + 32, seed32, 32);
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64);
memset(keydata, 0, sizeof(keydata));
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
secp256k1_fe_set_b32_mod(&s, nonce32);
secp256k1_fe_cmov(&s, &secp256k1_fe_one, secp256k1_fe_normalizes_to_zero(&s));
/* Randomize the projection to defend against multiplier sidechannels.
Do this before our own call to secp256k1_ecmult_gen below. */
secp256k1_gej_rescale(&ctx->initial, &s);
secp256k1_fe_clear(&s);
/* TODO: reintroduce projective blinding. */
/* For a random blinding value b, set ctx->scalar_offset=-b, ctx->ge_offset=bG. */
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
secp256k1_scalar_set_b32(&b, nonce32, NULL);
/* A blinding value of 0 works, but would undermine the projection hardening. */
/* The blinding value cannot be zero, as that would mean ge_offset = infinity,
* which secp256k1_gej_add_ge cannot handle. */
secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
memset(nonce32, 0, 32);
/* The random projection in ctx->initial ensures that gb will have a random projection. */
secp256k1_ecmult_gen(ctx, &gb, &b);
secp256k1_scalar_negate(&b, &b);
ctx->blind = b;
ctx->initial = gb;
ctx->scalar_offset = b;
secp256k1_ge_set_gej(&ctx->ge_offset, &gb);
/* Clean up. */
secp256k1_scalar_clear(&b);
secp256k1_gej_clear(&gb);
}

View File

@@ -248,8 +248,8 @@ static void run_selftest_tests(void) {
static int ecmult_gen_context_eq(const secp256k1_ecmult_gen_context *a, const secp256k1_ecmult_gen_context *b) {
return a->built == b->built
&& secp256k1_scalar_eq(&a->blind, &b->blind)
&& secp256k1_gej_eq_var(&a->initial, &b->initial);
&& secp256k1_scalar_eq(&a->scalar_offset, &b->scalar_offset)
&& secp256k1_ge_eq_var(&a->ge_offset, &b->ge_offset);
}
static int context_eq(const secp256k1_context *a, const secp256k1_context *b) {
@@ -5570,18 +5570,18 @@ static void test_ecmult_gen_blind(void) {
unsigned char seed32[32];
secp256k1_gej pgej;
secp256k1_gej pgej2;
secp256k1_gej i;
secp256k1_ge p;
secp256k1_ge pge;
random_scalar_order_test(&key);
secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej, &key);
secp256k1_testrand256(seed32);
b = CTX->ecmult_gen_ctx.blind;
i = CTX->ecmult_gen_ctx.initial;
b = CTX->ecmult_gen_ctx.scalar_offset;
p = CTX->ecmult_gen_ctx.ge_offset;
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, seed32);
CHECK(!secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.blind));
CHECK(!secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.scalar_offset));
secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej2, &key);
CHECK(!gej_xyz_equals_gej(&pgej, &pgej2));
CHECK(!gej_xyz_equals_gej(&i, &CTX->ecmult_gen_ctx.initial));
CHECK(!secp256k1_ge_eq_var(&p, &CTX->ecmult_gen_ctx.ge_offset));
secp256k1_ge_set_gej(&pge, &pgej);
CHECK(secp256k1_gej_eq_ge_var(&pgej2, &pge));
}
@@ -5589,13 +5589,14 @@ static void test_ecmult_gen_blind(void) {
static void test_ecmult_gen_blind_reset(void) {
/* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */
secp256k1_scalar b;
secp256k1_gej initial;
secp256k1_ge p1, p2;
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0);
b = CTX->ecmult_gen_ctx.blind;
initial = CTX->ecmult_gen_ctx.initial;
b = CTX->ecmult_gen_ctx.scalar_offset;
p1 = CTX->ecmult_gen_ctx.ge_offset;
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));
CHECK(secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.scalar_offset));
p2 = CTX->ecmult_gen_ctx.ge_offset;
CHECK(secp256k1_ge_eq_var(&p1, &p2));
}
static void run_ecmult_gen_blind(void) {