Skip to content

Commit bf1ebb8

Browse files
committed
extrakeys: add secp256k1_pubkey_cmp
1 parent d373bf6 commit bf1ebb8

File tree

3 files changed

+82
-0
lines changed

3 files changed

+82
-0
lines changed

include/secp256k1_extrakeys.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,22 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add
240240
const unsigned char *tweak32
241241
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
242242

243+
/** Compare two public keys using lexicographic order of their compressed
244+
* serialization.
245+
*
246+
* Returns: <0 if the first public key is less than the second
247+
* >0 if the first public key is greater than the second
248+
* 0 if the two public keys are equal
249+
* Args: ctx: a secp256k1 context object.
250+
* In: pubkey1: first public key to compare
251+
* pubkey2: second public key to compare
252+
*/
253+
SECP256K1_API int secp256k1_pubkey_cmp(
254+
const secp256k1_context *ctx,
255+
const secp256k1_pubkey *pk1,
256+
const secp256k1_pubkey *pk2
257+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
258+
243259
#ifdef __cplusplus
244260
}
245261
#endif

src/modules/extrakeys/main_impl.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,4 +282,31 @@ int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_ke
282282
return ret;
283283
}
284284

285+
int secp256k1_pubkey_cmp(const secp256k1_context* ctx, const secp256k1_pubkey* pk0, const secp256k1_pubkey* pk1) {
286+
unsigned char out[2][33];
287+
const secp256k1_pubkey* pk[2];
288+
int i;
289+
290+
VERIFY_CHECK(ctx != NULL);
291+
pk[0] = pk0; pk[1] = pk1;
292+
for (i = 0; i < 2; i++) {
293+
size_t outputlen = sizeof(out[i]);
294+
/* If the public key is NULL or invalid, pubkey_serialize will
295+
* call the illegal_callback and return 0. In that case we will
296+
* serialize the key as all zeros which is less than any valid public
297+
* key. This results in consistent comparisons even if NULL or invalid
298+
* pubkeys are involved and prevents edge cases such as sorting
299+
* algorithms that use this function and do not terminate as a
300+
* result. */
301+
if (!secp256k1_ec_pubkey_serialize(ctx, out[i], &outputlen, pk[i], SECP256K1_EC_COMPRESSED)) {
302+
/* Note that pubkey_serialize should already set the output to
303+
* zero in that case, but it's not guaranteed by the API, we can't
304+
* test it and writing a VERIFY_CHECK is more complex than
305+
* explicitly memsetting (again). */
306+
memset(out[i], 0, sizeof(out[i]));
307+
}
308+
}
309+
return secp256k1_memcmp_var(out[0], out[1], sizeof(out[1]));
310+
}
311+
285312
#endif

src/modules/extrakeys/tests_impl.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,42 @@ static void test_keypair_add(void) {
467467
}
468468
}
469469

470+
static void test_pubkey_comparison(void) {
471+
unsigned char pk1_ser[33] = {
472+
0x02,
473+
0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11,
474+
0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23
475+
};
476+
const unsigned char pk2_ser[33] = {
477+
0x03,
478+
0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d,
479+
0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c
480+
};
481+
secp256k1_pubkey pk1;
482+
secp256k1_pubkey pk2;
483+
484+
CHECK(secp256k1_ec_pubkey_parse(CTX, &pk1, pk1_ser, sizeof(pk1_ser)) == 1);
485+
CHECK(secp256k1_ec_pubkey_parse(CTX, &pk2, pk2_ser, sizeof(pk2_ser)) == 1);
486+
487+
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, NULL, &pk2) < 0));
488+
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, &pk1, NULL) > 0));
489+
CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk2) < 0);
490+
CHECK(secp256k1_pubkey_cmp(CTX, &pk2, &pk1) > 0);
491+
CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk1) == 0);
492+
CHECK(secp256k1_pubkey_cmp(CTX, &pk2, &pk2) == 0);
493+
memset(&pk1, 0, sizeof(pk1)); /* illegal pubkey */
494+
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk2) < 0));
495+
{
496+
int32_t ecount = 0;
497+
secp256k1_context_set_illegal_callback(CTX, counting_callback_fn, &ecount);
498+
CHECK(secp256k1_pubkey_cmp(CTX, &pk1, &pk1) == 0);
499+
CHECK(ecount == 2);
500+
secp256k1_context_set_illegal_callback(CTX, NULL, NULL);
501+
}
502+
CHECK_ILLEGAL_VOID(CTX, CHECK(secp256k1_pubkey_cmp(CTX, &pk2, &pk1) > 0));
503+
504+
}
505+
470506
static void run_extrakeys_tests(void) {
471507
/* xonly key test cases */
472508
test_xonly_pubkey();
@@ -478,6 +514,9 @@ static void run_extrakeys_tests(void) {
478514
/* keypair tests */
479515
test_keypair();
480516
test_keypair_add();
517+
518+
/* pubkey tests */
519+
test_pubkey_comparison();
481520
}
482521

483522
#endif

0 commit comments

Comments
 (0)