Skip to content

Commit

Permalink
extrakeys: Add keypair_xonly_tweak_add
Browse files Browse the repository at this point in the history
Summary:
This is a partial backport of secp256k1 [[bitcoin-core/secp256k1#558 | PR558]] : bitcoin-core/secp256k1@6fcb5b8

Depends on D7643

Test Plan:
  cmake -GNinja .. -DSECP256K1_ENABLE_MODULE_EXTRAKEYS=On
  ninja check-secp256k1

Reviewers: #bitcoin_abc, Fabien

Reviewed By: #bitcoin_abc, Fabien

Differential Revision: https://reviews.bitcoinabc.org/D7644
  • Loading branch information
jonasnick authored and deadalnix committed Sep 29, 2020
1 parent c341d26 commit d796ee6
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 1 deletion.
27 changes: 27 additions & 0 deletions include/secp256k1_extrakeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,33 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);

/** Tweak a keypair by adding tweak32 to the secret key and updating the public
* key accordingly.
*
* Calling this function and then secp256k1_keypair_pub results in the same
* public key as calling secp256k1_keypair_xonly_pub and then
* secp256k1_xonly_pubkey_tweak_add.
*
* Returns: 0 if the arguments are invalid or the resulting keypair would be
* invalid (only when the tweak is the negation of the keypair's
* secret key). 1 otherwise.
*
* Args: ctx: pointer to a context object initialized for verification
* (cannot be NULL)
* In/Out: keypair: pointer to a keypair to apply the tweak to. Will be set to
* an invalid value if this function returns 0 (cannot be
* NULL).
* In: tweak32: pointer to a 32-byte tweak. If the tweak is invalid according
* to secp256k1_ec_seckey_verify, this function returns 0. For
* uniformly random 32-byte arrays the chance of being invalid
* is negligible (around 1 in 2^128) (cannot be NULL).
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add(
const secp256k1_context* ctx,
secp256k1_keypair *keypair,
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

#ifdef __cplusplus
}
#endif
Expand Down
31 changes: 31 additions & 0 deletions src/modules/extrakeys/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,4 +214,35 @@ int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pu
return 1;
}

int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *tweak32) {
secp256k1_ge pk;
secp256k1_scalar sk;
int y_parity;
int ret;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx));
ARG_CHECK(keypair != NULL);
ARG_CHECK(tweak32 != NULL);

ret = secp256k1_keypair_load(ctx, &sk, &pk, keypair);
memset(keypair, 0, sizeof(*keypair));

y_parity = secp256k1_extrakeys_ge_even_y(&pk);
if (y_parity == 1) {
secp256k1_scalar_negate(&sk, &sk);
}

ret &= secp256k1_ec_seckey_tweak_add_helper(&sk, tweak32);
ret &= secp256k1_ec_pubkey_tweak_add_helper(&ctx->ecmult_ctx, &pk, tweak32);

secp256k1_declassify(ctx, &ret, sizeof(ret));
if (ret) {
secp256k1_keypair_save(keypair, &sk, &pk);
}

secp256k1_scalar_clear(&sk);
return ret;
}

#endif
109 changes: 109 additions & 0 deletions src/modules/extrakeys/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,114 @@ void test_keypair(void) {
secp256k1_context_destroy(verify);
}

void test_keypair_add(void) {
unsigned char sk[32];
secp256k1_keypair keypair;
unsigned char overflows[32];
unsigned char zeros96[96] = { 0 };
unsigned char tweak[32];
int i;
int ecount = 0;
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);

CHECK(sizeof(zeros96) == sizeof(keypair));
secp256k1_rand256(sk);
secp256k1_rand256(tweak);
memset(overflows, 0xFF, 32);
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);

CHECK(secp256k1_keypair_xonly_tweak_add(none, &keypair, tweak) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_keypair_xonly_tweak_add(sign, &keypair, tweak) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 1);
CHECK(secp256k1_keypair_xonly_tweak_add(verify, NULL, tweak) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, NULL) == 0);
CHECK(ecount == 4);
/* This does not set the keypair to zeroes */
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) != 0);

/* Invalid tweak zeroes the keypair */
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, overflows) == 0);
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0);

/* A zero tweak is fine */
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, zeros96) == 1);

