mirror of
https://github.com/status-im/secp256k1.git
synced 2025-02-23 11:18:15 +00:00
extrakeys: Add keypair struct with create, pub and pub_xonly
This commit is contained in:
parent
f0010349b8
commit
58254463f9
@ -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.
|
||||
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -17,6 +17,10 @@
|
||||
# include "include/secp256k1_recovery.h"
|
||||
#endif
|
||||
|
||||
#if ENABLE_MODULE_EXTRAKEYS
|
||||
# include "include/secp256k1_extrakeys.h"
|
||||
#endif
|
||||
|
||||
int main(void) {
|
||||
secp256k1_context* ctx;
|
||||
secp256k1_ecdsa_signature signature;
|
||||
@ -33,6 +37,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");
|
||||
@ -115,6 +122,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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user