Skip to content

Commit

Permalink
extrakeys: Add keypair struct with create, pub and pub_xonly
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@5825446

Depends on D7641

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/D7643
  • Loading branch information
jonasnick authored and deadalnix committed Sep 29, 2020
1 parent ee00af3 commit 542c2ef
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 0 deletions.
62 changes: 62 additions & 0 deletions src/secp256k1/include/secp256k1_extrakeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ typedef struct {
unsigned char data[64];
} secp256k1_xonly_pubkey;

/** Opaque data structure that holds a keypair consisting of a secret and a
* public key.
*
* The exact representation of data inside is implementation defined and not
* guaranteed to be portable between different platforms or versions. It is
* however guaranteed to be 96 bytes in size, and can be safely copied/moved.
*/
typedef struct {
unsigned char data[96];
} secp256k1_keypair;

/** Parse a 32-byte sequence into a xonly_pubkey object.
*
* Returns: 1 if the public key was fully valid.
Expand Down Expand Up @@ -140,6 +151,57 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_xonly_pubkey_tweak_add_
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);

/** Compute the keypair for a secret key.
*
* Returns: 1: secret was valid, keypair is ready to use
* 0: secret was invalid, try again with a different secret
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
* Out: keypair: pointer to the created keypair (cannot be NULL)
* In: seckey: pointer to a 32-byte secret key (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
const secp256k1_context* ctx,
secp256k1_keypair *keypair,
const unsigned char *seckey
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Get the public key from a keypair.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
* the keypair public key. If not, it's set to an invalid value.
* (cannot be NULL)
* In: keypair: pointer to a keypair (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
const secp256k1_context* ctx,
secp256k1_pubkey *pubkey,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Get the x-only public key from a keypair.
*
* This is the same as calling secp256k1_keypair_pub and then
* secp256k1_xonly_pubkey_from_pubkey.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
* Args: ctx: pointer to a context object (cannot be NULL)
* Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
* to the keypair public key after converting it to an
* xonly_pubkey. If not, it's set to an invalid value (cannot be
* NULL).
* pk_parity: pointer to an integer that will be set to the pk_parity
* argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL).
* In: keypair: pointer to a keypair (cannot be NULL)
*/
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
const secp256k1_context* ctx,
secp256k1_xonly_pubkey *pubkey,
int *pk_parity,
const secp256k1_keypair *keypair
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);

#ifdef __cplusplus
}
#endif
Expand Down
89 changes: 89 additions & 0 deletions src/secp256k1/src/modules/extrakeys/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,93 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u
&& secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
}

static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) {
secp256k1_scalar_get_b32(&keypair->data[0], sk);
secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk);
}


static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) {
int ret;

ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]);
/* We can declassify ret here because sk is only zero if a keypair function
* failed (which zeroes the keypair) and its return value is ignored. */
secp256k1_declassify(ctx, &ret, sizeof(ret));
ARG_CHECK(ret);
return ret;
}

/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk
* and ARG_CHECKs that the keypair is not invalid. It always initializes sk and
* pk with dummy values. */
static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) {
int ret;
const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32];

/* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's
* invalid. */
secp256k1_declassify(ctx, pubkey, sizeof(*pubkey));
ret = secp256k1_pubkey_load(ctx, pk, pubkey);
if (sk != NULL) {
ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair);
}
if (!ret) {
*pk = secp256k1_ge_const_g;
if (sk != NULL) {
*sk = secp256k1_scalar_one;
}
}
return ret;
}

int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) {
secp256k1_scalar sk;
secp256k1_ge pk;
int ret = 0;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(keypair != NULL);
memset(keypair, 0, sizeof(*keypair));
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
ARG_CHECK(seckey32 != NULL);

ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
secp256k1_keypair_save(keypair, &sk, &pk);
memczero(keypair, sizeof(*keypair), !ret);

secp256k1_scalar_clear(&sk);
return ret;
}

