Skip to content

Commit 96d8ccb

Browse files
Merge bitcoin#710: Eliminate harmless non-constant time operations on secret data.
7b50483 Adds a declassify operation to aid constant-time analysis. (Gregory Maxwell) 34a67c7 Eliminate harmless non-constant time operations on secret data. (Gregory Maxwell) Pull request description: There were several places where the code was non-constant time for invalid secret inputs. These are harmless under sane use but get in the way of automatic const-time validation. (Nonce overflow in signing is not addressed, nor is s==0 in signing) ACKs for top commit: sipa: utACK 7b50483 real-or-random: ACK 7b50483 I read the code carefully and tested it jonasnick: reACK 7b50483 Tree-SHA512: 0776c3a86e723d2f97b9b9cb31d0d0e59dfcf308093b3f46fbc859f73f9957f3fa977d03b57727232040368d058701ef107838f9b1ec98f925ec78ddad495c4e
2 parents 0585b8b + 7b50483 commit 96d8ccb

20 files changed

+197
-114
lines changed

.travis.yml

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ language: c
22
os: linux
33
addons:
44
apt:
5-
packages: libgmp-dev
5+
packages:
6+
- libgmp-dev
7+
- valgrind
68
compiler:
79
- clang
810
- gcc
@@ -60,18 +62,10 @@ matrix:
6062
env:
6163
- BIGNUM=no ENDOMORPHISM=yes ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes
6264
- VALGRIND=yes EXTRAFLAGS="--disable-openssl-tests CPPFLAGS=-DVALGRIND" BUILD=
63-
addons:
64-
apt:
65-
packages:
66-
- valgrind
6765
- compiler: gcc
6866
env: # The same as above but without endomorphism.
6967
- BIGNUM=no ENDOMORPHISM=no ASM=x86_64 EXPERIMENTAL=yes ECDH=yes RECOVERY=yes
7068
- VALGRIND=yes EXTRAFLAGS="--disable-openssl-tests CPPFLAGS=-DVALGRIND" BUILD=
71-
addons:
72-
apt:
73-
packages:
74-
- valgrind
7569

7670
before_script: ./autogen.sh
7771

Makefile.am

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ libsecp256k1_la_SOURCES = src/secp256k1.c
6969
libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES)
7070
libsecp256k1_la_LIBADD = $(SECP_LIBS) $(COMMON_LIB)
7171

72+
if VALGRIND_ENABLED
73+
libsecp256k1_la_CPPFLAGS += -DVALGRIND
74+
endif
75+
7276
noinst_PROGRAMS =
7377
if USE_BENCHMARK
7478
noinst_PROGRAMS += bench_verify bench_sign bench_internal bench_ecmult

