Skip to content

Commit 6fcb5b8

Browse files
committed
extrakeys: Add keypair_xonly_tweak_add
1 parent 5825446 commit 6fcb5b8

File tree

4 files changed

+177
-1
lines changed

4 files changed

+177
-1
lines changed

Diff for: include/secp256k1_extrakeys.h

+27
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,33 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
202202
const secp256k1_keypair *keypair
203203
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
204204

205+
/** Tweak a keypair by adding tweak32 to the secret key and updating the public
206+
* key accordingly.
207+
*
208+
* Calling this function and then secp256k1_keypair_pub results in the same
209+
* public key as calling secp256k1_keypair_xonly_pub and then
210+
* secp256k1_xonly_pubkey_tweak_add.
211+
*
212+
* Returns: 0 if the arguments are invalid or the resulting keypair would be
213+
* invalid (only when the tweak is the negation of the keypair's
214+
* secret key). 1 otherwise.
215+
*
216+
* Args: ctx: pointer to a context object initialized for verification
217+
* (cannot be NULL)
218+
* In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
219+
* an invalid value if this function returns 0 (cannot be
220+
* NULL).
221+
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according
222+
* to secp256k1_ec_seckey_verify, this function returns 0. For
223+
* uniformly random 32-byte arrays the chance of being invalid
224+
* is negligible (around 1 in 2^128) (cannot be NULL).
225+
*/
226+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add(
227+
const secp256k1_context* ctx,
228+
secp256k1_keypair *keypair,
229+
const unsigned char *tweak32
230+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
231+
205232
#ifdef __cplusplus
206233
}
207234
#endif

Diff for: src/modules/extrakeys/main_impl.h

+31
Original file line numberDiff line numberDiff line change
@@ -214,4 +214,35 @@ int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pu
214214
return 1;
215215
}
216216

217+
int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) {
218+
secp256k1_ge pk;
219+
secp256k1_scalar sk;
220+
int y_parity;
221+
int ret;
222+
223+
VERIFY_CHECK(ctx != NULL);
224+
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
225+
ARG_CHECK(keypair != NULL);
226+
ARG_CHECK(tweak32 != NULL);
227+
228+
ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair);
229+
memset(keypair, 0, sizeof(*keypair));
230+
231+
y_parity = secp256k1_extrakeys_ge_even_y(&pk);
232+
if (y_parity == 1) {
233+
secp256k1_scalar_negate(&sk, &sk);
234+
}
235+
236+
ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32);
237+
ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32);
238+
239+
secp256k1_declassify(ctx, &ret, sizeof(ret));
240+
if (ret) {
241+
secp256k1_keypair_save(keypair, &sk, &pk);
242+
}
243+
244+
secp256k1_scalar_clear(&sk);
245+
return ret;
246+
}
247+
217248
#endif

Diff for: src/modules/extrakeys/tests_impl.h

+109
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,114 @@ void test_keypair(void) {
401401
secp256k1_context_destroy(verify);
402402
}
403403

