Add scalar blinding and a secp256k1_context_randomize() call.
This computes (n-b)G + bG with random value b, in place of nG in ecmult_gen() for signing. This is intended to reduce exposure to potential power/EMI sidechannels during signing and pubkey generation by blinding the secret value with another value which is hopefully unknown to the attacker. It may not be very helpful if the attacker is able to observe the setup or if even the scalar addition has an unacceptable leak, but it has low overhead in any case and the security should be purely additive on top of the existing defenses against sidechannels.
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user