configure.ac

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ AC_ARG_WITH([ecmult-gen-precision], [AS_HELP_STRING([--with-ecmult-gen-precision
170170

171171
AC_CHECK_TYPES([__int128])
172172

173+
AC_CHECK_HEADER([valgrind/memcheck.h], [enable_valgrind=yes], [enable_valgrind=no], [])
174+
AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"])
175+
173176
if test x"$enable_coverage" = x"yes"; then
174177
AC_DEFINE(COVERAGE, 1, [Define this symbol to compile out all VERIFY code])
175178
CFLAGS="$CFLAGS -O0 --coverage"

include/secp256k1.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,14 @@ typedef int (*secp256k1_nonce_function)(
162162
/** The higher bits contain the actual data. Do not use directly. */
163163
#define SECP256K1_FLAGS_BIT_CONTEXT_VERIFY (1 << 8)
164164
#define SECP256K1_FLAGS_BIT_CONTEXT_SIGN (1 << 9)
165+
#define SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY (1 << 10)
165166
#define SECP256K1_FLAGS_BIT_COMPRESSION (1 << 8)
166167

167168
/** Flags to pass to secp256k1_context_create, secp256k1_context_preallocated_size, and
168169
* secp256k1_context_preallocated_create. */
169170
#define SECP256K1_CONTEXT_VERIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY)
170171
#define SECP256K1_CONTEXT_SIGN (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_SIGN)
172+
#define SECP256K1_CONTEXT_DECLASSIFY (SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_DECLASSIFY)
171173
#define SECP256K1_CONTEXT_NONE (SECP256K1_FLAGS_TYPE_CONTEXT)
172174

173175
/** Flag to pass to secp256k1_ec_pubkey_serialize. */

src/ecdsa_impl.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
280280
secp256k1_ge r;
281281
secp256k1_scalar n;
282282
int overflow = 0;
283+
int high;
283284

284285
secp256k1_ecmult_gen(ctx, &rp, nonce);
285286
secp256k1_ge_set_gej(&r, &rp);
@@ -295,7 +296,7 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
295296
/* The overflow condition is cryptographically unreachable as hitting it requires finding the discrete log
296297
* of some P where P.x >= order, and only 1 in about 2^127 points meet this criteria.
297298
*/
298-
*recid = (overflow ? 2 : 0) | (secp256k1_fe_is_odd(&r.y) ? 1 : 0);
299+
*recid = (overflow << 1) | secp256k1_fe_is_odd(&r.y);
299300
}
300301
secp256k1_scalar_mul(&n, sigr, seckey);
301302
secp256k1_scalar_add(&n, &n, message);
@@ -304,16 +305,12 @@ static int secp256k1_ecdsa_sig_sign(const secp256k1_ecmult_gen_context *ctx, sec
304305
secp256k1_scalar_clear(&n);
305306
secp256k1_gej_clear(&rp);
306307
secp256k1_ge_clear(&r);
307-
if (secp256k1_scalar_is_zero(sigs)) {
308-
return 0;
309-
}
310-
if (secp256k1_scalar_is_high(sigs)) {
311-
secp256k1_scalar_negate(sigs, sigs);
312-
if (recid) {
313-
*recid ^= 1;
314-
}
308+
high = secp256k1_scalar_is_high(sigs);
309+
secp256k1_scalar_cond_negate(sigs, high);
310+
if (recid) {
311+
*recid ^= high;
315312
}
316-
return 1;
313+
return !secp256k1_scalar_is_zero(sigs);
317314
}
318315

319316
#endif /* SECP256K1_ECDSA_IMPL_H */

src/eckey_impl.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,11 @@ static int secp256k1_eckey_pubkey_tweak_add(const secp256k1_ecmult_context *ctx,
7575
}
7676

7777
static int secp256k1_eckey_privkey_tweak_mul(secp256k1_scalar *key, const secp256k1_scalar *tweak) {
78-
if (secp256k1_scalar_is_zero(tweak)) {
79-
return 0;
80-
}
78+
int ret;
79+
ret = !secp256k1_scalar_is_zero(tweak);
8180

8281
secp256k1_scalar_mul(key, key, tweak);
83-
return 1;
82+
return ret;
8483
}
8584

8685
static int secp256k1_eckey_pubkey_tweak_mul(const secp256k1_ecmult_context *ctx, secp256k1_ge *key, const secp256k1_scalar *tweak) {

src/ecmult_gen_impl.h

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
163163
secp256k1_fe s;
164164
unsigned char nonce32[32];
165165
secp256k1_rfc6979_hmac_sha256 rng;
166-
int retry;
166+
int overflow;
167167
unsigned char keydata[64] = {0};
168168
if (seed32 == NULL) {
169169
/* When seed is NULL, reset the initial point and blinding value. */
@@ -183,21 +183,18 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
183183
}
184184
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, seed32 ? 64 : 32);
185185
memset(keydata, 0, sizeof(keydata));
186-
/* Retry for out of range results to achieve uniformity. */
187-
do {
188-
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
189-
retry = !secp256k1_fe_set_b32(&s, nonce32);
190-
retry = retry || secp256k1_fe_is_zero(&s);
191-
} while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > Fp. */
186+
/* Accept unobservably small non-uniformity. */
187+
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
188+
overflow = !secp256k1_fe_set_b32(&s, nonce32);
189+
overflow |= secp256k1_fe_is_zero(&s);
190+
secp256k1_fe_cmov(&s, &secp256k1_fe_one, overflow);
192191
/* Randomize the projection to defend against multiplier sidechannels. */
193192
secp256k1_gej_rescale(&ctx->initial, &s);
194193
secp256k1_fe_clear(&s);
195-
do {
196-
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
197-
secp256k1_scalar_set_b32(&b, nonce32, &retry);
198-
/* A blinding value of 0 works, but would undermine the projection hardening. */
199-
retry = retry || secp256k1_scalar_is_zero(&b);
200-
} while (retry); /* This branch true is cryptographically unreachable. Requires sha256_hmac output > order. */
194+
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
195+
secp256k1_scalar_set_b32(&b, nonce32, NULL);
196+
/* A blinding value of 0 works, but would undermine the projection hardening. */
197+
secp256k1_scalar_cmov(&b, &secp256k1_scalar_one, secp256k1_scalar_is_zero(&b));
201198
secp256k1_rfc6979_hmac_sha256_finalize(&rng);
202199
memset(nonce32, 0, 32);
203200
secp256k1_ecmult_gen(ctx, &gb, &b);

src/field_10x26_impl.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
320320
}
321321

322322
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
323+
int ret;
323324
r->n[0] = (uint32_t)a[31] | ((uint32_t)a[30] << 8) | ((uint32_t)a[29] << 16) | ((uint32_t)(a[28] & 0x3) << 24);
324325
r->n[1] = (uint32_t)((a[28] >> 2) & 0x3f) | ((uint32_t)a[27] << 6) | ((uint32_t)a[26] << 14) | ((uint32_t)(a[25] & 0xf) << 22);
325326
r->n[2] = (uint32_t)((a[25] >> 4) & 0xf) | ((uint32_t)a[24] << 4) | ((uint32_t)a[23] << 12) | ((uint32_t)(a[22] & 0x3f) << 20);
@@ -331,15 +332,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
331332
r->n[8] = (uint32_t)a[5] | ((uint32_t)a[4] << 8) | ((uint32_t)a[3] << 16) | ((uint32_t)(a[2] & 0x3) << 24);
332333
r->n[9] = (uint32_t)((a[2] >> 2) & 0x3f) | ((uint32_t)a[1] << 6) | ((uint32_t)a[0] << 14);
333334

334-
if (r->n[9] == 0x3FFFFFUL && (r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL && (r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL) {
335-
return 0;
336-
}
335+
ret = !((r->n[9] == 0x3FFFFFUL) & ((r->n[8] & r->n[7] & r->n[6] & r->n[5] & r->n[4] & r->n[3] & r->n[2]) == 0x3FFFFFFUL) & ((r->n[1] + 0x40UL + ((r->n[0] + 0x3D1UL) >> 26)) > 0x3FFFFFFUL));
337336
#ifdef VERIFY
338337
r->magnitude = 1;
339-
r->normalized = 1;
340-
secp256k1_fe_verify(r);
338+
if (ret) {
339+
r->normalized = 1;
340+
secp256k1_fe_verify(r);
341+
} else {
342+
r->normalized = 0;
343+
}
341344
#endif
342-
return 1;
345+
return ret;
343346
}
344347

345348
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
@@ -1107,10 +1110,10 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
11071110
r->n[8] = (r->n[8] & mask0) | (a->n[8] & mask1);
11081111
r->n[9] = (r->n[9] & mask0) | (a->n[9] & mask1);
11091112
#ifdef VERIFY
1110-
if (a->magnitude > r->magnitude) {
1113+
if (flag) {
11111114
r->magnitude = a->magnitude;
1115+
r->normalized = a->normalized;
11121116
}
1113-
r->normalized &= a->normalized;
11141117
#endif
11151118
}
11161119

src/field_5x52_impl.h

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ static int secp256k1_fe_cmp_var(const secp256k1_fe *a, const secp256k1_fe *b) {
283283
}
284284

285285
static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
286+
int ret;
286287
r->n[0] = (uint64_t)a[31]
287288
| ((uint64_t)a[30] << 8)
288289
| ((uint64_t)a[29] << 16)
@@ -317,15 +318,17 @@ static int secp256k1_fe_set_b32(secp256k1_fe *r, const unsigned char *a) {
317318
| ((uint64_t)a[2] << 24)
318319
| ((uint64_t)a[1] << 32)
319320
| ((uint64_t)a[0] << 40);
320-
if (r->n[4] == 0x0FFFFFFFFFFFFULL && (r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL && r->n[0] >= 0xFFFFEFFFFFC2FULL) {
321-
return 0;
322-
}
321+
ret = !((r->n[4] == 0x0FFFFFFFFFFFFULL) & ((r->n[3] & r->n[2] & r->n[1]) == 0xFFFFFFFFFFFFFULL) & (r->n[0] >= 0xFFFFEFFFFFC2FULL));
323322
#ifdef VERIFY
324323
r->magnitude = 1;
325-
r->normalized = 1;
326-
secp256k1_fe_verify(r);
324+
if (ret) {
325+
r->normalized = 1;
326+
secp256k1_fe_verify(r);
327+
} else {
328+
r->normalized = 0;
329+
}
327330
#endif
328-
return 1;
331+
return ret;
329332
}
330333

331334
/** Convert a field element to a 32-byte big endian value. Requires the input to be normalized */
@@ -454,10 +457,10 @@ static SECP256K1_INLINE void secp256k1_fe_cmov(secp256k1_fe *r, const secp256k1_
454457
r->n[3] = (r->n[3] & mask0) | (a->n[3] & mask1);
455458
r->n[4] = (r->n[4] & mask0) | (a->n[4] & mask1);
456459
#ifdef VERIFY
457-
if (a->magnitude > r->magnitude) {
460+
if (flag) {
458461
r->magnitude = a->magnitude;
462+
r->normalized = a->normalized;
459463
}
460-
r->normalized &= a->normalized;
461464
#endif
462465
}
463466

src/field_impl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,4 +315,6 @@ static int secp256k1_fe_is_quad_var(const secp256k1_fe *a) {
315315
#endif
316316
}
317317

318+
static const secp256k1_fe secp256k1_fe_one = SECP256K1_FE_CONST(0, 0, 0, 0, 0, 0, 0, 1);
319+
318320
#endif /* SECP256K1_FIELD_IMPL_H */

0 commit comments

Comments
 (0)