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:
@@ -32,9 +32,9 @@ typedef struct {
|
|||||||
/* Whether the context has been built. */
|
/* Whether the context has been built. */
|
||||||
int built;
|
int built;
|
||||||
|
|
||||||
/* Blinding values used when computing (n-b)G + bG. */
|
/* Blinding values used when computing nG as (n-b)G + bG. */
|
||||||
secp256k1_scalar blind; /* -b */
|
secp256k1_scalar scalar_offset; /* -b */
|
||||||
secp256k1_gej initial; /* bG */
|
secp256k1_ge ge_offset; /* bG */
|
||||||
} secp256k1_ecmult_gen_context;
|
} secp256k1_ecmult_gen_context;
|
||||||
|
|
||||||
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx);
|
static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context* ctx);
|
||||||
|
|||||||
@@ -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) {
|
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context *ctx) {
|
||||||
ctx->built = 0;
|
ctx->built = 0;
|
||||||
secp256k1_scalar_clear(&ctx->blind);
|
secp256k1_scalar_clear(&ctx->scalar_offset);
|
||||||
secp256k1_gej_clear(&ctx->initial);
|
secp256k1_ge_clear(&ctx->ge_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For accelerating the computation of a*G:
|
/* 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_ge_storage adds;
|
||||||
secp256k1_scalar gnb;
|
secp256k1_scalar gnb;
|
||||||
int i, j, n_i;
|
int i, j, n_i;
|
||||||
|
|
||||||
memset(&adds, 0, sizeof(adds));
|
memset(&adds, 0, sizeof(adds));
|
||||||
*r = ctx->initial;
|
secp256k1_gej_set_infinity(r);
|
||||||
/* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
|
|
||||||
secp256k1_scalar_add(&gnb, gn, &ctx->blind);
|
/* Blind scalar/point multiplication by computing (gn-b)*G + b*G instead of gn*G. */
|
||||||
add.infinity = 0;
|
secp256k1_scalar_add(&gnb, gn, &ctx->scalar_offset);
|
||||||
|
|
||||||
for (i = 0; i < n; i++) {
|
for (i = 0; i < n; i++) {
|
||||||
n_i = secp256k1_scalar_get_bits(&gnb, i * bits, bits);
|
n_i = secp256k1_scalar_get_bits(&gnb, i * bits, bits);
|
||||||
for (j = 0; j < g; j++) {
|
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);
|
secp256k1_gej_add_ge(r, r, &add);
|
||||||
}
|
}
|
||||||
n_i = 0;
|
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_ge_clear(&add);
|
||||||
secp256k1_scalar_clear(&gnb);
|
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) {
|
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const unsigned char *seed32) {
|
||||||
secp256k1_scalar b;
|
secp256k1_scalar b;
|
||||||
secp256k1_gej gb;
|
secp256k1_gej gb;
|
||||||
secp256k1_fe s;
|
|
||||||
unsigned char nonce32[32];
|
unsigned char nonce32[32];
|
||||||
secp256k1_rfc6979_hmac_sha256 rng;
|
secp256k1_rfc6979_hmac_sha256 rng;
|
||||||
unsigned char keydata[64];
|
unsigned char keydata[64];
|
||||||
if (seed32 == NULL) {
|
if (seed32 == NULL) {
|
||||||
/* When seed is NULL, reset the initial point and blinding value. */
|
/* When seed is NULL, reset the final point and blinding value. */
|
||||||
secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g);
|
secp256k1_ge_neg(&ctx->ge_offset, &secp256k1_ge_const_g);
|
||||||
secp256k1_gej_neg(&ctx->initial, &ctx->initial);
|
ctx->scalar_offset = secp256k1_scalar_one;
|
||||||
secp256k1_scalar_set_int(&ctx->blind, 1);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* The prior blinding value (if not reset) is chained forward by including it in the hash. */
|
/* 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,
|
/** 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
|
* 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.
|
* 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);
|
memcpy(keydata + 32, seed32, 32);
|
||||||
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64);
|
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64);
|
||||||
memset(keydata, 0, sizeof(keydata));
|
memset(keydata, 0, sizeof(keydata));
|
||||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
|
||||||
secp256k1_fe_set_b32_mod(&s, nonce32);
|
/* TODO: reintroduce projective blinding. */
|
||||||
secp256k1_fe_cmov(&s, &secp256k1_fe_one, secp256k1_fe_normalizes_to_zero(&s));
|
|
||||||
/* Randomize the projection to defend against multiplier sidechannels.
|
/* For a random blinding value b, set ctx->scalar_offset=-b, ctx->ge_offset=bG. */
|
||||||
Do this before our own call to secp256k1_ecmult_gen below. */
|
|
||||||
secp256k1_gej_rescale(&ctx->initial, &s);
|
|
||||||
secp256k1_fe_clear(&s);
|
|
||||||
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
|
||||||
secp256k1_scalar_set_b32(&b, nonce32, NULL);
|
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_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
|
||||||
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
|
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
|
||||||
memset(nonce32, 0, 32);
|
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_ecmult_gen(ctx, &gb, &b);
|
||||||
secp256k1_scalar_negate(&b, &b);
|
secp256k1_scalar_negate(&b, &b);
|
||||||
ctx->blind = b;
|
ctx->scalar_offset = b;
|
||||||
ctx->initial = gb;
|
secp256k1_ge_set_gej(&ctx->ge_offset, &gb);
|
||||||
|
|
||||||
|
/* Clean up. */
|
||||||
secp256k1_scalar_clear(&b);
|
secp256k1_scalar_clear(&b);
|
||||||
secp256k1_gej_clear(&gb);
|
secp256k1_gej_clear(&gb);
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/tests.c
25
src/tests.c
@@ -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) {
|
static int ecmult_gen_context_eq(const secp256k1_ecmult_gen_context *a, const secp256k1_ecmult_gen_context *b) {
|
||||||
return a->built == b->built
|
return a->built == b->built
|
||||||
&& secp256k1_scalar_eq(&a->blind, &b->blind)
|
&& secp256k1_scalar_eq(&a->scalar_offset, &b->scalar_offset)
|
||||||
&& secp256k1_gej_eq_var(&a->initial, &b->initial);
|
&& secp256k1_ge_eq_var(&a->ge_offset, &b->ge_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int context_eq(const secp256k1_context *a, const secp256k1_context *b) {
|
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];
|
unsigned char seed32[32];
|
||||||
secp256k1_gej pgej;
|
secp256k1_gej pgej;
|
||||||
secp256k1_gej pgej2;
|
secp256k1_gej pgej2;
|
||||||
secp256k1_gej i;
|
secp256k1_ge p;
|
||||||
secp256k1_ge pge;
|
secp256k1_ge pge;
|
||||||
random_scalar_order_test(&key);
|
random_scalar_order_test(&key);
|
||||||
secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej, &key);
|
secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej, &key);
|
||||||
secp256k1_testrand256(seed32);
|
secp256k1_testrand256(seed32);
|
||||||
b = CTX->ecmult_gen_ctx.blind;
|
b = CTX->ecmult_gen_ctx.scalar_offset;
|
||||||
i = CTX->ecmult_gen_ctx.initial;
|
p = CTX->ecmult_gen_ctx.ge_offset;
|
||||||
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, seed32);
|
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);
|
secp256k1_ecmult_gen(&CTX->ecmult_gen_ctx, &pgej2, &key);
|
||||||
CHECK(!gej_xyz_equals_gej(&pgej, &pgej2));
|
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);
|
secp256k1_ge_set_gej(&pge, &pgej);
|
||||||
CHECK(secp256k1_gej_eq_ge_var(&pgej2, &pge));
|
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) {
|
static void test_ecmult_gen_blind_reset(void) {
|
||||||
/* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */
|
/* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */
|
||||||
secp256k1_scalar b;
|
secp256k1_scalar b;
|
||||||
secp256k1_gej initial;
|
secp256k1_ge p1, p2;
|
||||||
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0);
|
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0);
|
||||||
b = CTX->ecmult_gen_ctx.blind;
|
b = CTX->ecmult_gen_ctx.scalar_offset;
|
||||||
initial = CTX->ecmult_gen_ctx.initial;
|
p1 = CTX->ecmult_gen_ctx.ge_offset;
|
||||||
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0);
|
secp256k1_ecmult_gen_blind(&CTX->ecmult_gen_ctx, 0);
|
||||||
CHECK(secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.blind));
|
CHECK(secp256k1_scalar_eq(&b, &CTX->ecmult_gen_ctx.scalar_offset));
|
||||||
CHECK(gej_xyz_equals_gej(&initial, &CTX->ecmult_gen_ctx.initial));
|
p2 = CTX->ecmult_gen_ctx.ge_offset;
|
||||||
|
CHECK(secp256k1_ge_eq_var(&p1, &p2));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void run_ecmult_gen_blind(void) {
|
static void run_ecmult_gen_blind(void) {
|
||||||
|
|||||||
Reference in New Issue
Block a user