Skip to content

Commit 16cd02b

Browse files
committed
Add API to override SHA256 transform at runtime
This introduces `secp256k1_context_set_sha256_transform_callback()`, which allows users to provide their own SHA256 block-compression function at runtime. This is useful in setups where the optimal implementation is detected dynamically, where rebuilding the library is not possible, or when the compression function is not written in bare C89. The callback is installed on the `secp256k1_context` and is then used by all operations that compute SHA256 hashes. As part of the setup, the library performs sanity checks to ensure that the supplied function is equivalent to the default transform. Passing NULL to the callback setter restores the built-in implementation.
1 parent 97fb41e commit 16cd02b

26 files changed

+393
-208
lines changed

include/secp256k1.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ extern "C" {
66
#endif
77

88
#include <stddef.h>
9+
#include <stdint.h>
910

1011
/** Unless explicitly stated all pointer arguments must not be NULL.
1112
*
@@ -92,6 +93,7 @@ typedef struct secp256k1_ecdsa_signature {
9293
* the message, the algorithm, the key and the attempt.
9394
*/
9495
typedef int (*secp256k1_nonce_function)(
96+
const secp256k1_context *ctx,
9597
unsigned char *nonce32,
9698
const unsigned char *msg32,
9799
const unsigned char *key32,
@@ -403,6 +405,29 @@ SECP256K1_API void secp256k1_context_set_error_callback(
403405
const void *data
404406
) SECP256K1_ARG_NONNULL(1);
405407

408+
/**
409+
* Set a callback function to override the internal SHA-256 transform.
410+
*
411+
* This installs a function to replace the built-in block-compression
412+
* step used by the library's internal SHA-256 implementation.
413+
* The provided callback must be functionally identical (bit-for-bit)
414+
* to the default transform. Any deviation will cause incorrect results
415+
* and undefined behaviour.
416+
*
417+
* This API exists to support environments that wish to route the
418+
* SHA-256 compression step through a hardware-accelerated or otherwise
419+
* specialized implementation. It is not meant for modifying the
420+
* semantics of SHA-256.
421+
*
422+
* Args: ctx: pointer to a context object.
423+
* In: callback: pointer to a function implementing the transform step.
424+
* (passing NULL restores the default implementation)
425+
*/
426+
SECP256K1_API void secp256k1_context_set_sha256_transform_callback(
427+
secp256k1_context *ctx,
428+
void (*sha256_transform_callback)(uint32_t *state, const unsigned char *block, size_t rounds)
429+
) SECP256K1_ARG_NONNULL(1);
430+
406431
/** Parse a variable-length public key into the pubkey object.
407432
*
408433
* Returns: 1 if the public key was fully valid.

include/secp256k1_ecdh.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ extern "C" {
1919
* data: arbitrary data pointer that is passed through
2020
*/
2121
typedef int (*secp256k1_ecdh_hash_function)(
22+
const secp256k1_context *ctx,
2223
unsigned char *output,
2324
const unsigned char *x32,
2425
const unsigned char *y32,

include/secp256k1_ellswift.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extern "C" {
6262
* data: arbitrary data pointer that is passed through
6363
*/
6464
typedef int (*secp256k1_ellswift_xdh_hash_function)(
65+
const secp256k1_context *ctx,
6566
unsigned char *output,
6667
const unsigned char *x32,
6768
const unsigned char *ell_a64,

include/secp256k1_schnorrsig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ extern "C" {
3939
* the message, the key, the pubkey, the algorithm description, and data.
4040
*/
4141
typedef int (*secp256k1_nonce_function_hardened)(
42+
const secp256k1_context *ctx,
4243
unsigned char *nonce32,
4344
const unsigned char *msg,
4445
size_t msglen,

src/bench_ecmult.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ static void generate_scalar(uint32_t num, secp256k1_scalar* scalar) {
265265
c[7] = num >> 8;
266266
c[8] = num >> 16;
267267
c[9] = num >> 24;
268-
secp256k1_sha256_initialize(&sha256);
268+
secp256k1_sha256_initialize(&sha256, /*fn_transform=*/NULL);
269269
secp256k1_sha256_write(&sha256, c, sizeof(c));
270270
secp256k1_sha256_finalize(&sha256, buf);
271271
secp256k1_scalar_set_b32(scalar, buf, &overflow);

src/bench_internal.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ static void help(int default_iters) {
3838
}
3939

4040
typedef struct {
41+
const secp256k1_context* ctx;
4142
secp256k1_scalar scalar[2];
4243
secp256k1_fe fe[4];
4344
secp256k1_ge ge[2];
@@ -82,6 +83,9 @@ static void bench_setup(void* arg) {
8283
}
8384
};
8485

86+
/* Customize context if needed */
87+
data->ctx = secp256k1_context_static;
88+
8589
secp256k1_scalar_set_b32(&data->scalar[0], init[0], NULL);
8690
secp256k1_scalar_set_b32(&data->scalar[1], init[1], NULL);
8791
secp256k1_fe_set_b32_limit(&data->fe[0], init[0]);
@@ -346,7 +350,7 @@ static void bench_sha256(void* arg, int iters) {
346350
secp256k1_sha256 sha;
347351

348352
for (i = 0; i < iters; i++) {
349-
secp256k1_sha256_initialize(&sha);
353+
secp256k1_sha256_initialize(&sha, data->ctx->hash_context.fn_sha256_transform);
350354
secp256k1_sha256_write(&sha, data->data, 32);
351355
secp256k1_sha256_finalize(&sha, data->data);
352356
}
@@ -358,7 +362,7 @@ static void bench_hmac_sha256(void* arg, int iters) {
358362
secp256k1_hmac_sha256 hmac;
359363

360364
for (i = 0; i < iters; i++) {
361-
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32);
365+
secp256k1_hmac_sha256_initialize(&hmac, data->data, 32, data->ctx->hash_context.fn_sha256_transform);
362366
secp256k1_hmac_sha256_write(&hmac, data->data, 32);
363367
secp256k1_hmac_sha256_finalize(&hmac, data->data);
364368
}
@@ -370,8 +374,8 @@ static void bench_rfc6979_hmac_sha256(void* arg, int iters) {
370374
secp256k1_rfc6979_hmac_sha256 rng;
371375

372376
for (i = 0; i < iters; i++) {
373-
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64);
374-
secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32);
377+
secp256k1_rfc6979_hmac_sha256_initialize(&rng, data->data, 64, data->ctx->hash_context.fn_sha256_transform);
378+
secp256k1_rfc6979_hmac_sha256_generate(&rng, data->data, 32, data->ctx->hash_context.fn_sha256_transform);
375379
}
376380
}
377381

src/ecmult_gen_impl.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,9 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
291291
secp256k1_rfc6979_hmac_sha256 rng;
292292
unsigned char keydata[64];
293293

294+
/* future: use context callback */
295+
const sha256_transform_callback fn_sha256_transform = NULL;
296+
294297
/* Compute the (2^COMB_BITS - 1)/2 term once. */
295298
secp256k1_ecmult_gen_scalar_diff(&diff);
296299

@@ -309,17 +312,17 @@ static void secp256k1_ecmult_gen_blind(secp256k1_ecmult_gen_context *ctx, const
309312
*/
310313
VERIFY_CHECK(seed32 != NULL);
311314
memcpy(keydata + 32, seed32, 32);
312-
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64);
315+
secp256k1_rfc6979_hmac_sha256_initialize(&rng, keydata, 64, fn_sha256_transform);
313316
secp256k1_memclear_explicit(keydata, sizeof(keydata));
314317

315318
/* Compute projective blinding factor (cannot be 0). */
316-
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
319+
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32, fn_sha256_transform);
317320
secp256k1_fe_set_b32_mod(&f, nonce32);
318321
secp256k1_fe_cmov(&f, &secp256k1_fe_one, secp256k1_fe_normalizes_to_zero(&f));
319322
ctx->proj_blind = f;
320323

321324
/* For a random blinding value b, set scalar_offset=diff-b, ge_offset=bG */
322-
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32);
325+
secp256k1_rfc6979_hmac_sha256_generate(&rng, nonce32, 32, fn_sha256_transform);
323326
secp256k1_scalar_set_b32(&b, nonce32, NULL);
324327
/* The blinding value cannot be zero, as that would mean ge_offset = infinity,
325328
* which secp256k1_gej_add_ge cannot handle. */

src/hash.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,20 @@
1010
#include <stdlib.h>
1111
#include <stdint.h>
1212

13+
typedef void (*sha256_transform_callback)(uint32_t *state, const unsigned char *block, size_t rounds);
14+
15+
/* Validate user-supplied SHA-256 transform by comparing its output against
16+
* the library's linked implementation */
17+
static int secp256k1_sha256_check_transform(sha256_transform_callback fn_transform);
18+
1319
typedef struct {
1420
uint32_t s[8];
1521
unsigned char buf[64];
1622
uint64_t bytes;
23+
sha256_transform_callback fn_transform;
1724
} secp256k1_sha256;
1825

19-
static void secp256k1_sha256_initialize(secp256k1_sha256 *hash);
26+
static void secp256k1_sha256_initialize(secp256k1_sha256 *hash, sha256_transform_callback fn_transform);
2027
static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t size);
2128
static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out32);
2229
static void secp256k1_sha256_clear(secp256k1_sha256 *hash);
@@ -25,7 +32,7 @@ typedef struct {
2532
secp256k1_sha256 inner, outer;
2633
} secp256k1_hmac_sha256;
2734

28-
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size);
35+
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t size, sha256_transform_callback fn_sha256_transform);
2936
static void secp256k1_hmac_sha256_write(secp256k1_hmac_sha256 *hash, const unsigned char *data, size_t size);
3037
static void secp256k1_hmac_sha256_finalize(secp256k1_hmac_sha256 *hash, unsigned char *out32);
3138
static void secp256k1_hmac_sha256_clear(secp256k1_hmac_sha256 *hash);
@@ -36,8 +43,8 @@ typedef struct {
3643
int retry;
3744
} secp256k1_rfc6979_hmac_sha256;
3845

39-
static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen);
40-
static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen);
46+
static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen, sha256_transform_callback fn_sha256_transform);
47+
static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen, sha256_transform_callback fn_sha256_transform);
4148
static void secp256k1_rfc6979_hmac_sha256_finalize(secp256k1_rfc6979_hmac_sha256 *rng);
4249
static void secp256k1_rfc6979_hmac_sha256_clear(secp256k1_rfc6979_hmac_sha256 *rng);
4350

