Skip to content

Commit 5fbff5d

Browse files
committed
Merge #1170: contexts: Forbid destroying, cloning and randomizing the static context
e39d954 tests: Add CHECK_ILLEGAL(_VOID) macros and use in static ctx tests (Tim Ruffing) 61841fc contexts: Forbid randomizing secp256k1_context_static (Tim Ruffing) 4b6df5e contexts: Forbid cloning/destroying secp256k1_context_static (Tim Ruffing) Pull request description: As discussed in #1126. For randomization, this has a history. Initially, this threw the illegal callback but then we changed it to be a no-op on non-signing contexts: 6198375 But this was with (non-static) none/verification contexts in mind, not with the static context. If we anyway forbid cloning the static context, you should never a way to randomize a copy of the static context. (You need a copy because the static context itself is not writable. But you cannot obtain a copy except when using memcpy etc.) ACKs for top commit: sipa: utACK e39d954 apoelstra: ACK e39d954 Tree-SHA512: dc804b15652d536b5d67db7297ac0e65eab3a64cbb35a9856329cb87e7ea0fe8ea733108104b3bba580077fe03d6ad6b161c797cf866a74722bab7849f0bb60c
2 parents 233822d + e39d954 commit 5fbff5d

File tree

5 files changed

+155
-41
lines changed

5 files changed

+155
-41
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
#### Changed
11+
- Forbade cloning or destroying `secp256k1_context_static`. Create a new context instead of cloning the static context. (If this change breaks your code, your code is probably wrong.)
12+
- Forbade randomizing (copies of) `secp256k1_context_static`. Randomizing a copy of `secp256k1_context_static` did not have any effect and did not provide defense-in-depth protection against side-channel attacks. Create a new context if you want to benefit from randomization.
13+
1014
## [0.2.0] - 2022-12-12
1115

1216
#### Added

include/secp256k1.h

