Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

#### Added
- New function `secp256k1_ec_pubkey_sort` that sorts public keys using lexicographic (of compressed serialization) order.

#### Changed
- The implementation of the point multiplication algorithm used for signing and public key generation was changed, resulting in improved performance for those operations.
- The related configure option `--ecmult-gen-precision` was replaced with `--ecmult-gen-kb` (`ECMULT_GEN_KB` for CMake).
Expand Down
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ noinst_HEADERS += src/field.h
noinst_HEADERS += src/field_impl.h
noinst_HEADERS += src/bench.h
noinst_HEADERS += src/wycheproof/ecdsa_secp256k1_sha256_bitcoin_test.h
noinst_HEADERS += src/hsort.h
noinst_HEADERS += src/hsort_impl.h
noinst_HEADERS += contrib/lax_der_parsing.h
noinst_HEADERS += contrib/lax_der_parsing.c
noinst_HEADERS += contrib/lax_der_privatekey_parsing.h
Expand Down
14 changes: 14 additions & 0 deletions include/secp256k1.h
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,20 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_cmp(
const secp256k1_pubkey *pubkey2
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Sort public keys using lexicographic (of compressed serialization) order
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
*
* Args: ctx: pointer to a context object
* In: pubkeys: array of pointers to pubkeys to sort
* n_pubkeys: number of elements in the pubkeys array
*/
SECP256K1_API int secp256k1_ec_pubkey_sort(
const secp256k1_context *ctx,
const secp256k1_pubkey **pubkeys,
size_t n_pubkeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);

/** Parse an ECDSA signature in compact (64 bytes) format.
*
* Returns: 1 when the signature could be parsed, 0 otherwise.
Expand Down
15 changes: 0 additions & 15 deletions include/secp256k1_extrakeys.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,21 +240,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_keypair_xonly_tweak_add
const unsigned char *tweak32
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3);

/** Sort public keys using lexicographic order of their compressed
* serialization.
*
* Returns: 0 if the arguments are invalid. 1 otherwise.
*
* Args: ctx: pointer to a context object
* In: pubkeys: array of pointers to pubkeys to sort
* n_pubkeys: number of elements in the pubkeys array
*/
SECP256K1_API int secp256k1_pubkey_sort(
const secp256k1_context *ctx,
const secp256k1_pubkey **pubkeys,
size_t n_pubkeys
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion include/secp256k1_musig.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ SECP256K1_API int secp256k1_musig_partial_sig_parse(
*
* Different orders of `pubkeys` result in different `agg_pk`s.
*
* Before aggregating, the pubkeys can be sorted with `secp256k1_pubkey_sort`
* Before aggregating, the pubkeys can be sorted with `secp256k1_ec_pubkey_sort`
* which ensures the same `agg_pk` result for the same multiset of pubkeys.
* This is useful to do before `pubkey_agg`, such that the order of pubkeys
* does not affect the aggregate public key.
Expand Down
13 changes: 12 additions & 1 deletion src/modules/extrakeys/hsort.h → src/hsort.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,19 @@
/* In-place, iterative heapsort with an interface matching glibc's qsort_r. This
* is preferred over standard library implementations because they generally
* make no guarantee about being fast for malicious inputs.
* Remember that heapsort is unstable.
*
* See the qsort_r manpage for a description of the interface.
* In/Out: ptr: pointer to the array to sort. The contents of the array are
* sorted in ascending order according to the comparison function.
* In: count: number of elements in the array.
* size: size in bytes of each element.
* cmp: pointer to a comparison function that is called with two
* arguments that point to the objects being compared. The cmp_data
* argument of secp256k1_hsort is passed as third argument. The
* function must return an integer less than, equal to, or greater
* than zero if the first argument is considered to be respectively
* less than, equal to, or greater than the second.
* cmp_data: pointer passed as third argument to cmp.
*/
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
int (*cmp)(const void *, const void *, void *),
Expand Down
69 changes: 39 additions & 30 deletions src/modules/extrakeys/hsort_impl.h → src/hsort_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,42 @@
* compares as less than or equal to the element at index parent(i) = (i-1)/2.
*/

static SECP256K1_INLINE size_t child1(size_t i) {
static SECP256K1_INLINE size_t secp256k1_heap_child1(size_t i) {
VERIFY_CHECK(i <= (SIZE_MAX - 1)/2);
return 2*i + 1;
}

static SECP256K1_INLINE size_t child2(size_t i) {
static SECP256K1_INLINE size_t secp256k1_heap_child2(size_t i) {
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
return child1(i)+1;
return secp256k1_heap_child1(i)+1;
}

static SECP256K1_INLINE void heap_swap64(unsigned char *a, size_t i, size_t j, size_t stride) {
static SECP256K1_INLINE void secp256k1_heap_swap64(unsigned char *a, unsigned char *b, size_t len) {
unsigned char tmp[64];
VERIFY_CHECK(stride <= 64);
memcpy(tmp, a + i*stride, stride);
memmove(a + i*stride, a + j*stride, stride);
memcpy(a + j*stride, tmp, stride);
VERIFY_CHECK(len <= 64);
memcpy(tmp, a, len);
memmove(a, b, len);
memcpy(b, tmp, len);
}

static SECP256K1_INLINE void heap_swap(unsigned char *a, size_t i, size_t j, size_t stride) {
while (64 < stride) {
heap_swap64(a + (stride - 64), i, j, 64);
stride -= 64;
static SECP256K1_INLINE void secp256k1_heap_swap(unsigned char *arr, size_t i, size_t j, size_t stride) {
unsigned char *a = arr + i*stride;
unsigned char *b = arr + j*stride;
size_t len = stride;
while (64 < len) {
secp256k1_heap_swap64(a + (len - 64), b + (len - 64), 64);
len -= 64;
}
heap_swap64(a, i, j, stride);
secp256k1_heap_swap64(a, b, len);
}

static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_size, size_t stride,
/* This function accepts an array arr containing heap_size elements, each of
* size stride. The elements in the array at indices >i satisfy the max-heap
* property, i.e., for any element at index j (where j > i), all of its children
* are smaller than the element itself. The purpose of the function is to update
* the array so that all elements at indices >=i satisfy the max-heap
* property. */
static SECP256K1_INLINE void secp256k1_heap_down(unsigned char *arr, size_t i, size_t heap_size, size_t stride,
int (*cmp)(const void *, const void *, void *), void *cmp_data) {
while (i < heap_size/2) {
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
Expand All @@ -50,7 +59,7 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
* 2*i <= SIZE_MAX - 2
*/

VERIFY_CHECK(child1(i) < heap_size);
VERIFY_CHECK(secp256k1_heap_child1(i) < heap_size);
/* Proof:
* i < heap_size/2
* i + 1 <= heap_size/2
Expand All @@ -59,7 +68,7 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
* child1(i) < heap_size
*/

/* Let [x] be notation for the contents at a[x*stride].
/* Let [x] be notation for the contents at arr[x*stride].
*
* If [child1(i)] > [i] and [child2(i)] > [i],
* swap [i] with the larger child to ensure the new parent is larger
Expand All @@ -68,20 +77,20 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
* Else if [child1(i)] > [i], swap [i] with [child1(i)].
* Else if [child2(i)] > [i], swap [i] with [child2(i)].
*/
if (child2(i) < heap_size
&& 0 <= cmp(a + child2(i)*stride, a + child1(i)*stride, cmp_data)) {
if (0 < cmp(a + child2(i)*stride, a + i*stride, cmp_data)) {
heap_swap(a, i, child2(i), stride);
i = child2(i);
if (secp256k1_heap_child2(i) < heap_size
&& 0 <= cmp(arr + secp256k1_heap_child2(i)*stride, arr + secp256k1_heap_child1(i)*stride, cmp_data)) {
if (0 < cmp(arr + secp256k1_heap_child2(i)*stride, arr + i*stride, cmp_data)) {
secp256k1_heap_swap(arr, i, secp256k1_heap_child2(i), stride);
i = secp256k1_heap_child2(i);
} else {
/* At this point we have [child2(i)] >= [child1(i)] and we have
* [child2(i)] <= [i], and thus [child1(i)] <= [i] which means
* that the next comparison can be skipped. */
return;
}
} else if (0 < cmp(a + child1(i)*stride, a + i*stride, cmp_data)) {
heap_swap(a, i, child1(i), stride);
i = child1(i);
} else if (0 < cmp(arr + secp256k1_heap_child1(i)*stride, arr + i*stride, cmp_data)) {
secp256k1_heap_swap(arr, i, secp256k1_heap_child1(i), stride);
i = secp256k1_heap_child1(i);
} else {
return;
}
Expand All @@ -98,18 +107,18 @@ static SECP256K1_INLINE void heap_down(unsigned char *a, size_t i, size_t heap_s
/* In-place heap sort. */
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
int (*cmp)(const void *, const void *, void *),
void *cmp_data ) {
void *cmp_data) {
size_t i;

for(i = count/2; 0 < i; --i) {
heap_down(ptr, i-1, count, size, cmp, cmp_data);
for (i = count/2; 0 < i; --i) {
secp256k1_heap_down(ptr, i-1, count, size, cmp, cmp_data);
}
for(i = count; 1 < i; --i) {
for (i = count; 1 < i; --i) {
/* Extract the largest value from the heap */
heap_swap(ptr, 0, i-1, size);
secp256k1_heap_swap(ptr, 0, i-1, size);

/* Repair the heap condition */
heap_down(ptr, 0, i-1, size, cmp, cmp_data);
secp256k1_heap_down(ptr, 0, i-1, size, cmp, cmp_data);
}
}

Expand Down
4 changes: 1 addition & 3 deletions src/modules/extrakeys/Makefile.am.include
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
include_HEADERS += include/secp256k1_extrakeys.h
noinst_HEADERS += src/modules/extrakeys/tests_impl.h
noinst_HEADERS += src/modules/extrakeys/tests_exhaustive_impl.h
noinst_HEADERS += src/modules/extrakeys/main_impl.h
noinst_HEADERS += src/modules/extrakeys/hsort.h
noinst_HEADERS += src/modules/extrakeys/hsort_impl.h
noinst_HEADERS += src/modules/extrakeys/main_impl.h
35 changes: 0 additions & 35 deletions src/modules/extrakeys/main_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

#include "../../../include/secp256k1.h"
#include "../../../include/secp256k1_extrakeys.h"
#include "hsort_impl.h"
#include "../../util.h"

static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
Expand Down Expand Up @@ -283,38 +282,4 @@ int secp256k1_keypair_xonly_tweak_add(const secp256k1_context* ctx, secp256k1_ke
return ret;
}

/* This struct wraps a const context pointer to satisfy the secp256k1_hsort api
* which expects a non-const cmp_data pointer. */
typedef struct {
const secp256k1_context *ctx;
} secp256k1_pubkey_sort_cmp_data;

static int secp256k1_pubkey_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) {
return secp256k1_ec_pubkey_cmp(((secp256k1_pubkey_sort_cmp_data*)cmp_data)->ctx,
*(secp256k1_pubkey **)pk1,
*(secp256k1_pubkey **)pk2);
}

int secp256k1_pubkey_sort(const secp256k1_context* ctx, const secp256k1_pubkey **pubkeys, size_t n_pubkeys) {
secp256k1_pubkey_sort_cmp_data cmp_data;
VERIFY_CHECK(ctx != NULL);
ARG_CHECK(pubkeys != NULL);

cmp_data.ctx = ctx;

/* Suppress wrong warning (fixed in MSVC 19.33) */
#if defined(_MSC_VER) && (_MSC_VER < 1933)
#pragma warning(push)
#pragma warning(disable: 4090)
#endif

secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_pubkey_sort_cmp, &cmp_data);

#if defined(_MSC_VER) && (_MSC_VER < 1933)
#pragma warning(pop)
#endif

return 1;
}

#endif
Loading
Loading