src/hash_impl.h

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include <stdint.h>
1717
#include <string.h>
1818

19-
static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) {
19+
static void secp256k1_sha256_initialize(secp256k1_sha256 *hash, sha256_transform_callback fn_transform) {
2020
hash->s[0] = 0x6a09e667ul;
2121
hash->s[1] = 0xbb67ae85ul;
2222
hash->s[2] = 0x3c6ef372ul;
@@ -26,6 +26,7 @@ static void secp256k1_sha256_initialize(secp256k1_sha256 *hash) {
2626
hash->s[6] = 0x1f83d9abul;
2727
hash->s[7] = 0x5be0cd19ul;
2828
hash->bytes = 0;
29+
hash->fn_transform = fn_transform == NULL ? secp256k1_sha256_transform : fn_transform;
2930
}
3031

3132
static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *data, size_t len) {
@@ -38,7 +39,7 @@ static void secp256k1_sha256_write(secp256k1_sha256 *hash, const unsigned char *
3839
memcpy(hash->buf + bufsize, data, chunk_len);
3940
data += chunk_len;
4041
len -= chunk_len;
41-
secp256k1_sha256_transform(hash->s, hash->buf, 1);
42+
hash->fn_transform(hash->s, hash->buf, 1);
4243
bufsize = 0;
4344
}
4445
if (len) {
@@ -63,15 +64,56 @@ static void secp256k1_sha256_finalize(secp256k1_sha256 *hash, unsigned char *out
6364
}
6465
}
6566

67+
static int secp256k1_sha256_check_transform(sha256_transform_callback fn_transform) {
68+
secp256k1_sha256 sha256;
69+
unsigned char out[2][32];
70+
71+
/* Four messages of different sizes: 1, 24, 64 and 81 bytes */
72+
unsigned char* msgs[4];
73+
size_t lens[4];
74+
unsigned char msg_0 = 0;
75+
unsigned char msg_1[24] = "secp256k1_verif_round_i";
76+
unsigned char msg_2[64] = "For this test, this 63-byte string will be used as input data i";
77+
unsigned char msg_3[81] = "Genesis: The Times 03/Jan/2009 Chancellor on brink of second bailout for banks i";
78+
msgs[0] = &msg_0; lens[0] = sizeof(msg_0);
79+
msgs[1] = msg_1; lens[1] = sizeof(msg_1);
80+
msgs[2] = msg_2; lens[2] = sizeof(msg_2);
81+
msgs[3] = msg_3; lens[3] = sizeof(msg_3);
82+
83+
/* Compare hashes between built-in transform vs the one provided by the user */
84+
{
85+
unsigned char i, j, k;
86+
sha256_transform_callback funcs[2];
87+
funcs[0] = secp256k1_sha256_transform; /* Built-in */
88+
funcs[1] = fn_transform; /* User provided */
89+
90+
for (i = 0; i < 10; i++) {
91+
msg_0 = i;
92+
msg_1[23] = i;
93+
msg_2[63] = i;
94+
msg_3[80] = i;
95+
for (j = 0; j < 4; j++) {
96+
for (k = 0; k < 2; k++) {
97+
secp256k1_sha256_initialize(&sha256, funcs[k]);
98+
secp256k1_sha256_write(&sha256, msgs[j], lens[j]);
99+
secp256k1_sha256_finalize(&sha256, out[k]);
100+
}
101+
if (memcmp(out[0], out[1], 32) != 0) return 0;
102+
}
103+
}
104+
}
105+
return 1;
106+
}
107+
66108
/* Initializes a sha256 struct and writes the 64 byte string
67109
* SHA256(tag)||SHA256(tag) into it. */
68-
static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen) {
110+
static void secp256k1_sha256_initialize_tagged(secp256k1_sha256 *hash, const unsigned char *tag, size_t taglen, sha256_transform_callback fn_sha256_transform) {
69111
unsigned char buf[32];
70-
secp256k1_sha256_initialize(hash);
112+
secp256k1_sha256_initialize(hash, fn_sha256_transform);
71113
secp256k1_sha256_write(hash, tag, taglen);
72114
secp256k1_sha256_finalize(hash, buf);
73115

74-
secp256k1_sha256_initialize(hash);
116+
secp256k1_sha256_initialize(hash, fn_sha256_transform);
75117
secp256k1_sha256_write(hash, buf, 32);
76118
secp256k1_sha256_write(hash, buf, 32);
77119
}
@@ -80,27 +122,27 @@ static void secp256k1_sha256_clear(secp256k1_sha256 *hash) {
80122
secp256k1_memclear_explicit(hash, sizeof(*hash));
81123
}
82124

83-
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen) {
125+
static void secp256k1_hmac_sha256_initialize(secp256k1_hmac_sha256 *hash, const unsigned char *key, size_t keylen, sha256_transform_callback fn_sha256_transform) {
84126
size_t n;
85127
unsigned char rkey[64];
86128
if (keylen <= sizeof(rkey)) {
87129
memcpy(rkey, key, keylen);
88130
memset(rkey + keylen, 0, sizeof(rkey) - keylen);
89131
} else {
90132
secp256k1_sha256 sha256;
91-
secp256k1_sha256_initialize(&sha256);
133+
secp256k1_sha256_initialize(&sha256, fn_sha256_transform);
92134
secp256k1_sha256_write(&sha256, key, keylen);
93135
secp256k1_sha256_finalize(&sha256, rkey);
94136
memset(rkey + 32, 0, 32);
95137
}
96138

97-
secp256k1_sha256_initialize(&hash->outer);
139+
secp256k1_sha256_initialize(&hash->outer, fn_sha256_transform);
98140
for (n = 0; n < sizeof(rkey); n++) {
99141
rkey[n] ^= 0x5c;
100142
}
101143
secp256k1_sha256_write(&hash->outer, rkey, sizeof(rkey));
102144

103-
secp256k1_sha256_initialize(&hash->inner);
145+
secp256k1_sha256_initialize(&hash->inner, fn_sha256_transform);
104146
for (n = 0; n < sizeof(rkey); n++) {
105147
rkey[n] ^= 0x5c ^ 0x36;
106148
}
@@ -124,7 +166,7 @@ static void secp256k1_hmac_sha256_clear(secp256k1_hmac_sha256 *hash) {
124166
secp256k1_memclear_explicit(hash, sizeof(*hash));
125167
}
126168

127-
static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen) {
169+
static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha256 *rng, const unsigned char *key, size_t keylen, sha256_transform_callback fn_sha256_transform) {
128170
secp256k1_hmac_sha256 hmac;
129171
static const unsigned char zero[1] = {0x00};
130172
static const unsigned char one[1] = {0x01};
@@ -133,45 +175,45 @@ static void secp256k1_rfc6979_hmac_sha256_initialize(secp256k1_rfc6979_hmac_sha2
133175
memset(rng->k, 0x00, 32); /* RFC6979 3.2.c. */
134176

135177
/* RFC6979 3.2.d. */
136-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
178+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
137179
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
138180
secp256k1_hmac_sha256_write(&hmac, zero, 1);
139181
secp256k1_hmac_sha256_write(&hmac, key, keylen);
140182
secp256k1_hmac_sha256_finalize(&hmac, rng->k);
141-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
183+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
142184
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
143185
secp256k1_hmac_sha256_finalize(&hmac, rng->v);
144186

145187
/* RFC6979 3.2.f. */
146-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
188+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
147189
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
148190
secp256k1_hmac_sha256_write(&hmac, one, 1);
149191
secp256k1_hmac_sha256_write(&hmac, key, keylen);
150192
secp256k1_hmac_sha256_finalize(&hmac, rng->k);
151-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
193+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
152194
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
153195
secp256k1_hmac_sha256_finalize(&hmac, rng->v);
154196
rng->retry = 0;
155197
}
156198

157-
static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen) {
199+
static void secp256k1_rfc6979_hmac_sha256_generate(secp256k1_rfc6979_hmac_sha256 *rng, unsigned char *out, size_t outlen, sha256_transform_callback fn_sha256_transform) {
158200
/* RFC6979 3.2.h. */
159201
static const unsigned char zero[1] = {0x00};
160202
if (rng->retry) {
161203
secp256k1_hmac_sha256 hmac;
162-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
204+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
163205
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
164206
secp256k1_hmac_sha256_write(&hmac, zero, 1);
165207
secp256k1_hmac_sha256_finalize(&hmac, rng->k);
166-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
208+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
167209
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
168210
secp256k1_hmac_sha256_finalize(&hmac, rng->v);
169211
}
170212

171213
while (outlen > 0) {
172214
secp256k1_hmac_sha256 hmac;
173215
size_t now = outlen;
174-
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32);
216+
secp256k1_hmac_sha256_initialize(&hmac, rng->k, 32, fn_sha256_transform);
175217
secp256k1_hmac_sha256_write(&hmac, rng->v, 32);
176218
secp256k1_hmac_sha256_finalize(&hmac, rng->v);
177219
if (now > 32) {

0 commit comments

Comments
 (0)