Skip to content

Commit c341d26

Browse files
jonasnickdeadalnix
authored andcommitted
extrakeys: Add keypair struct with create, pub and pub_xonly
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
1 parent 1790105 commit c341d26

File tree

4 files changed

+260
-0
lines changed

4 files changed

+260
-0
lines changed

include/secp256k1_extrakeys.h

+62
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,17 @@ typedef struct {
2323
unsigned char data[64];
2424
} secp256k1_xonly_pubkey;
2525

26+
/** Opaque data structure that holds a keypair consisting of a secret and a
27+
* public key.
28+
*
29+
* The exact representation of data inside is implementation defined and not
30+
* guaranteed to be portable between different platforms or versions. It is
31+
* however guaranteed to be 96 bytes in size, and can be safely copied/moved.
32+
*/
33+
typedef struct {
34+
unsigned char data[96];
35+
} secp256k1_keypair;
36+
2637
/** Parse a 32-byte sequence into a xonly_pubkey object.
2738
*
2839
* 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_
140151
const unsigned char *tweak32
141152
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5);
142153

154+
/** Compute the keypair for a secret key.
155+
*
156+
* Returns: 1: secret was valid, keypair is ready to use
157+
* 0: secret was invalid, try again with a different secret
158+
* Args: ctx: pointer to a context object, initialized for signing (cannot be NULL)
159+
* Out: keypair: pointer to the created keypair (cannot be NULL)
160+
* In: seckey: pointer to a 32-byte secret key (cannot be NULL)
161+
*/
162+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_create(
163+
const secp256k1_context* ctx,
164+
secp256k1_keypair *keypair,
165+
const unsigned char *seckey
166+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
167+
168+
/** Get the public key from a keypair.
169+
*
170+
* Returns: 0 if the arguments are invalid. 1 otherwise.
171+
* Args: ctx: pointer to a context object (cannot be NULL)
172+
* Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to
173+
* the keypair public key. If not, it's set to an invalid value.
174+
* (cannot be NULL)
175+
* In: keypair: pointer to a keypair (cannot be NULL)
176+
*/
177+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_pub(
178+
const secp256k1_context* ctx,
179+
secp256k1_pubkey *pubkey,
180+
const secp256k1_keypair *keypair
181+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
182+
183+
/** Get the x-only public key from a keypair.
184+
*
185+
* This is the same as calling secp256k1_keypair_pub and then
186+
* secp256k1_xonly_pubkey_from_pubkey.
187+
*
188+
* Returns: 0 if the arguments are invalid. 1 otherwise.
189+
* Args: ctx: pointer to a context object (cannot be NULL)
190+
* Out: pubkey: pointer to an xonly_pubkey object. If 1 is returned, it is set
191+
* to the keypair public key after converting it to an
192+
* xonly_pubkey. If not, it's set to an invalid value (cannot be
193+
* NULL).
194+
* pk_parity: pointer to an integer that will be set to the pk_parity
195+
* argument of secp256k1_xonly_pubkey_from_pubkey (can be NULL).
196+
* In: keypair: pointer to a keypair (cannot be NULL)
197+
*/
198+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_pub(
199+
const secp256k1_context* ctx,
200+
secp256k1_xonly_pubkey *pubkey,
201+
int *pk_parity,
202+
const secp256k1_keypair *keypair
203+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4);
204+
143205
#ifdef __cplusplus
144206
}
145207
#endif

src/modules/extrakeys/main_impl.h

+89
Original file line numberDiff line numberDiff line change
@@ -125,4 +125,93 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u
125125
&& secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
126126
}
127127

