Skip to content

Commit d227579

Browse files
committed
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.
1 parent 426fa52 commit d227579

File tree

7 files changed

+209
-3
lines changed

7 files changed

+209
-3
lines changed

include/secp256k1.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,18 @@ SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
328328
const unsigned char *tweak
329329
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
330330

331+
/** Updates the context randomization.
332+
* Returns: 1: randomization successfully updated
333+
* 0: error
334+
* In: ctx: pointer to a context object (cannot be NULL)
335+
* seed32: pointer to a 32-byte random seed (NULL resets to initial state)
336+
*/
337+
SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
338+
secp256k1_context_t* ctx,
339+
const unsigned char *seed32
340+
) SECP256K1_ARG_NONNULL(1);
341+
342+
331343
# ifdef __cplusplus
332344
}
333345
# endif

src/ecmult_gen.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ typedef struct {
2424
* the intermediate sums while computing a*G.
2525
*/
2626
secp256k1_ge_storage_t (*prec)[64][16]; /* prec[j][i] = 16^j * i * G + U_i */
27+
secp256k1_scalar_t blind;
28+
secp256k1_gej_t initial;
2729
} secp256k1_ecmult_gen_context_t;
2830

2931
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
3638
/** Multiply with the generator: R = a*G */
3739
static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t* ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *a);
3840

41+
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32);
42+
3943
#endif

src/ecmult_gen_impl.h

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**********************************************************************
2-
* Copyright (c) 2013, 2014 Pieter Wuille *
2+
* Copyright (c) 2013, 2014, 2015 Pieter Wuille, Gregory Maxwell *
33
* Distributed under the MIT software license, see the accompanying *
44
* file COPYING or http://www.opensource.org/licenses/mit-license.php.*
55
**********************************************************************/
@@ -10,6 +10,7 @@
1010
#include "scalar.h"
1111
#include "group.h"
1212
#include "ecmult_gen.h"
13+
#include "hash_impl.h"
1314

1415
static void secp256k1_ecmult_gen_context_init(secp256k1_ecmult_gen_context_t *ctx) {
1516
ctx->prec = NULL;
@@ -74,6 +75,7 @@ static void secp256k1_ecmult_gen_context_build(secp256k1_ecmult_gen_context_t *c
7475
secp256k1_ge_to_storage(&(*ctx->prec)[j][i], &prec[j*16 + i]);
7576
}
7677
}
78+
secp256k1_ecmult_gen_blind(ctx, NULL);
7779
}
7880

7981
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
8789
} else {
8890
dst->prec = (secp256k1_ge_storage_t (*)[64][16])checked_malloc(sizeof(*dst->prec));
8991
memcpy(dst->prec, src->prec, sizeof(*dst->prec));
92+
dst->initial = src->initial;
93+
dst->blind = src->blind;
9094
}
9195
}
9296

9397
static void secp256k1_ecmult_gen_context_clear(secp256k1_ecmult_gen_context_t *ctx) {
9498
free(ctx->prec);
99+
secp256k1_scalar_clear(&ctx->blind);
100+
secp256k1_gej_clear(&ctx->initial);
95101
ctx->prec = NULL;
96102
}
97103

