Merge pull request #190

d227579 Add scalar blinding and a secp256k1_context_randomize() call. (Gregory Maxwell)
This commit is contained in:
Pieter Wuille 2015-04-22 12:32:58 -07:00
commit 61c1b1ed46
No known key found for this signature in database
GPG key ID: 57896D2FF8F0B657
7 changed files with 209 additions and 3 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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;
}

View file

@ -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();