128+
static void secp256k1_keypair_save(secp256k1_keypair *keypair, const secp256k1_scalar *sk, secp256k1_ge *pk) {
129+
secp256k1_scalar_get_b32(&keypair->data[0], sk);
130+
secp256k1_pubkey_save((secp256k1_pubkey *)&keypair->data[32], pk);
131+
}
132+
133+
134+
static int secp256k1_keypair_seckey_load(const secp256k1_context* ctx, secp256k1_scalar *sk, const secp256k1_keypair *keypair) {
135+
int ret;
136+
137+
ret = secp256k1_scalar_set_b32_seckey(sk, &keypair->data[0]);
138+
/* We can declassify ret here because sk is only zero if a keypair function
139+
* failed (which zeroes the keypair) and its return value is ignored. */
140+
secp256k1_declassify(ctx, &ret, sizeof(ret));
141+
ARG_CHECK(ret);
142+
return ret;
143+
}
144+
145+
/* Load a keypair into pk and sk (if non-NULL). This function declassifies pk
146+
* and ARG_CHECKs that the keypair is not invalid. It always initializes sk and
147+
* pk with dummy values. */
148+
static int secp256k1_keypair_load(const secp256k1_context* ctx, secp256k1_scalar *sk, secp256k1_ge *pk, const secp256k1_keypair *keypair) {
149+
int ret;
150+
const secp256k1_pubkey *pubkey = (const secp256k1_pubkey *)&keypair->data[32];
151+
152+
/* Need to declassify the pubkey because pubkey_load ARG_CHECKs if it's
153+
* invalid. */
154+
secp256k1_declassify(ctx, pubkey, sizeof(*pubkey));
155+
ret = secp256k1_pubkey_load(ctx, pk, pubkey);
156+
if (sk != NULL) {
157+
ret = ret && secp256k1_keypair_seckey_load(ctx, sk, keypair);
158+
}
159+
if (!ret) {
160+
*pk = secp256k1_ge_const_g;
161+
if (sk != NULL) {
162+
*sk = secp256k1_scalar_one;
163+
}
164+
}
165+
return ret;
166+
}
167+
168+
int secp256k1_keypair_create(const secp256k1_context* ctx, secp256k1_keypair *keypair, const unsigned char *seckey32) {
169+
secp256k1_scalar sk;
170+
secp256k1_ge pk;
171+
int ret = 0;
172+
VERIFY_CHECK(ctx != NULL);
173+
ARG_CHECK(keypair != NULL);
174+
memset(keypair, 0, sizeof(*keypair));
175+
ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx));
176+
ARG_CHECK(seckey32 != NULL);
177+
178+
ret = secp256k1_ec_pubkey_create_helper(&ctx->ecmult_gen_ctx, &sk, &pk, seckey32);
179+
secp256k1_keypair_save(keypair, &sk, &pk);
180+
memczero(keypair, sizeof(*keypair), !ret);
181+
182+
secp256k1_scalar_clear(&sk);
183+
return ret;
184+
}
185+
186+
int secp256k1_keypair_pub(const secp256k1_context* ctx, secp256k1_pubkey *pubkey, const secp256k1_keypair *keypair) {
187+
VERIFY_CHECK(ctx != NULL);
188+
ARG_CHECK(pubkey != NULL);
189+
memset(pubkey, 0, sizeof(*pubkey));
190+
ARG_CHECK(keypair != NULL);
191+
192+
memcpy(pubkey->data, &keypair->data[32], sizeof(*pubkey));
193+
return 1;
194+
}
195+
196+
int secp256k1_keypair_xonly_pub(const secp256k1_context* ctx, secp256k1_xonly_pubkey *pubkey, int *pk_parity, const secp256k1_keypair *keypair) {
197+
secp256k1_ge pk;
198+
int tmp;
199+
200+
VERIFY_CHECK(ctx != NULL);
201+
ARG_CHECK(pubkey != NULL);
202+
memset(pubkey, 0, sizeof(*pubkey));
203+
ARG_CHECK(keypair != NULL);
204+
205+
if (!secp256k1_keypair_load(ctx, NULL, &pk, keypair)) {
206+
return 0;
207+
}
208+
tmp = secp256k1_extrakeys_ge_even_y(&pk);
209+
if (pk_parity != NULL) {
210+
*pk_parity = tmp;
211+
}
212+
secp256k1_xonly_pubkey_save(pubkey, &pk);
213+
214+
return 1;
215+
}
216+
128217
#endif

src/modules/extrakeys/tests_impl.h

+95
Original file line numberDiff line numberDiff line change
@@ -309,12 +309,107 @@ void test_xonly_pubkey_tweak_recursive(void) {
309309
}
310310
#undef N_PUBKEYS
311311