404+
void test_keypair_add(void) {
405+
unsigned char sk[32];
406+
secp256k1_keypair keypair;
407+
unsigned char overflows[32];
408+
unsigned char zeros96[96] = { 0 };
409+
unsigned char tweak[32];
410+
int i;
411+
int ecount = 0;
412+
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
413+
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
414+
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
415+
416+
CHECK(sizeof(zeros96) == sizeof(keypair));
417+
secp256k1_rand256(sk);
418+
secp256k1_rand256(tweak);
419+
memset(overflows, 0xFF, 32);
420+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
421+
422+
CHECK(secp256k1_keypair_xonly_tweak_add(none, &keypair, tweak) == 0);
423+
CHECK(ecount == 1);
424+
CHECK(secp256k1_keypair_xonly_tweak_add(sign, &keypair, tweak) == 0);
425+
CHECK(ecount == 2);
426+
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 1);
427+
CHECK(secp256k1_keypair_xonly_tweak_add(verify, NULL, tweak) == 0);
428+
CHECK(ecount == 3);
429+
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, NULL) == 0);
430+
CHECK(ecount == 4);
431+
/* This does not set the keypair to zeroes */
432+
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) != 0);
433+
434+
/* Invalid tweak zeroes the keypair */
435+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
436+
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0);
437+
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0);
438+
439+
/* A zero tweak is fine */
440+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
441+
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1);
442+
443+
/* Fails if the resulting keypair was (sk=0, pk=infinity) */
444+
for (i = 0; i < count; i++) {
445+
secp256k1_scalar scalar_tweak;
446+
secp256k1_keypair keypair_tmp;
447+
secp256k1_rand256(sk);
448+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
449+
memcpy(&keypair_tmp, &keypair, sizeof(keypair));
450+
/* Because sk may be negated before adding, we need to try with tweak =
451+
* sk as well as tweak = -sk. */
452+
secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
453+
secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
454+
secp256k1_scalar_get_b32(tweak, &scalar_tweak);
455+
CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0)
456+
|| (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0));
457+
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0
458+
|| memcmp(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0);
459+
}
460+
461+
/* Invalid keypair with a valid tweak */
462+
memset(&keypair, 0, sizeof(keypair));
463+
secp256k1_rand256(tweak);
464+
ecount = 0;
465+
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
466+
CHECK(ecount == 1);
467+
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0);
468+
/* Only seckey part of keypair invalid */
469+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
470+
memset(&keypair, 0, 32);
471+
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
472+
CHECK(ecount == 2);
473+
/* Only pubkey part of keypair invalid */
474+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
475+
memset(&keypair.data[32], 0, 64);
476+
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
477+
CHECK(ecount == 3);
478+
479+
/* Check that the keypair_tweak_add implementation is correct */
480+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
481+
for (i = 0; i < count; i++) {
482+
secp256k1_xonly_pubkey internal_pk;
483+
secp256k1_xonly_pubkey output_pk;
484+
secp256k1_pubkey output_pk_xy;
485+
secp256k1_pubkey output_pk_expected;
486+
unsigned char pk32[32];
487+
int pk_parity;
488+
489+
secp256k1_rand256(tweak);
490+
CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
491+
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
492+
CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);
493+
494+
/* Check that it passes xonly_pubkey_tweak_add_check */
495+
CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1);
496+
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1);
497+
498+
/* Check that the resulting pubkey matches xonly_pubkey_tweak_add */
499+
CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1);
500+
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1);
501+
CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
502+
503+
/* Check that the secret key in the keypair is tweaked correctly */
504+
CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
505+
CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
506+
}
507+
secp256k1_context_destroy(none);
508+
secp256k1_context_destroy(sign);
509+
secp256k1_context_destroy(verify);
510+
}
511+
404512
void run_extrakeys_tests(void) {
405513
/* xonly key test cases */
406514
test_xonly_pubkey();
@@ -410,6 +518,7 @@ void run_extrakeys_tests(void) {
410518

411519
/* keypair tests */
412520
test_keypair();
521+
test_keypair_add();
413522
}
414523

415524
#endif

Diff for: src/valgrind_ctime_test.c

+10-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ int main(void) {
5757
msg[i] = i + 1;
5858
}
5959

60-
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
60+
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
61+
| SECP256K1_CONTEXT_VERIFY
62+
| SECP256K1_CONTEXT_DECLASSIFY);
6163

6264
/* Test keygen. */
6365
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
@@ -122,11 +124,18 @@ int main(void) {
122124
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
123125
CHECK(ret);
124126

127+
/* Test keypair_create and keypair_xonly_tweak_add. */
125128
#if ENABLE_MODULE_EXTRAKEYS
126129
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
127130
ret = secp256k1_keypair_create(ctx, &keypair, key);
128131
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
129132
CHECK(ret == 1);
133+
134+
/* The tweak is not treated as a secret in keypair_tweak_add */
135+
VALGRIND_MAKE_MEM_DEFINED(msg, 32);
136+
ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
137+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
138+
CHECK(ret == 1);
130139
#endif
131140

132141
secp256k1_context_destroy(ctx);

0 commit comments

Comments
 (0)