98104
static void secp256k1_ecmult_gen(const secp256k1_ecmult_gen_context_t *ctx, secp256k1_gej_t *r, const secp256k1_scalar_t *gn) {
99105
secp256k1_ge_t add;
100106
secp256k1_ge_storage_t adds;
107+
secp256k1_scalar_t gnb;
101108
int bits;
102109
int i, j;
103110
memset(&adds, 0, sizeof(adds));
104-
secp256k1_gej_set_infinity(r);
111+
*r = ctx->initial;
112+
/* Blind scalar/point multiplication by computing (n-b)G + bG instead of nG. */
113+
secp256k1_scalar_add(&gnb, gn, &ctx->blind);
105114
add.infinity = 0;
106115
for (j = 0; j < 64; j++) {
107-
bits = secp256k1_scalar_get_bits(gn, j * 4, 4);
116+
bits = secp256k1_scalar_get_bits(&gnb, j * 4, 4);
108117
for (i = 0; i < 16; i++) {
109118
/** This uses a conditional move to avoid any secret data in array indexes.
110119
* _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
123132
}
124133
bits = 0;
125134
secp256k1_ge_clear(&add);
135+
secp256k1_scalar_clear(&gnb);
136+
}
137+
138+
/* Setup blinding values for secp256k1_ecmult_gen. */
139+
static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context_t *ctx, const unsigned char *seed32) {
140+
secp256k1_scalar_t b;
141+
secp256k1_gej_t gb;
142+
secp256k1_fe_t s;
143+
unsigned char nonce32[32];
144+
secp256k1_rfc6979_hmac_sha256_t rng;
145+
int retry;
146+
if (!seed32) {
147+
/* When seed is NULL, reset the initial point and blinding value. */
148+
secp256k1_gej_set_ge(&ctx->initial, &secp256k1_ge_const_g);
149+
secp256k1_gej_neg(&ctx->initial, &ctx->initial);
150+
secp256k1_scalar_set_int(&ctx->blind, 1);
151+
}
152+
/* The prior blinding value (if not reset) is chained forward by including it in the hash. */
153+
secp256k1_scalar_get_b32(nonce32, &ctx->blind);
154+
/** Using a CSPRNG allows a failure free interface, avoids needing large amounts of random data,
155+
* and guards against weak or adversarial seeds. This is a simpler and safer interface than
156+
* asking the caller for blinding values directly and expecting them to retry on failure.
157+
*/
158+
secp256k1_rfc6979_hmac_sha256_initialize(&rng, seed32 ? seed32 : nonce32, 32, nonce32, 32, NULL, 0);
159+
/* Retry for out of range results to achieve uniformity. */
160+
do {
161+
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
162+
retry = !secp256k1_fe_set_b32(&s, nonce32);
163+
retry |= secp256k1_fe_is_zero(&s);
164+
} while (retry);
165+
/* Randomize the projection to defend against multiplier sidechannels. */
166+
secp256k1_gej_rescale(&ctx->initial, &s);
167+
secp256k1_fe_clear(&s);
168+
do {
169+
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
170+
secp256k1_scalar_set_b32(&b, nonce32, &retry);
171+
/* A blinding value of 0 works, but would undermine the projection hardening. */
172+
retry |= secp256k1_scalar_is_zero(&b);
173+
} while (retry);
174+
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
175+
memset(nonce32, 0, 32);
176+
secp256k1_ecmult_gen(ctx, &gb, &b);
177+
secp256k1_scalar_negate(&b, &b);
178+
ctx->blind = b;
179+
ctx->initial = gb;
180+
secp256k1_scalar_clear(&b);
181+
secp256k1_gej_clear(&gb);
126182
}
127183

128184
#endif

src/group.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,7 @@ static void secp256k1_ge_from_storage(secp256k1_ge_t *r, const secp256k1_ge_stor
115115
/** If flag is true, set *r equal to *a; otherwise leave it. Constant-time. */
116116
static void secp256k1_ge_storage_cmov(secp256k1_ge_storage_t *r, const secp256k1_ge_storage_t *a, int flag);
117117

118+
/** Rescale a jacobian point by b which must be non-zero. Constant-time. */
119+
static void secp256k1_gej_rescale(secp256k1_gej_t *r, const secp256k1_fe_t *b);
120+
118121
#endif

src/group_impl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,17 @@ static void secp256k1_gej_add_ge(secp256k1_gej_t *r, const secp256k1_gej_t *a, c
396396
r->infinity = infinity;
397397
}
398398

399+
static void secp256k1_gej_rescale(secp256k1_gej_t *r, const secp256k1_fe_t *s) {
400+
/* Operations: 4 mul, 1 sqr */
401+
secp256k1_fe_t zz;
402+
VERIFY_CHECK(!secp256k1_fe_is_zero(s));
403+
secp256k1_fe_sqr(&zz, s);
404+
secp256k1_fe_mul(&r->x, &r->x, &zz); /* r->x *= s^2 */
405+
secp256k1_fe_mul(&r->y, &r->y, &zz);
406+
secp256k1_fe_mul(&r->y, &r->y, s); /* r->y *= s^3 */
407+
secp256k1_fe_mul(&r->z, &r->z, s); /* r->z *= s */
408+
}
409+
399410
static void secp256k1_ge_to_storage(secp256k1_ge_storage_t *r, const secp256k1_ge_t *a) {
400411
secp256k1_fe_t x, y;
401412
VERIFY_CHECK(!a->infinity);

src/secp256k1.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,10 @@ int secp256k1_ec_privkey_import(const secp256k1_context_t* ctx, unsigned char *s
410410
secp256k1_scalar_clear(&key);
411411
return ret;
412412
}
413+
414+
int secp256k1_context_randomize(secp256k1_context_t* ctx, const unsigned char *seed32) {
415+
DEBUG_CHECK(ctx != NULL);
416+
DEBUG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
417+
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
418+
return 1;
419+
}

src/tests.c

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,28 @@ void ge_equals_ge(const secp256k1_ge_t *a, const secp256k1_ge_t *b) {
907907
CHECK(secp256k1_fe_equal_var(&b->y, &b->y));
908908
}
909909

910+
/* This compares jacobian points including their Z, not just their geometric meaning. */
911+
int gej_xyz_equals_gej(const secp256k1_gej_t *a, const secp256k1_gej_t *b) {
912+
secp256k1_gej_t a2;
913+
secp256k1_gej_t b2;
914+
int ret = 1;
915+
ret &= a->infinity == b->infinity;
916+
if (ret && !a->infinity) {
917+
a2 = *a;
918+
b2 = *b;
919+
secp256k1_fe_normalize(&a2.x);
920+
secp256k1_fe_normalize(&a2.y);
921+
secp256k1_fe_normalize(&a2.z);
922+
secp256k1_fe_normalize(&b2.x);
923+
secp256k1_fe_normalize(&b2.y);
924+
secp256k1_fe_normalize(&b2.z);
925+
ret &= secp256k1_fe_cmp_var(&a2.x, &b2.x) == 0;
926+
ret &= secp256k1_fe_cmp_var(&a2.y, &b2.y) == 0;
927+
ret &= secp256k1_fe_cmp_var(&a2.z, &b2.z) == 0;
928+
}
929+
return ret;
930+
}
931+
910932
void ge_equals_gej(const secp256k1_ge_t *a, const secp256k1_gej_t *b) {
911933
secp256k1_fe_t z2s;
912934
secp256k1_fe_t u1, u2, s1, s2;
@@ -1033,6 +1055,9 @@ void test_ge(void) {
10331055
secp256k1_ge_t *ge_set_all = (secp256k1_ge_t *)malloc((4 * runs + 1) * sizeof(secp256k1_ge_t));
10341056
secp256k1_ge_set_all_gej_var(4 * runs + 1, ge_set_all, gej);
10351057
for (i = 0; i < 4 * runs + 1; i++) {
1058+
secp256k1_fe_t s;
1059+
random_fe_non_zero(&s);
1060+
secp256k1_gej_rescale(&gej[i], &s);
10361061
ge_equals_gej(&ge_set_all[i], &gej[i]);
10371062
}
10381063
free(ge_set_all);
@@ -1203,6 +1228,87 @@ void run_wnaf(void) {
12031228
}
12041229
}
12051230

1231+
void test_ecmult_constants(void) {
1232+
/* Test ecmult_gen() for [0..36) and [order-36..0). */
1233+
secp256k1_scalar_t x;
1234+
secp256k1_gej_t r;
1235+
secp256k1_ge_t ng;
1236+
int i;
1237+
int j;
1238+
secp256k1_ge_neg(&ng, &secp256k1_ge_const_g);
1239+
for (i = 0; i < 36; i++ ) {
1240+
secp256k1_scalar_set_int(&x, i);
1241+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x);
1242+
for (j = 0; j < i; j++) {
1243+
if (j == i - 1) {
1244+
ge_equals_gej(&secp256k1_ge_const_g, &r);
1245+
}
1246+
secp256k1_gej_add_ge(&r, &r, &ng);
1247+
}
1248+
CHECK(secp256k1_gej_is_infinity(&r));
1249+
}
1250+
for (i = 1; i <= 36; i++ ) {
1251+
secp256k1_scalar_set_int(&x, i);
1252+
secp256k1_scalar_negate(&x, &x);
1253+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &r, &x);
1254+
for (j = 0; j < i; j++) {
1255+
if (j == i - 1) {
1256+
ge_equals_gej(&ng, &r);
1257+
}
1258+
secp256k1_gej_add_ge(&r, &r, &secp256k1_ge_const_g);
1259+
}
1260+
CHECK(secp256k1_gej_is_infinity(&r));
1261+
}
1262+
}
1263+
1264+
void run_ecmult_constants(void) {
1265+
test_ecmult_constants();
1266+
}
1267+
1268+
void test_ecmult_gen_blind(void) {
1269+
/* Test ecmult_gen() blinding and confirm that the blinding changes, the affline points match, and the z's don't match. */
1270+
secp256k1_scalar_t key;
1271+
secp256k1_scalar_t b;
1272+
unsigned char seed32[32];
1273+
secp256k1_gej_t pgej;
1274+
secp256k1_gej_t pgej2;
1275+
secp256k1_gej_t i;
1276+
secp256k1_ge_t pge;
1277+
random_scalar_order_test(&key);
1278+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej, &key);
1279+
secp256k1_rand256(seed32);
1280+
b = ctx->ecmult_gen_ctx.blind;
1281+
i = ctx->ecmult_gen_ctx.initial;
1282+
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
1283+
CHECK(!secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind));
1284+
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pgej2, &key);
1285+
CHECK(!gej_xyz_equals_gej(&pgej, &pgej2));
1286+
CHECK(!gej_xyz_equals_gej(&i, &ctx->ecmult_gen_ctx.initial));
1287+
secp256k1_ge_set_gej(&pge, &pgej);
1288+
ge_equals_gej(&pge, &pgej2);
1289+
}
1290+
1291+
void test_ecmult_gen_blind_reset(void) {
1292+
/* Test ecmult_gen() blinding reset and confirm that the blinding is consistent. */
1293+
secp256k1_scalar_t b;
1294+
secp256k1_gej_t initial;
1295+
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0);
1296+
b = ctx->ecmult_gen_ctx.blind;
1297+
initial = ctx->ecmult_gen_ctx.initial;
1298+
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, 0);
1299+
CHECK(secp256k1_scalar_eq(&b, &ctx->ecmult_gen_ctx.blind));
1300+
CHECK(gej_xyz_equals_gej(&initial, &ctx->ecmult_gen_ctx.initial));
1301+
}
1302+
1303+
void run_ecmult_gen_blind(void) {
1304+
int i;
1305+
test_ecmult_gen_blind_reset();
1306+
for (i = 0; i < 10; i++) {
1307+
test_ecmult_gen_blind();
1308+
}
1309+
}
1310+
1311+
12061312
void random_sign(secp256k1_ecdsa_sig_t *sig, const secp256k1_scalar_t *key, const secp256k1_scalar_t *msg, int *recid) {
12071313
secp256k1_scalar_t nonce;
12081314
do {
@@ -1913,6 +2019,11 @@ int main(int argc, char **argv) {
19132019
run_context_tests();
19142020
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
19152021

2022+
if (secp256k1_rand32() & 1) {
2023+
secp256k1_rand256(run32);
2024+
CHECK(secp256k1_context_randomize(ctx, secp256k1_rand32() & 1 ? run32 : NULL));
2025+
}
2026+
19162027
run_sha256_tests();
19172028
run_hmac_sha256_tests();
19182029
run_rfc6979_hmac_sha256_tests();
@@ -1941,6 +2052,8 @@ int main(int argc, char **argv) {
19412052
run_wnaf();
19422053
run_point_times_order();
19432054
run_ecmult_chain();
2055+
run_ecmult_constants();
2056+
run_ecmult_gen_blind();
19442057

19452058
/* ecdsa tests */
19462059
run_random_pubkeys();

0 commit comments

Comments
 (0)