+11-11
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,11 @@ SECP256K1_API secp256k1_context* secp256k1_context_create(
291291
* called at most once for every call of this function. If you need to avoid dynamic
292292
* memory allocation entirely, see the functions in secp256k1_preallocated.h.
293293
*
294+
* Cloning secp256k1_context_static is not possible, and should not be emulated by
295+
* the caller (e.g., using memcpy). Create a new context instead.
296+
*
294297
* Returns: a newly created context object.
295-
* Args: ctx: an existing context to copy
298+
* Args: ctx: an existing context to copy (not secp256k1_context_static)
296299
*/
297300
SECP256K1_API secp256k1_context* secp256k1_context_clone(
298301
const secp256k1_context* ctx
@@ -310,6 +313,7 @@ SECP256K1_API secp256k1_context* secp256k1_context_clone(
310313
*
311314
* Args: ctx: an existing context to destroy, constructed using
312315
* secp256k1_context_create or secp256k1_context_clone
316+
* (i.e., not secp256k1_context_static).
313317
*/
314318
SECP256K1_API void secp256k1_context_destroy(
315319
secp256k1_context* ctx
@@ -820,10 +824,10 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
820824

821825
/** Randomizes the context to provide enhanced protection against side-channel leakage.
822826
*
823-
* Returns: 1: randomization successful (or called on copy of secp256k1_context_static)
827+
* Returns: 1: randomization successful
824828
* 0: error
825-
* Args: ctx: pointer to a context object.
826-
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state)
829+
* Args: ctx: pointer to a context object (not secp256k1_context_static).
830+
* In: seed32: pointer to a 32-byte random seed (NULL resets to initial state).
827831
*
828832
* While secp256k1 code is written and tested to be constant-time no matter what
829833
* secret values are, it is possible that a compiler may output code which is not,
@@ -838,21 +842,17 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul(
838842
* functions that perform computations involving secret keys, e.g., signing and
839843
* public key generation. It is possible to call this function more than once on
840844
* the same context, and doing so before every few computations involving secret
841-
* keys is recommended as a defense-in-depth measure.
845+
* keys is recommended as a defense-in-depth measure. Randomization of the static
846+
* context secp256k1_context_static is not supported.
842847
*
843848
* Currently, the random seed is mainly used for blinding multiplications of a
844849
* secret scalar with the elliptic curve base point. Multiplications of this
845850
* kind are performed by exactly those API functions which are documented to
846-
* require a context that is not the secp256k1_context_static. As a rule of thumb,
851+
* require a context that is not secp256k1_context_static. As a rule of thumb,
847852
* these are all functions which take a secret key (or a keypair) as an input.
848853
* A notable exception to that rule is the ECDH module, which relies on a different
849854
* kind of elliptic curve point multiplication and thus does not benefit from
850855
* enhanced protection against side-channel leakage currently.
851-
*
852-
* It is safe to call this function on a copy of secp256k1_context_static in writable
853-
* memory (e.g., obtained via secp256k1_context_clone). In that case, this
854-
* function is guaranteed to return 1, but the call will have no effect because
855-
* the static context (or a copy thereof) is not meant to be randomized.
856856
*/
857857
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize(
858858
secp256k1_context* ctx,

include/secp256k1_preallocated.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,11 @@ SECP256K1_API size_t secp256k1_context_preallocated_clone_size(
8888
* the lifetime of this context object, see the description of
8989
* secp256k1_context_preallocated_create for details.
9090
*
91+
* Cloning secp256k1_context_static is not possible, and should not be emulated by
92+
* the caller (e.g., using memcpy). Create a new context instead.
93+
*
9194
* Returns: a newly created context object.
92-
* Args: ctx: an existing context to copy.
95+
* Args: ctx: an existing context to copy (not secp256k1_context_static).
9396
* In: prealloc: a pointer to a rewritable contiguous block of memory of
9497
* size at least secp256k1_context_preallocated_size(flags)
9598
* bytes, as detailed above.
@@ -117,7 +120,8 @@ SECP256K1_API secp256k1_context* secp256k1_context_preallocated_clone(
117120
*
118121
* Args: ctx: an existing context to destroy, constructed using
119122
* secp256k1_context_preallocated_create or
120-
* secp256k1_context_preallocated_clone.
123+
* secp256k1_context_preallocated_clone
124+
* (i.e., not secp256k1_context_static).
121125
*/
122126
SECP256K1_API void secp256k1_context_preallocated_destroy(
123127
secp256k1_context* ctx

src/secp256k1.c

+22-8
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ size_t secp256k1_context_preallocated_size(unsigned int flags) {
109109
}
110110

111111
size_t secp256k1_context_preallocated_clone_size(const secp256k1_context* ctx) {
112-
size_t ret = sizeof(secp256k1_context);
113112
VERIFY_CHECK(ctx != NULL);
114-
return ret;
113+
ARG_CHECK(secp256k1_context_is_proper(ctx));
114+
return sizeof(secp256k1_context);
115115
}
116116

117117
secp256k1_context* secp256k1_context_preallocated_create(void* prealloc, unsigned int flags) {
@@ -152,6 +152,7 @@ secp256k1_context* secp256k1_context_preallocated_clone(const secp256k1_context*
152152
secp256k1_context* ret;
153153
VERIFY_CHECK(ctx != NULL);
154154
ARG_CHECK(prealloc != NULL);
155+
ARG_CHECK(secp256k1_context_is_proper(ctx));
155156

156157
ret = (secp256k1_context*)prealloc;
157158
*ret = *ctx;
@@ -163,24 +164,35 @@ secp256k1_context* secp256k1_context_clone(const secp256k1_context* ctx) {
163164
size_t prealloc_size;
164165

165166
VERIFY_CHECK(ctx != NULL);
167+
ARG_CHECK(secp256k1_context_is_proper(ctx));
168+
166169
prealloc_size = secp256k1_context_preallocated_clone_size(ctx);
167170
ret = (secp256k1_context*)checked_malloc(&ctx->error_callback, prealloc_size);
168171
ret = secp256k1_context_preallocated_clone(ctx, ret);
169172
return ret;
170173
}
171174

172175
void secp256k1_context_preallocated_destroy(secp256k1_context* ctx) {
173-
ARG_CHECK_VOID(ctx != secp256k1_context_static);
174-
if (ctx != NULL) {
175-
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
176+
ARG_CHECK_VOID(ctx == NULL || secp256k1_context_is_proper(ctx));
177+
178+
/* Defined as noop */
179+
if (ctx == NULL) {
180+
return;
176181
}
182+
183+
secp256k1_ecmult_gen_context_clear(&ctx->ecmult_gen_ctx);
177184
}
178185

179186
void secp256k1_context_destroy(secp256k1_context* ctx) {
180-
if (ctx != NULL) {
181-
secp256k1_context_preallocated_destroy(ctx);
182-
free(ctx);
187+
ARG_CHECK_VOID(ctx == NULL || secp256k1_context_is_proper(ctx));
188+
189+
/* Defined as noop */
190+
if (ctx == NULL) {
191+
return;
183192
}
193+
194+
secp256k1_context_preallocated_destroy(ctx);
195+
free(ctx);
184196
}
185197

186198
void secp256k1_context_set_illegal_callback(secp256k1_context* ctx, void (*fun)(const char* message, void* data), const void* data) {
@@ -737,6 +749,8 @@ int secp256k1_ec_pubkey_tweak_mul(const secp256k1_context* ctx, secp256k1_pubkey
737749

738750
int secp256k1_context_randomize(secp256k1_context* ctx, const unsigned char *seed32) {
739751
VERIFY_CHECK(ctx != NULL);
752+
ARG_CHECK(secp256k1_context_is_proper(ctx));
753+
740754
if (secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)) {
741755
secp256k1_ecmult_gen_blind(&ctx->ecmult_gen_ctx, seed32);
742756
}

src/tests.c

+112-20
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,43 @@ static int COUNT = 64;
3232
static secp256k1_context *CTX = NULL;
3333
static secp256k1_context *STATIC_CTX = NULL;
3434

35+
static int all_bytes_equal(const void* s, unsigned char value, size_t n) {
36+
const unsigned char *p = s;
37+
size_t i;
38+
39+
for (i = 0; i < n; i++) {
40+
if (p[i] != value) {
41+
return 0;
42+
}
43+
}
44+
return 1;
45+
}
46+
47+
/* TODO Use CHECK_ILLEGAL(_VOID) everywhere and get rid of the uncounting callback */
48+
/* CHECK that expr_or_stmt calls the illegal callback of ctx exactly once
49+
*
50+
* For checking functions that use ARG_CHECK_VOID */
51+
#define CHECK_ILLEGAL_VOID(ctx, expr_or_stmt) do { \
52+
int32_t _calls_to_illegal_callback = 0; \
53+
secp256k1_callback _saved_illegal_cb = ctx->illegal_callback; \
54+
secp256k1_context_set_illegal_callback(ctx, \
55+
counting_illegal_callback_fn, &_calls_to_illegal_callback); \
56+
{ expr_or_stmt; } \
57+
ctx->illegal_callback = _saved_illegal_cb; \
58+
CHECK(_calls_to_illegal_callback == 1); \
59+
} while(0);
60+
61+
/* CHECK that expr calls the illegal callback of ctx exactly once and that expr == 0
62+
*
63+
* For checking functions that use ARG_CHECK */
64+
#define CHECK_ILLEGAL(ctx, expr) CHECK_ILLEGAL_VOID(ctx, CHECK((expr) == 0))
65+
3566
static void counting_illegal_callback_fn(const char* str, void* data) {
3667
/* Dummy callback function that just counts. */
3768
int32_t *p;
3869
(void)str;
3970
p = data;
71+
CHECK(*p != INT32_MAX);
4072
(*p)++;
4173
}
4274

@@ -45,6 +77,7 @@ static void uncounting_illegal_callback_fn(const char* str, void* data) {
4577
int32_t *p;
4678
(void)str;
4779
p = data;
80+
CHECK(*p != INT32_MIN);
4881
(*p)--;
4982
}
5083

@@ -229,31 +262,61 @@ static void run_ec_illegal_argument_tests(void) {
229262
secp256k1_context_set_illegal_callback(CTX, NULL, NULL);
230263
}
231264

232-
static void run_static_context_tests(void) {
233-
int32_t dummy = 0;
234-
265+
static void run_static_context_tests(int use_prealloc) {
235266
/* Check that deprecated secp256k1_context_no_precomp is an alias to secp256k1_context_static. */
236267
CHECK(secp256k1_context_no_precomp == secp256k1_context_static);
237268

238-
/* check if sizes for cloning are consistent */
239-
CHECK(secp256k1_context_preallocated_clone_size(STATIC_CTX) >= sizeof(secp256k1_context));
269+
{
270+
unsigned char seed[32] = {0x17};
240271

241-
/* Verify that setting and resetting illegal callback works */
242-
secp256k1_context_set_illegal_callback(STATIC_CTX, counting_illegal_callback_fn, &dummy);
243-
CHECK(STATIC_CTX->illegal_callback.fn == counting_illegal_callback_fn);
244-
secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL);
245-
CHECK(STATIC_CTX->illegal_callback.fn == secp256k1_default_illegal_callback_fn);
272+
/* Randomizing secp256k1_context_static is not supported. */
273+
CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_randomize(STATIC_CTX, seed));
274+
CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_randomize(STATIC_CTX, NULL));
275+
276+
/* Destroying or cloning secp256k1_context_static is not supported. */
277+
if (use_prealloc) {
278+
CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_preallocated_clone_size(STATIC_CTX));
279+
{
280+
secp256k1_context *my_static_ctx = malloc(sizeof(*STATIC_CTX));
281+
CHECK(my_static_ctx != NULL);
282+
memset(my_static_ctx, 0x2a, sizeof(*my_static_ctx));
283+
CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_preallocated_clone(STATIC_CTX, my_static_ctx));
284+
CHECK(all_bytes_equal(my_static_ctx, 0x2a, sizeof(*my_static_ctx)));
285+
free(my_static_ctx);
286+
}
287+
CHECK_ILLEGAL_VOID(STATIC_CTX, secp256k1_context_preallocated_destroy(STATIC_CTX));
288+
} else {
289+
CHECK_ILLEGAL(STATIC_CTX, secp256k1_context_clone(STATIC_CTX));
290+
CHECK_ILLEGAL_VOID(STATIC_CTX, secp256k1_context_destroy(STATIC_CTX));
291+
}
292+
}
293+
294+
{
295+
/* Verify that setting and resetting illegal callback works */
296+
int32_t dummy = 0;
297+
secp256k1_context_set_illegal_callback(STATIC_CTX, counting_illegal_callback_fn, &dummy);
298+
CHECK(STATIC_CTX->illegal_callback.fn == counting_illegal_callback_fn);
299+
CHECK(STATIC_CTX->illegal_callback.data == &dummy);
300+
secp256k1_context_set_illegal_callback(STATIC_CTX, NULL, NULL);
301+
CHECK(STATIC_CTX->illegal_callback.fn == secp256k1_default_illegal_callback_fn);
302+
CHECK(STATIC_CTX->illegal_callback.data == NULL);
303+
}
246304
}
247305

248306
static void run_proper_context_tests(int use_prealloc) {
249307
int32_t dummy = 0;
250-
secp256k1_context *my_ctx;
308+
secp256k1_context *my_ctx, *my_ctx_fresh;
251309
void *my_ctx_prealloc = NULL;
310+
unsigned char seed[32] = {0x17};
252311

253312
secp256k1_gej pubj;
254313
secp256k1_ge pub;
255314
secp256k1_scalar msg, key, nonce;
256315
secp256k1_scalar sigr, sigs;
316+
317+
/* Fresh reference context for comparison */
318+
my_ctx_fresh = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
319+
257320
if (use_prealloc) {
258321
my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
259322
CHECK(my_ctx_prealloc != NULL);
@@ -262,6 +325,13 @@ static void run_proper_context_tests(int use_prealloc) {
262325
my_ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
263326
}
264327

328+
/* Randomize and reset randomization */
329+
CHECK(context_eq(my_ctx, my_ctx_fresh));
330+
CHECK(secp256k1_context_randomize(my_ctx, seed) == 1);
331+
CHECK(!context_eq(my_ctx, my_ctx_fresh));
332+
CHECK(secp256k1_context_randomize(my_ctx, NULL) == 1);
333+
CHECK(context_eq(my_ctx, my_ctx_fresh));
334+
265335
/* set error callback (to a function that still aborts in case malloc() fails in secp256k1_context_clone() below) */
266336
secp256k1_context_set_error_callback(my_ctx, secp256k1_default_illegal_callback_fn, NULL);
267337
CHECK(my_ctx->error_callback.fn != secp256k1_default_error_callback_fn);
@@ -276,16 +346,33 @@ static void run_proper_context_tests(int use_prealloc) {
276346

277347
if (use_prealloc) {
278348
/* clone into a non-preallocated context and then again into a new preallocated one. */
279-
ctx_tmp = my_ctx; my_ctx = secp256k1_context_clone(my_ctx); secp256k1_context_preallocated_destroy(ctx_tmp);
280-
free(my_ctx_prealloc); my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(my_ctx_prealloc != NULL);
281-
ctx_tmp = my_ctx; my_ctx = secp256k1_context_preallocated_clone(my_ctx, my_ctx_prealloc); secp256k1_context_destroy(ctx_tmp);
349+
ctx_tmp = my_ctx;
350+
my_ctx = secp256k1_context_clone(my_ctx);
351+
CHECK(context_eq(ctx_tmp, my_ctx));
352+
secp256k1_context_preallocated_destroy(ctx_tmp);
353+
354+
free(my_ctx_prealloc);
355+
my_ctx_prealloc = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
356+
CHECK(my_ctx_prealloc != NULL);
357+
ctx_tmp = my_ctx;
358+
my_ctx = secp256k1_context_preallocated_clone(my_ctx, my_ctx_prealloc);
359+
CHECK(context_eq(ctx_tmp, my_ctx));
360+
secp256k1_context_destroy(ctx_tmp);
282361
} else {
283362
/* clone into a preallocated context and then again into a new non-preallocated one. */
284363
void *prealloc_tmp;
285364

286-
prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE)); CHECK(prealloc_tmp != NULL);
287-
ctx_tmp = my_ctx; my_ctx = secp256k1_context_preallocated_clone(my_ctx, prealloc_tmp); secp256k1_context_destroy(ctx_tmp);
288-
ctx_tmp = my_ctx; my_ctx = secp256k1_context_clone(my_ctx); secp256k1_context_preallocated_destroy(ctx_tmp);
365+
prealloc_tmp = malloc(secp256k1_context_preallocated_size(SECP256K1_CONTEXT_NONE));
366+
CHECK(prealloc_tmp != NULL);
367+
ctx_tmp = my_ctx;
368+
my_ctx = secp256k1_context_preallocated_clone(my_ctx, prealloc_tmp);
369+
CHECK(context_eq(ctx_tmp, my_ctx));
370+
secp256k1_context_destroy(ctx_tmp);
371+
372+
ctx_tmp = my_ctx;
373+
my_ctx = secp256k1_context_clone(my_ctx);
374+
CHECK(context_eq(ctx_tmp, my_ctx));
375+
secp256k1_context_preallocated_destroy(ctx_tmp);
289376
free(prealloc_tmp);
290377
}
291378
}
@@ -296,12 +383,16 @@ static void run_proper_context_tests(int use_prealloc) {
296383
/* And that it resets back to default. */
297384
secp256k1_context_set_error_callback(my_ctx, NULL, NULL);
298385
CHECK(my_ctx->error_callback.fn == secp256k1_default_error_callback_fn);
386+
CHECK(context_eq(my_ctx, my_ctx_fresh));
299387

300388
/* Verify that setting and resetting illegal callback works */
301389
secp256k1_context_set_illegal_callback(my_ctx, counting_illegal_callback_fn, &dummy);
302390
CHECK(my_ctx->illegal_callback.fn == counting_illegal_callback_fn);
391+
CHECK(my_ctx->illegal_callback.data == &dummy);
303392
secp256k1_context_set_illegal_callback(my_ctx, NULL, NULL);
304393
CHECK(my_ctx->illegal_callback.fn == secp256k1_default_illegal_callback_fn);
394+
CHECK(my_ctx->illegal_callback.data == NULL);
395+
CHECK(context_eq(my_ctx, my_ctx_fresh));
305396

306397
/*** attempt to use them ***/
307398
random_scalar_order_test(&msg);
@@ -327,6 +418,8 @@ static void run_proper_context_tests(int use_prealloc) {
327418
} else {
328419
secp256k1_context_destroy(my_ctx);
329420
}
421+
secp256k1_context_destroy(my_ctx_fresh);
422+
330423
/* Defined as no-op. */
331424
secp256k1_context_destroy(NULL);
332425
secp256k1_context_preallocated_destroy(NULL);
@@ -7389,9 +7482,8 @@ int main(int argc, char **argv) {
73897482
run_selftest_tests();
73907483

73917484
/* context tests */
7392-
run_proper_context_tests(0);
7393-
run_proper_context_tests(1);
7394-
run_static_context_tests();
7485+
run_proper_context_tests(0); run_proper_context_tests(1);
7486+
run_static_context_tests(0); run_static_context_tests(1);
73957487
run_deprecated_context_flags_test();
73967488

73977489
/* scratch tests */

0 commit comments

Comments
 (0)