Skip to content

Commit 0831038

Browse files
committed
add secp256k1_ec_pubkey_equal and secp256k1_ec_pubkey_leq methods
1 parent 9e5939d commit 0831038

File tree

3 files changed

+114
-2
lines changed

3 files changed

+114
-2
lines changed

include/secp256k1.h

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space;
6161
* The exact representation of data inside is implementation defined and not
6262
* guaranteed to be portable between different platforms or versions. It is
6363
* however guaranteed to be 64 bytes in size, and can be safely copied/moved.
64-
* If you need to convert to a format suitable for storage, transmission, or
65-
* comparison, use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse.
64+
* If you need to convert to a format suitable for storage or transmission,
65+
* use secp256k1_ec_pubkey_serialize and secp256k1_ec_pubkey_parse. To
66+
* compare keys, use secp256k1_ec_pubkey_equal or secp256k1_ec_pubkey_leq.
6667
*/
6768
typedef struct {
6869
unsigned char data[64];
@@ -370,6 +371,34 @@ SECP256K1_API int secp256k1_ec_pubkey_serialize(
370371
unsigned int flags
371372
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4);
372373

374+
/** Determine whether two public keys are equal
375+
*
376+
* Returns: 1 if the public keys were equal
377+
* 0 otherwise
378+
* Args: ctx: a secp256k1 context object.
379+
* In: pubkey1: first public key to compare
380+
* pubkey2: second public key to compare
381+
*/
382+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_equal(
383+
const secp256k1_context* ctx,
384+
const secp256k1_pubkey* pubkey1,
385+
const secp256k1_pubkey* pubkey2
386+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
387+
388+
/** Determine the lexicographic compressed serialization ordering of two public keys
389+
*
390+
* Returns: 1 if the first key was less than or equal to the second one
391+
* 0 if the first key was greater than the second one
392+
* Args: ctx: a secp256k1 context object.
393+
* In: pubkey1: first public key to compare
394+
* pubkey2: second public key to compare
395+
*/
396+
SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_leq(
397+
const secp256k1_context* ctx,
398+
const secp256k1_pubkey* pubkey1,
399+
const secp256k1_pubkey* pubkey2
400+
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);
401+
373402
/** Parse an ECDSA signature in compact (64 bytes) format.
374403
*
375404
* Returns: 1 when the signature could be parsed, 0 otherwise.

src/secp256k1.c

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,41 @@ int secp256k1_ec_pubkey_serialize(const secp256k1_context* ctx, unsigned char *o
317317
return ret;
318318
}
319319

320+
int secp256k1_ec_pubkey_equal(const secp256k1_context* ctx, const secp256k1_pubkey* pubkey1, const secp256k1_pubkey* pubkey2) {
321+
VERIFY_CHECK(ctx != NULL);
322+
ARG_CHECK(pubkey1 != NULL);
323+
ARG_CHECK(pubkey2 != NULL);
324+
return !secp256k1_memcmp_var(pubkey1, pubkey2, sizeof(pubkey1));
325+
}
326+
327+
int secp256k1_ec_pubkey_leq(const secp256k1_context* ctx, const secp256k1_pubkey* pubkey1, const secp256k1_pubkey* pubkey2) {
328+
unsigned char out1[33];
329+
unsigned char out2[33];
330+
size_t out_size = sizeof(out1);
331+
size_t i;
332+
int check;
333+
334+
VERIFY_CHECK(ctx != NULL);
335+
ARG_CHECK(pubkey1 != NULL);
336+
ARG_CHECK(pubkey2 != NULL);
337+
338+
check = secp256k1_ec_pubkey_serialize(ctx, out1, &out_size, pubkey1, SECP256K1_EC_COMPRESSED);
339+
VERIFY_CHECK(check == 1);
340+
VERIFY_CHECK(out_size == sizeof(out1));
341+
check = secp256k1_ec_pubkey_serialize(ctx, out2, &out_size, pubkey2, SECP256K1_EC_COMPRESSED);
342+
VERIFY_CHECK(check == 1);
343+
VERIFY_CHECK(out_size == sizeof(out1));
344+
345+
for (i = 0; i < out_size; i++) {
346+
if (out1[i] > out2[i]) {
347+
return 0;
348+
} else if (out1[i] < out2[i]) {
349+
return 1;
350+
}
351+
}
352+
return 1;
353+
}
354+
320355
static void secp256k1_ecdsa_signature_load(const secp256k1_context* ctx, secp256k1_scalar* r, secp256k1_scalar* s, const secp256k1_ecdsa_signature* sig) {
321356
(void)ctx;
322357
if (sizeof(secp256k1_scalar) == 32) {

src/tests.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4654,6 +4654,53 @@ void test_random_pubkeys(void) {
46544654
}
46554655
}
46564656

4657+
void run_pubkey_comparison(void) {
4658+
unsigned char pk1_ser[33] = {
4659+
0x02,
4660+
0x58, 0x84, 0xb3, 0xa2, 0x4b, 0x97, 0x37, 0x88, 0x92, 0x38, 0xa6, 0x26, 0x62, 0x52, 0x35, 0x11,
4661+
0xd0, 0x9a, 0xa1, 0x1b, 0x80, 0x0b, 0x5e, 0x93, 0x80, 0x26, 0x11, 0xef, 0x67, 0x4b, 0xd9, 0x23
4662+
};
4663+
const unsigned char pk2_ser[33] = {
4664+
0x02,
4665+
0xde, 0x36, 0x0e, 0x87, 0x59, 0x8f, 0x3c, 0x01, 0x36, 0x2a, 0x2a, 0xb8, 0xc6, 0xf4, 0x5e, 0x4d,
4666+
0xb2, 0xc2, 0xd5, 0x03, 0xa7, 0xf9, 0xf1, 0x4f, 0xa8, 0xfa, 0x95, 0xa8, 0xe9, 0x69, 0x76, 0x1c
4667+
};
4668+
secp256k1_pubkey pk1;
4669+
secp256k1_pubkey pk2;
4670+
int32_t ecount = 0;
4671+
4672+
CHECK(secp256k1_ec_pubkey_parse(ctx, &pk1, pk1_ser, sizeof(pk1_ser)) == 1);
4673+
CHECK(secp256k1_ec_pubkey_parse(ctx, &pk2, pk2_ser, sizeof(pk2_ser)) == 1);
4674+
4675+
secp256k1_context_set_illegal_callback(ctx, counting_illegal_callback_fn, &ecount);
4676+
CHECK(secp256k1_ec_pubkey_equal(ctx, NULL, &pk2) == 0);
4677+
CHECK(ecount == 1);
4678+
CHECK(secp256k1_ec_pubkey_equal(ctx, &pk1, NULL) == 0);
4679+
CHECK(ecount == 2);
4680+
CHECK(secp256k1_ec_pubkey_equal(ctx, &pk1, &pk2) == 0);
4681+
CHECK(secp256k1_ec_pubkey_equal(ctx, &pk1, &pk1) == 1);
4682+
CHECK(secp256k1_ec_pubkey_equal(ctx, &pk2, &pk2) == 1);
4683+
CHECK(ecount == 2);
4684+
4685+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk1, NULL) == 0);
4686+
CHECK(ecount == 3);
4687+
CHECK(secp256k1_ec_pubkey_leq(ctx, NULL, &pk2) == 0);
4688+
CHECK(ecount == 4);
4689+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk1, &pk2) == 1);
4690+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk2, &pk1) == 0);
4691+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk1, &pk1) == 1);
4692+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk2, &pk2) == 1);
4693+
CHECK(ecount == 4);
4694+
secp256k1_context_set_illegal_callback(ctx, NULL, NULL);
4695+
4696+
/* Make pk2 the same as pk1 but with 3 rather than 2. Note that in
4697+
* an uncompressed encoding, these would have the opposite ordering */
4698+
pk1_ser[0] = 3;
4699+
CHECK(secp256k1_ec_pubkey_parse(ctx, &pk2, pk1_ser, sizeof(pk1_ser)) == 1);
4700+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk1, &pk2) == 1);
4701+
CHECK(secp256k1_ec_pubkey_leq(ctx, &pk2, &pk1) == 0);
4702+
}
4703+
46574704
void run_random_pubkeys(void) {
46584705
int i;
46594706
for (i = 0; i < 10*count; i++) {
@@ -5700,6 +5747,7 @@ int main(int argc, char **argv) {
57005747
#endif
57015748

57025749
/* ecdsa tests */
5750+
run_pubkey_comparison();
57035751
run_random_pubkeys();
57045752
run_ecdsa_der_parse();
57055753
run_ecdsa_sign_verify();

0 commit comments

Comments
 (0)