diff --git a/include/secp256k1.h b/include/secp256k1.h index bcc2be0379..06afd4c65b 100644 --- a/include/secp256k1.h +++ b/include/secp256k1.h @@ -328,6 +328,18 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( const unsigned char *tweak ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); +/** Updates the context randomization. + * Returns: 1: randomization successfully updated + * 0: error + * In: ctx: pointer to a context object (cannot be NULL) + * seed32: pointer to a 32-byte random seed (NULL resets to initial state) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + secp256k1_context_t* ctx, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1); + + # ifdef __cplusplus } # endif diff --git a/src/ecmult_gen.h b/src/ecmult_gen.h index c65e5cd0e0..3745633c47 100644 --- a/src/ecmult_gen.h +++ b/src/ecmult_gen.h @@ -24,6 +24,8 @@ typedef struct { * the intermediate sums while computing a*G. */ secp256k1_ge_storage_t (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */ + secp256k1_scalar_t blind; + secp256k1_gej_t initial; } secp256k1_ecmult_gen_context_t; static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t* ctx); @@ -36,4 +38,6 @@ static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_cont /** Multiply with the generator: R = a*G */ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t* ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *a); +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32); + #endif diff --git a/src/ecmult_gen_impl.h b/src/ecmult_gen_impl.h index 9c8f7209e7..4697753ac8 100644 --- a/src/ecmult_gen_impl.h +++ b/src/ecmult_gen_impl.h @@ -1,5 +1,5 @@ /********************************************************************** - * Copyright (c) 2013, 2014 Pieter Wuille * + * Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell * * Distributed under the MIT software license, see the accompanying * * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ @@ -10,6 +10,7 @@ #include "scalar.h" #include "group.h" #include "ecmult_gen.h" +#include "hash_impl.h" static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t *ctx) { ctx->prec = NULL; @@ -74,6 +75,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t *c secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]); } } + secp256k1_ecmult_gen_blind(ctx, NULL); } static int secp256k1_ecmult_gen_context_is_built(const secp256k1_ecmult_gen_context_t* ctx) { @@ -87,24 +89,31 @@ static void secp256k1_ecmult_gen_context_clone(secp256k1_ecmult_gen_context_t *d } else { dst->prec = (secp256k1_ge_storage_t (*)[64][16])checked_malloc(sizeof(*dst->prec)); memcpy(dst->prec, src->prec, sizeof(*dst->prec)); + dst->initial = src->initial; + dst->blind = src->blind; } } static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context_t *ctx) { free(ctx->prec); + secp256k1_scalar_clear(&ctx->blind); + secp256k1_gej_clear(&ctx->initial); ctx->prec = NULL; } static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t *ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *gn) { secp256k1_ge_t add; secp256k1_ge_storage_t adds; + secp256k1_scalar_t gnb; int bits; int i, j; memset(&adds, 0, sizeof(adds)); - secp256k1_gej_set_infinity(r); + *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; for (j = 0; j < 64; j++) { - bits = secp256k1_scalar_get_bits(gn, j * 4, 4); + bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4); for (i = 0; i < 16; i++) { /** This uses a conditional move to avoid any secret data in array indexes. * _Any_ use of secret indexes has been demonstrated to result in timing @@ -123,6 +132,53 @@ static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t *ctx, secp } bits = 0; secp256k1_ge_clear(&add); + secp256k1_scalar_clear(&gnb); +} + +/* Setup blinding values for secp256k1_ecmult_gen. */ +static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32) { + secp256k1_scalar_t b; + secp256k1_gej_t gb; + secp256k1_fe_t s; + unsigned char nonce32[32]; + secp256k1_rfc6979_hmac_sha256_t rng; + int retry; + if (!seed32) { + /* 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. + */ + secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed32 ? seed32 : nonce32, 32, nonce32, 32, NULL, 0); + /* Retry for out of range results to achieve uniformity. */ + do { + secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32); + retry = !secp256k1_fe_set_b32(&s, nonce32); + retry |= secp256k1_fe_is_zero(&s); + } while (retry); + /* Randomize the projection to defend against multiplier sidechannels. */ + secp256k1_gej_rescale(&ctx->initial, &s); + secp256k1_fe_clear(&s); + 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); + 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; + secp256k1_scalar_clear(&b); + secp256k1_gej_clear(&gb); } #endif diff --git a/src/group.h b/src/group.h index d1e5834909..0b08b3b991 100644 --- a/src/group.h +++ b/src/group.h @@ -115,4 +115,7 @@ static void secp256k1_ge_from_storage(secp256k1_ge_t *r, const secp256k1_ge_stor /** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */ static void secp256k1_ge_storage_cmov(secp256k1_ge_storage_t *r, const secp256k1_ge_storage_t *a, int flag); +/** Rescale a jacobian point by b which must be non-zero. Constant-time. */ +static void secp256k1_gej_rescale(secp256k1_gej_t *r, const secp256k1_fe_t *b); + #endif diff --git a/src/group_impl.h b/src/group_impl.h index 131393ff7b..0f64576fbb 100644 --- a/src/group_impl.h +++ b/src/group_impl.h @@ -396,6 +396,17 @@ static void secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, c r->infinity = infinity; } +static void secp256k1_gej_rescale(secp256k1_gej_t *r, const secp256k1_fe_t *s) { + /* Operations: 4 mul, 1 sqr */ + secp256k1_fe_t zz; + VERIFY_CHECK(!secp256k1_fe_is_zero(s)); + secp256k1_fe_sqr(&zz, s); + secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */ + secp256k1_fe_mul(&r->y, &r->y, &zz); + secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */ + secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */ +} + static void secp256k1_ge_to_storage(secp256k1_ge_storage_t *r, const secp256k1_ge_t *a) { secp256k1_fe_t x, y; VERIFY_CHECK(!a->infinity); diff --git a/src/secp256k1.c b/src/secp256k1.c index 7445ae98e8..d6192dc4ed 100644 --- a/src/secp256k1.c +++ b/src/secp256k1.c @@ -410,3 +410,10 @@ int secp256k1_ec_privkey_import(const secp256k1_context_t* ctx, unsigned char *s secp256k1_scalar_clear(&key); return ret; } + +int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *seed32) { + DEBUG_CHECK(ctx != NULL); + DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + return 1; +} diff --git a/src/tests.c b/src/tests.c index d391488b61..31dbc9dd60 100644 --- a/src/tests.c +++ b/src/tests.c @@ -907,6 +907,28 @@ void ge_equals_ge(const secp256k1_ge_t *a, const secp256k1_ge_t *b) { CHECK(secp256k1_fe_equal_var(&b->y, &b->y)); } +/* This compares jacobian points including their Z, not just their geometric meaning. */ +int gej_xyz_equals_gej(const secp256k1_gej_t *a, const secp256k1_gej_t *b) { + secp256k1_gej_t a2; + secp256k1_gej_t b2; + int ret = 1; + ret &= a->infinity == b->infinity; + if (ret && !a->infinity) { + a2 = *a; + b2 = *b; + secp256k1_fe_normalize(&a2.x); + secp256k1_fe_normalize(&a2.y); + secp256k1_fe_normalize(&a2.z); + secp256k1_fe_normalize(&b2.x); + secp256k1_fe_normalize(&b2.y); + secp256k1_fe_normalize(&b2.z); + ret &= secp256k1_fe_cmp_var(&a2.x, &b2.x) == 0; + ret &= secp256k1_fe_cmp_var(&a2.y, &b2.y) == 0; + ret &= secp256k1_fe_cmp_var(&a2.z, &b2.z) == 0; + } + return ret; +} + void ge_equals_gej(const secp256k1_ge_t *a, const secp256k1_gej_t *b) { secp256k1_fe_t z2s; secp256k1_fe_t u1, u2, s1, s2; @@ -1033,6 +1055,9 @@ void test_ge(void) { secp256k1_ge_t *ge_set_all = (secp256k1_ge_t *)malloc((4 * runs + 1) * sizeof(secp256k1_ge_t)); secp256k1_ge_set_all_gej_var(4 * runs + 1, ge_set_all, gej); for (i = 0; i < 4 * runs + 1; i++) { + secp256k1_fe_t s; + random_fe_non_zero(&s); + secp256k1_gej_rescale(&gej[i], &s); ge_equals_gej(&ge_set_all[i], &gej[i]); } free(ge_set_all); @@ -1203,6 +1228,87 @@ void run_wnaf(void) { } } +void test_ecmult_constants(void) { + /* Test ecmult_gen() for [0..36) and [order-36..0). */ + secp256k1_scalar_t x; + secp256k1_gej_t r; + secp256k1_ge_t ng; + int i; + int j; + secp256k1_ge_neg(&ng, &secp256k1_ge_const_g); + for (i = 0; i < 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&secp256k1_ge_const_g, &r); + } + secp256k1_gej_add_ge(&r, &r, &ng); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } + for (i = 1; i <= 36; i++ ) { + secp256k1_scalar_set_int(&x, i); + secp256k1_scalar_negate(&x, &x); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x); + for (j = 0; j < i; j++) { + if (j == i - 1) { + ge_equals_gej(&ng, &r); + } + secp256k1_gej_add_ge(&r, &r, &secp256k1_ge_const_g); + } + CHECK(secp256k1_gej_is_infinity(&r)); + } +} + +void run_ecmult_constants(void) { + test_ecmult_constants(); +} + +void test_ecmult_gen_blind(void) { + /* Test ecmult_gen() blinding and confirm that the blinding changes, the affline points match, and the z's don't match. */ + secp256k1_scalar_t key; + secp256k1_scalar_t b; + unsigned char seed32[32]; + secp256k1_gej_t pgej; + secp256k1_gej_t pgej2; + secp256k1_gej_t i; + secp256k1_ge_t pge; + random_scalar_order_test(&key); + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key); + secp256k1_rand256(seed32); + b = ctx->ecmult_gen_ctx.blind; + i = ctx->ecmult_gen_ctx.initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32); + CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind)); + 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)); + secp256k1_ge_set_gej(&pge, &pgej); + ge_equals_gej(&pge, &pgej2); +} + +void test_ecmult_gen_blind_reset(void) { + /* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */ + secp256k1_scalar_t b; + secp256k1_gej_t initial; + secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0); + b = ctx->ecmult_gen_ctx.blind; + initial = ctx->ecmult_gen_ctx.initial; + 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)); +} + +void run_ecmult_gen_blind(void) { + int i; + test_ecmult_gen_blind_reset(); + for (i = 0; i < 10; i++) { + test_ecmult_gen_blind(); + } +} + + void random_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *key, const secp256k1_scalar_t *msg, int *recid) { secp256k1_scalar_t nonce; do { @@ -1913,6 +2019,11 @@ int main(int argc, char **argv) { run_context_tests(); ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if (secp256k1_rand32() & 1) { + secp256k1_rand256(run32); + CHECK(secp256k1_context_randomize(ctx, secp256k1_rand32() & 1 ? run32 : NULL)); + } + run_sha256_tests(); run_hmac_sha256_tests(); run_rfc6979_hmac_sha256_tests(); @@ -1941,6 +2052,8 @@ int main(int argc, char **argv) { run_wnaf(); run_point_times_order(); run_ecmult_chain(); + run_ecmult_constants(); + run_ecmult_gen_blind(); /* ecdsa tests */ run_random_pubkeys();