312+
void test_keypair(void) {
313+
unsigned char sk[32];
314+
unsigned char zeros96[96] = { 0 };
315+
unsigned char overflows[32];
316+
secp256k1_keypair keypair;
317+
secp256k1_pubkey pk, pk_tmp;
318+
secp256k1_xonly_pubkey xonly_pk, xonly_pk_tmp;
319+
int pk_parity, pk_parity_tmp;
320+
int ecount;
321+
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
322+
secp256k1_context *sign = api_test_context(SECP256K1_CONTEXT_SIGN, &ecount);
323+
secp256k1_context *verify = api_test_context(SECP256K1_CONTEXT_VERIFY, &ecount);
324+
325+
CHECK(sizeof(zeros96) == sizeof(keypair));
326+
memset(overflows, 0xFF, sizeof(overflows));
327+
328+
/* Test keypair_create */
329+
ecount = 0;
330+
secp256k1_rand256(sk);
331+
CHECK(secp256k1_keypair_create(none, &keypair, sk) == 0);
332+
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
333+
CHECK(ecount == 1);
334+
CHECK(secp256k1_keypair_create(verify, &keypair, sk) == 0);
335+
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
336+
CHECK(ecount == 2);
337+
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
338+
CHECK(secp256k1_keypair_create(sign, NULL, sk) == 0);
339+
CHECK(ecount == 3);
340+
CHECK(secp256k1_keypair_create(sign, &keypair, NULL) == 0);
341+
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
342+
CHECK(ecount == 4);
343+
344+
/* Invalid secret key */
345+
CHECK(secp256k1_keypair_create(sign, &keypair, zeros96) == 0);
346+
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
347+
CHECK(secp256k1_keypair_create(sign, &keypair, overflows) == 0);
348+
CHECK(memcmp(zeros96, &keypair, sizeof(keypair)) == 0);
349+
350+
/* Test keypair_pub */
351+
ecount = 0;
352+
secp256k1_rand256(sk);
353+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
354+
CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
355+
CHECK(secp256k1_keypair_pub(none, NULL, &keypair) == 0);
356+
CHECK(ecount == 1);
357+
CHECK(secp256k1_keypair_pub(none, &pk, NULL) == 0);
358+
CHECK(ecount == 2);
359+
CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);
360+
361+
/* Using an invalid keypair is fine for keypair_pub */
362+
memset(&keypair, 0, sizeof(keypair));
363+
CHECK(secp256k1_keypair_pub(none, &pk, &keypair) == 1);
364+
CHECK(memcmp(zeros96, &pk, sizeof(pk)) == 0);
365+
366+
/* keypair holds the same pubkey as pubkey_create */
367+
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
368+
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
369+
CHECK(secp256k1_keypair_pub(none, &pk_tmp, &keypair) == 1);
370+
CHECK(memcmp(&pk, &pk_tmp, sizeof(pk)) == 0);
371+
372+
/** Test keypair_xonly_pub **/
373+
ecount = 0;
374+
secp256k1_rand256(sk);
375+
CHECK(secp256k1_keypair_create(ctx, &keypair, sk) == 1);
376+
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
377+
CHECK(secp256k1_keypair_xonly_pub(none, NULL, &pk_parity, &keypair) == 0);
378+
CHECK(ecount == 1);
379+
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, NULL, &keypair) == 1);
380+
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, NULL) == 0);
381+
CHECK(ecount == 2);
382+
CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
383+
/* Using an invalid keypair will set the xonly_pk to 0 (first reset
384+
* xonly_pk). */
385+
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 1);
386+
memset(&keypair, 0, sizeof(keypair));
387+
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk, &pk_parity, &keypair) == 0);
388+
CHECK(memcmp(zeros96, &xonly_pk, sizeof(xonly_pk)) == 0);
389+
CHECK(ecount == 3);
390+
391+
/** keypair holds the same xonly pubkey as pubkey_create **/
392+
CHECK(secp256k1_ec_pubkey_create(sign, &pk, sk) == 1);
393+
CHECK(secp256k1_xonly_pubkey_from_pubkey(none, &xonly_pk, &pk_parity, &pk) == 1);
394+
CHECK(secp256k1_keypair_create(sign, &keypair, sk) == 1);
395+
CHECK(secp256k1_keypair_xonly_pub(none, &xonly_pk_tmp, &pk_parity_tmp, &keypair) == 1);
396+
CHECK(memcmp(&xonly_pk, &xonly_pk_tmp, sizeof(pk)) == 0);
397+
CHECK(pk_parity == pk_parity_tmp);
398+
399+
secp256k1_context_destroy(none);
400+
secp256k1_context_destroy(sign);
401+
secp256k1_context_destroy(verify);
402+
}
403+
312404
void run_extrakeys_tests(void) {
313405
/* xonly key test cases */
314406
test_xonly_pubkey();
315407
test_xonly_pubkey_tweak();
316408
test_xonly_pubkey_tweak_check();
317409
test_xonly_pubkey_tweak_recursive();
410+
411+
/* keypair tests */
412+
test_keypair();
318413
}
319414

320415
#endif

src/valgrind_ctime_test.c

+14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121
# include "include/secp256k1_schnorr.h"
2222
#endif
2323

24+
#if ENABLE_MODULE_EXTRAKEYS
25+
# include "include/secp256k1_extrakeys.h"
26+
#endif
27+
2428
int main(void) {
2529
secp256k1_context* ctx;
2630
secp256k1_ecdsa_signature signature;
@@ -37,6 +41,9 @@ int main(void) {
3741
secp256k1_ecdsa_recoverable_signature recoverable_signature;
3842
int recid;
3943
#endif
44+
#if ENABLE_MODULE_EXTRAKEYS
45+
secp256k1_keypair keypair;
46+
#endif
4047

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

138+
#if ENABLE_MODULE_EXTRAKEYS
139+
VALGRIND_MAKE_MEM_UNDEFINED(key, 32);
140+
ret = secp256k1_keypair_create(ctx, &keypair, key);
141+
VALGRIND_MAKE_MEM_DEFINED(&ret, sizeof(ret));
142+
CHECK(ret == 1);
143+
#endif
144+
131145
secp256k1_context_destroy(ctx);
132146
return 0;
133147
}

0 commit comments

Comments
 (0)