int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
memset(pubkey, 0, sizeof(*pubkey));
ARG_CHECK(keypair != NULL);

memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey));
return 1;
}

int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) {
secp256k1_ge pk;
int tmp;

VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkey != NULL);
memset(pubkey, 0, sizeof(*pubkey));
ARG_CHECK(keypair != NULL);

if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) {
return 0;
}
tmp = secp256k1_extrakeys_ge_even_y(&pk);
if (pk_parity != NULL) {
*pk_parity = tmp;
}
secp256k1_xonly_pubkey_save(pubkey, &pk);

return 1;
}

#endif
95 changes: 95 additions & 0 deletions src/secp256k1/src/modules/extrakeys/tests_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -309,12 +309,107 @@ void test_xonly_pubkey_tweak_recursive(void) {
}
#undef N_PUBKEYS

void test_keypair(void) {
unsigned char sk[32];
unsigned char zeros96[96] = { 0 };
unsigned char overflows[32];
secp256k1_keypair keypair;
secp256k1_pubkey pk, pk_tmp;
secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
int pk_parity, pk_parity_tmp;
int ecount;
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));
memset(overflows, 0xFF, sizeof(overflows));

/* Test keypair_create */
ecount = 0;
secp256k1_rand256(sk);
CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0);
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0);
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
CHECK(ecount == 2);
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0);
CHECK(ecount == 3);
CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0);
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
CHECK(ecount == 4);

/* Invalid secret key */
CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0);
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0);
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);

/* Test keypair_pub */
ecount = 0;
secp256k1_rand256(sk);
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0);
CHECK(ecount == 2);
CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);

/* Using an invalid keypair is fine for keypair_pub */
memset(&keypair, 0, sizeof(keypair));
CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);

/* keypair holds the same pubkey as pubkey_create */
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1);
CHECK(memcmp(&pk, &pk_tmp, sizeof(pk)) == 0);

/** Test keypair_xonly_pub **/
ecount = 0;
secp256k1_rand256(sk);
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0);
CHECK(ecount == 1);
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1);
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0);
CHECK(ecount == 2);
CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
/* Using an invalid keypair will set the xonly_pk to 0 (first reset
* xonly_pk). */
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
memset(&keypair, 0, sizeof(keypair));
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0);
CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
CHECK(ecount == 3);

/** keypair holds the same xonly pubkey as pubkey_create **/
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1);
CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
CHECK(pk_parity == pk_parity_tmp);

secp256k1_context_destroy(none);
secp256k1_context_destroy(sign);
secp256k1_context_destroy(verify);
}

void run_extrakeys_tests(void) {
/* xonly key test cases */
test_xonly_pubkey();
test_xonly_pubkey_tweak();
test_xonly_pubkey_tweak_check();
test_xonly_pubkey_tweak_recursive();

/* keypair tests */
test_keypair();
}

#endif
14 changes: 14 additions & 0 deletions src/secp256k1/src/valgrind_ctime_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@
# include "include/secp256k1_schnorr.h"
#endif

#if ENABLE_MODULE_EXTRAKEYS
# include "include/secp256k1_extrakeys.h"
#endif

int main(void) {
secp256k1_context* ctx;
secp256k1_ecdsa_signature signature;
Expand All @@ -37,6 +41,9 @@ int main(void) {
secp256k1_ecdsa_recoverable_signature recoverable_signature;
int recid;
#endif
#if ENABLE_MODULE_EXTRAKEYS
secp256k1_keypair keypair;
#endif

if (!RUNNING_ON_VALGRIND) {
fprintf(stderr, "This test can only usefully be run inside valgrind.\n");
Expand Down Expand Up @@ -128,6 +135,13 @@ int main(void) {
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
CHECK(ret);

#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);
#endif

secp256k1_context_destroy(ctx);
return 0;
}

0 comments on commit 542c2ef

Please sign in to comment.