/* Fails if the resulting keypair was (sk=0, pk=infinity) */
for (i = 0; i < count; i++) {
secp256k1_scalar scalar_tweak;
secp256k1_keypair keypair_tmp;
secp256k1_rand256(sk);
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
memcpy(&keypair_tmp, &keypair, sizeof(keypair));
/* Because sk may be negated before adding, we need to try with tweak =
* sk as well as tweak = -sk. */
secp256k1_scalar_set_b32(&scalar_tweak, sk, NULL);
secp256k1_scalar_negate(&scalar_tweak, &scalar_tweak);
secp256k1_scalar_get_b32(tweak, &scalar_tweak);
CHECK((secp256k1_keypair_xonly_tweak_add(ctx, &keypair, sk) == 0)
|| (secp256k1_keypair_xonly_tweak_add(ctx, &keypair_tmp, tweak) == 0));
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0
|| memcmp(&keypair_tmp, zeros96, sizeof(keypair_tmp)) == 0);
}

/* Invalid keypair with a valid tweak */
memset(&keypair, 0, sizeof(keypair));
secp256k1_rand256(tweak);
ecount = 0;
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
CHECK(ecount == 1);
CHECK(memcmp(&keypair, zeros96, sizeof(keypair)) == 0);
/* Only seckey part of keypair invalid */
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
memset(&keypair, 0, 32);
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
CHECK(ecount == 2);
/* Only pubkey part of keypair invalid */
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
memset(&keypair.data[32], 0, 64);
CHECK(secp256k1_keypair_xonly_tweak_add(verify, &keypair, tweak) == 0);
CHECK(ecount == 3);

/* Check that the keypair_tweak_add implementation is correct */
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
for (i = 0; i < count; i++) {
secp256k1_xonly_pubkey internal_pk;
secp256k1_xonly_pubkey output_pk;
secp256k1_pubkey output_pk_xy;
secp256k1_pubkey output_pk_expected;
unsigned char pk32[32];
int pk_parity;

secp256k1_rand256(tweak);
CHECK(secp256k1_keypair_xonly_pub(ctx, &internal_pk, NULL, &keypair) == 1);
CHECK(secp256k1_keypair_xonly_tweak_add(ctx, &keypair, tweak) == 1);
CHECK(secp256k1_keypair_xonly_pub(ctx, &output_pk, &pk_parity, &keypair) == 1);

/* Check that it passes xonly_pubkey_tweak_add_check */
CHECK(secp256k1_xonly_pubkey_serialize(ctx, pk32, &output_pk) == 1);
CHECK(secp256k1_xonly_pubkey_tweak_add_check(ctx, pk32, pk_parity, &internal_pk, tweak) == 1);

/* Check that the resulting pubkey matches xonly_pubkey_tweak_add */
CHECK(secp256k1_keypair_pub(ctx, &output_pk_xy, &keypair) == 1);
CHECK(secp256k1_xonly_pubkey_tweak_add(ctx, &output_pk_expected, &internal_pk, tweak) == 1);
CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);

/* Check that the secret key in the keypair is tweaked correctly */
CHECK(secp256k1_ec_pubkey_create(ctx, &output_pk_expected, &keypair.data[0]) == 1);
CHECK(memcmp(&output_pk_xy, &output_pk_expected, sizeof(output_pk_xy)) == 0);
}
secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(verify);
}

void run_extrakeys_tests(void) {
/* xonly key test cases */
test_xonly_pubkey();
Expand All @@ -410,6 +518,7 @@ void run_extrakeys_tests(void) {

/* keypair tests */
test_keypair();
test_keypair_add();
}

#endif
11 changes: 10 additions & 1 deletion src/valgrind_ctime_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ int main(void) {
msg[i] = i + 1;
}

ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_DECLASSIFY);
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN
| SECP256K1_CONTEXT_VERIFY
| SECP256K1_CONTEXT_DECLASSIFY);

/* Test keygen. */
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
Expand Down Expand Up @@ -135,11 +137,18 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret);

/* Test keypair_create and keypair_xonly_tweak_add. */
#if ENABLE_MODULE_EXTRAKEYS
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
ret = secp256k1_keypair_create(ctx, &keypair, key);
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);

/* The tweak is not treated as a secret in keypair_tweak_add */
VALGRIND_MAKE_MEM_DEFINED(msg, 32);
ret = secp256k1_keypair_xonly_tweak_add(ctx, &keypair, msg);
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret == 1);
#endif

secp256k1_context_destroy(ctx);
Expand Down

0 comments on commit d796ee6

Please sign in to comment.