Merge elementsproject/secp256k1-zkp#120: Add MuSig Key Aggregation spec
fc26ca8ddef0629c7df190f1cc92157fce64e370 musig: remove unnecessary constant time normalize in combine (Jonas Nick) 48f63efe683bf5539324a52fa43f4a2a32285a91 musig: remove unnecessary branch in pubkey_tweak_add (Jonas Nick) 5860b5e0fe78b2bd34c1defb6ce3ad879029463e musig: do not also require schnorrsig module config flag (Jonas Nick) f27fd1d5e754fc9b919d9c9f6e47a6eb8c9e2af7 musig: improve test coverage of pubkey_combine (Jonas Nick) 56014e8ca01e88e0fbf2f125363c4e7cc48039df musig: change pubkey_combine arg to array of pointers to pks (Jonas Nick) 08fa02d579154e26097fd582a409b814ef3dedba musig: add key aggregation spec draft (Jonas Nick) 4a9b059b16d7925a03bd0d695efa1637ad7e9826 musig: rename Musig coefficient to KeyAgg coefficient (Jonas Nick) 4bc46d836e7877715db54ee039ade407ee44ea45 musig: optimize key aggregation using const 1 for 2nd key (Jonas Nick) 2310849f50fa71f10ebd2f44669330f7ce76fc94 musig: compute musig coefficient by hashing key instead of index (Jonas Nick) 9683c8a7eb6cefa070cd1a931d8dee714496ee82 musig: add static test vectors for key aggregation (Jonas Nick) 9b3d7bf53617c962cd291039d5ce97088c4513cc extrakeys: add xonly_sort function (Jonas Nick) f31affd8a613ebbdb07050a90ff1ccb2b1f0a1fd extrakeys: add hsort, in-place, iterative heapsort (Jonas Nick) Pull request description: ACKs for top commit: real-or-random: ACK fc26ca8ddef0629c7df190f1cc92157fce64e370 Tree-SHA512: fa29fe259d0e98d634782c0fb36308716dc3ffa6e35fe47b87fc25b2e5dd0a9859a72da0b9d669f03d379bc3ed972c5961995762b2f7e4ac16b9c6b5d8c4721d
This commit is contained in:
commit
5d2df05419
@ -492,6 +492,7 @@ fi
|
||||
|
||||
if test x"$enable_module_musig" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_MUSIG, 1, [Define this symbol to enable the MuSig module])
|
||||
enable_module_schnorrsig=yes
|
||||
fi
|
||||
|
||||
if test x"$enable_module_recovery" = x"yes"; then
|
||||
@ -513,7 +514,8 @@ fi
|
||||
if test x"$enable_module_surjectionproof" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_SURJECTIONPROOF, 1, [Define this symbol to enable the surjection proof module])
|
||||
fi
|
||||
|
||||
# Test if extrakeys is set _after_ the MuSig module to allow the MuSig
|
||||
# module to set enable_module_schnorrsig=yes
|
||||
if test x"$enable_module_schnorrsig" = x"yes"; then
|
||||
AC_DEFINE(ENABLE_MODULE_SCHNORRSIG, 1, [Define this symbol to enable the schnorrsig module])
|
||||
enable_module_extrakeys=yes
|
||||
@ -663,6 +665,7 @@ echo " module ecdh = $enable_module_ecdh"
|
||||
echo " module recovery = $enable_module_recovery"
|
||||
echo " module extrakeys = $enable_module_extrakeys"
|
||||
echo " module schnorrsig = $enable_module_schnorrsig"
|
||||
echo " module musig = $enable_module_musig"
|
||||
echo " module ecdsa-s2c = $enable_module_ecdsa_s2c"
|
||||
echo " module ecdsa-adaptor = $enable_module_ecdsa_adaptor"
|
||||
echo
|
||||
|
@ -166,6 +166,20 @@ 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);
|
||||
|
||||
/** Sorts xonly public keys according to secp256k1_xonly_pubkey_cmp
|
||||
*
|
||||
* 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_xonly_sort(
|
||||
const secp256k1_context* ctx,
|
||||
const secp256k1_xonly_pubkey **pubkeys,
|
||||
size_t n_pubkeys
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2);
|
||||
|
||||
/** Compute the keypair for a secret key.
|
||||
*
|
||||
* Returns: 1: secret was valid, keypair is ready to use
|
||||
|
@ -25,6 +25,8 @@ extern "C" {
|
||||
* magic: Set during initialization in `pubkey_combine` to allow
|
||||
* detecting an uninitialized object.
|
||||
* pk_hash: The 32-byte hash of the original public keys
|
||||
* second_pk: Serialized x-coordinate of the second public key in the list.
|
||||
* Filled with zeros if there is none.
|
||||
* pk_parity: Whether the MuSig-aggregated point was negated when
|
||||
* converting it to the combined xonly pubkey.
|
||||
* is_tweaked: Whether the combined pubkey was tweaked
|
||||
@ -35,6 +37,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
uint64_t magic;
|
||||
unsigned char pk_hash[32];
|
||||
unsigned char second_pk[32];
|
||||
int pk_parity;
|
||||
int is_tweaked;
|
||||
unsigned char tweak[32];
|
||||
@ -94,7 +97,7 @@ typedef struct {
|
||||
* The workflow for this structure is as follows:
|
||||
*
|
||||
* 1. This structure is initialized with `musig_session_init` or
|
||||
* `musig_session_init_verifier`, which set the `index` field, and zero out
|
||||
* `musig_session_init_verifier`, which initializes
|
||||
* all other fields. The public session is initialized with the signers'
|
||||
* nonce_commitments.
|
||||
*
|
||||
@ -111,14 +114,12 @@ typedef struct {
|
||||
*
|
||||
* Fields:
|
||||
* present: indicates whether the signer's nonce is set
|
||||
* index: index of the signer in the MuSig key aggregation
|
||||
* nonce: public nonce, must be a valid curvepoint if the signer is `present`
|
||||
* nonce_commitment: commitment to the nonce, or all-bits zero if a commitment
|
||||
* has not yet been set
|
||||
*/
|
||||
typedef struct {
|
||||
int present;
|
||||
uint32_t index;
|
||||
secp256k1_xonly_pubkey nonce;
|
||||
unsigned char nonce_commitment[32];
|
||||
} secp256k1_musig_session_signer_data;
|
||||
@ -137,8 +138,14 @@ typedef struct {
|
||||
} secp256k1_musig_partial_signature;
|
||||
|
||||
/** Computes a combined public key and the hash of the given public keys.
|
||||
*
|
||||
* Different orders of `pubkeys` result in different `combined_pk`s.
|
||||
*
|
||||
* The pubkeys can be sorted before combining with `secp256k1_xonly_sort` which
|
||||
* ensures the same resulting `combined_pk` for the same multiset of pubkeys.
|
||||
* This is useful to do before pubkey_combine, such that the order of pubkeys
|
||||
* does not affect the combined public key.
|
||||
*
|
||||
* Returns: 1 if the public keys were successfully combined, 0 otherwise
|
||||
* Args: ctx: pointer to a context object initialized for verification
|
||||
* (cannot be NULL)
|
||||
@ -147,9 +154,9 @@ typedef struct {
|
||||
* Out: combined_pk: the MuSig-combined xonly public key (cannot be NULL)
|
||||
* pre_session: if non-NULL, pointer to a musig_pre_session struct to be used in
|
||||
* `musig_session_init` or `musig_pubkey_tweak_add`.
|
||||
* In: pubkeys: input array of public keys to combine. The order is important;
|
||||
* a different order will result in a different combined public
|
||||
* key (cannot be NULL)
|
||||
* In: pubkeys: input array of pointers to public keys to combine. The order
|
||||
* is important; a different order will result in a different
|
||||
* combined public key (cannot be NULL)
|
||||
* n_pubkeys: length of pubkeys array. Must be greater than 0.
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_pubkey_combine(
|
||||
@ -157,7 +164,7 @@ SECP256K1_API int secp256k1_musig_pubkey_combine(
|
||||
secp256k1_scratch_space *scratch,
|
||||
secp256k1_xonly_pubkey *combined_pk,
|
||||
secp256k1_musig_pre_session *pre_session,
|
||||
const secp256k1_xonly_pubkey *pubkeys,
|
||||
const secp256k1_xonly_pubkey * const* pubkeys,
|
||||
size_t n_pubkeys
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5);
|
||||
|
||||
@ -221,8 +228,6 @@ SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_pubkey_tweak_add(
|
||||
* `musig_pubkey_tweak_add` (cannot be NULL).
|
||||
* n_signers: length of signers array. Number of signers participating in
|
||||
* the MuSig. Must be greater than 0 and at most 2^32 - 1.
|
||||
* my_index: index of this signer in the signers array. Must be less
|
||||
* than `n_signers`.
|
||||
* seckey: the signer's 32-byte secret key (cannot be NULL)
|
||||
*/
|
||||
SECP256K1_API int secp256k1_musig_session_init(
|
||||
@ -235,7 +240,6 @@ SECP256K1_API int secp256k1_musig_session_init(
|
||||
const secp256k1_xonly_pubkey *combined_pk,
|
||||
const secp256k1_musig_pre_session *pre_session,
|
||||
size_t n_signers,
|
||||
size_t my_index,
|
||||
const unsigned char *seckey
|
||||
) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11);
|
||||
|
||||
|
@ -2,3 +2,5 @@ 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
|
||||
|
22
src/modules/extrakeys/hsort.h
Normal file
22
src/modules/extrakeys/hsort.h
Normal file
@ -0,0 +1,22 @@
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2021 Russell O'Connor, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_HSORT_H_
|
||||
#define SECP256K1_HSORT_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* See the qsort_r manpage for a description of the interface.
|
||||
*/
|
||||
static void secp256k1_hsort(void *ptr, size_t count, size_t size,
|
||||
int (*cmp)(const void *, const void *, void *),
|
||||
void *cmp_data);
|
||||
#endif
|
116
src/modules/extrakeys/hsort_impl.h
Normal file
116
src/modules/extrakeys/hsort_impl.h
Normal file
@ -0,0 +1,116 @@
|
||||
/***********************************************************************
|
||||
* Copyright (c) 2021 Russell O'Connor, Jonas Nick *
|
||||
* Distributed under the MIT software license, see the accompanying *
|
||||
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef SECP256K1_HSORT_IMPL_H_
|
||||
#define SECP256K1_HSORT_IMPL_H_
|
||||
|
||||
#include "hsort.h"
|
||||
|
||||
/* An array is a heap when, for all non-zero indexes i, the element at index i
|
||||
* 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) {
|
||||
VERIFY_CHECK(i <= (SIZE_MAX - 1)/2);
|
||||
return 2*i + 1;
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE size_t child2(size_t i) {
|
||||
VERIFY_CHECK(i <= SIZE_MAX/2 - 1);
|
||||
return child1(i)+1;
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void swap64(unsigned char *a, size_t i, size_t j, size_t stride) {
|
||||
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);
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void swap(unsigned char *a, size_t i, size_t j, size_t stride) {
|
||||
while (64 < stride) {
|
||||
swap64(a + (stride - 64), i, j, 64);
|
||||
stride -= 64;
|
||||
}
|
||||
swap64(a, i, j, stride);
|
||||
}
|
||||
|
||||
static SECP256K1_INLINE void heap_down(unsigned char *a, 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);
|
||||
/* Proof:
|
||||
* i < heap_size/2
|
||||
* i + 1 <= heap_size/2
|
||||
* 2*i + 2 <= heap_size <= SIZE_MAX
|
||||
* 2*i <= SIZE_MAX - 2
|
||||
*/
|
||||
|
||||
VERIFY_CHECK(child1(i) < heap_size);
|
||||
/* Proof:
|
||||
* i < heap_size/2
|
||||
* i + 1 <= heap_size/2
|
||||
* 2*i + 2 <= heap_size
|
||||
* 2*i + 1 < heap_size
|
||||
* child1(i) < heap_size
|
||||
*/
|
||||
|
||||
/* Let [x] be notation for the contents at a[x*stride].
|
||||
*
|
||||
* If [child1(i)] > [i] and [child2(i)] > [i],
|
||||
* swap [i] with the larger child to ensure the new parent is larger
|
||||
* than both children. When [child1(i)] == [child2(i)], swap [i] with
|
||||
* [child2(i)].
|
||||
* 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)) {
|
||||
swap(a, i, child2(i), stride);
|
||||
i = 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)) {
|
||||
swap(a, i, child1(i), stride);
|
||||
i = child1(i);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* heap_size/2 <= i
|
||||
* heap_size/2 < i + 1
|
||||
* heap_size < 2*i + 2
|
||||
* heap_size <= 2*i + 1
|
||||
* heap_size <= child1(i)
|
||||
* Thus child1(i) and child2(i) are now out of bounds and we are at a leaf.
|
||||
*/
|
||||
}
|
||||
|
||||
/* 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 ) {
|
||||
size_t i;
|
||||
|
||||
for(i = count/2; 0 < i; --i) {
|
||||
heap_down(ptr, i-1, count, size, cmp, cmp_data);
|
||||
}
|
||||
for(i = count; 1 < i; --i) {
|
||||
/* Extract the largest value from the heap */
|
||||
swap(ptr, 0, i-1, size);
|
||||
|
||||
/* Repair the heap condition */
|
||||
heap_down(ptr, 0, i-1, size, cmp, cmp_data);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -9,6 +9,7 @@
|
||||
|
||||
#include "../../../include/secp256k1.h"
|
||||
#include "../../../include/secp256k1_extrakeys.h"
|
||||
#include "hsort_impl.h"
|
||||
|
||||
static SECP256K1_INLINE int secp256k1_xonly_pubkey_load(const secp256k1_context* ctx, secp256k1_ge *ge, const secp256k1_xonly_pubkey *pubkey) {
|
||||
return secp256k1_pubkey_load(ctx, ge, (const secp256k1_pubkey *) pubkey);
|
||||
@ -154,6 +155,28 @@ int secp256k1_xonly_pubkey_tweak_add_check(const secp256k1_context* ctx, const u
|
||||
&& secp256k1_fe_is_odd(&pk.y) == tweaked_pk_parity;
|
||||
}
|
||||
|
||||
/* 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_xonly_sort_cmp_data;
|
||||
|
||||
static int secp256k1_xonly_sort_cmp(const void* pk1, const void* pk2, void *cmp_data) {
|
||||
return secp256k1_xonly_pubkey_cmp(((secp256k1_xonly_sort_cmp_data*)cmp_data)->ctx,
|
||||
*(secp256k1_xonly_pubkey **)pk1,
|
||||
*(secp256k1_xonly_pubkey **)pk2);
|
||||
}
|
||||
|
||||
int secp256k1_xonly_sort(const secp256k1_context* ctx, const secp256k1_xonly_pubkey **pubkeys, size_t n_pubkeys) {
|
||||
secp256k1_xonly_sort_cmp_data cmp_data;
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pubkeys != NULL);
|
||||
|
||||
cmp_data.ctx = ctx;
|
||||
secp256k1_hsort(pubkeys, n_pubkeys, sizeof(*pubkeys), secp256k1_xonly_sort_cmp, &cmp_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -571,6 +571,152 @@ void test_keypair_add(void) {
|
||||
secp256k1_context_destroy(verify);
|
||||
}
|
||||
|
||||
static void test_hsort_is_sorted(int *ints, size_t n) {
|
||||
size_t i;
|
||||
for (i = 1; i < n; i++) {
|
||||
CHECK(ints[i-1] <= ints[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static int test_hsort_cmp(const void *i1, const void *i2, void *counter) {
|
||||
*(size_t*)counter += 1;
|
||||
return *(int*)i1 - *(int*)i2;
|
||||
}
|
||||
|
||||
#define NUM 64
|
||||
void test_hsort(void) {
|
||||
int ints[NUM] = { 0 };
|
||||
size_t counter = 0;
|
||||
int i, j;
|
||||
|
||||
secp256k1_hsort(ints, 0, sizeof(ints[0]), test_hsort_cmp, &counter);
|
||||
CHECK(counter == 0);
|
||||
secp256k1_hsort(ints, 1, sizeof(ints[0]), test_hsort_cmp, &counter);
|
||||
CHECK(counter == 0);
|
||||
secp256k1_hsort(ints, NUM, sizeof(ints[0]), test_hsort_cmp, &counter);
|
||||
CHECK(counter > 0);
|
||||
test_hsort_is_sorted(ints, NUM);
|
||||
|
||||
/* Test hsort with length n array and random elements in
|
||||
* [-interval/2, interval/2] */
|
||||
for (i = 0; i < count; i++) {
|
||||
int n = secp256k1_testrand_int(NUM);
|
||||
int interval = secp256k1_testrand_int(64);
|
||||
for (j = 0; j < n; j++) {
|
||||
ints[j] = secp256k1_testrand_int(interval) - interval/2;
|
||||
}
|
||||
secp256k1_hsort(ints, n, sizeof(ints[0]), test_hsort_cmp, &counter);
|
||||
test_hsort_is_sorted(ints, n);
|
||||
}
|
||||
}
|
||||
#undef NUM
|
||||
|
||||
void test_xonly_sort_helper(secp256k1_xonly_pubkey *pk, size_t *pk_order, size_t n_pk) {
|
||||
size_t i;
|
||||
const secp256k1_xonly_pubkey *pk_test[5];
|
||||
|
||||
for (i = 0; i < n_pk; i++) {
|
||||
pk_test[i] = &pk[pk_order[i]];
|
||||
}
|
||||
secp256k1_xonly_sort(ctx, pk_test, n_pk);
|
||||
for (i = 0; i < n_pk; i++) {
|
||||
CHECK(secp256k1_memcmp_var(pk_test[i], &pk[i], sizeof(*pk_test[i])) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
void permute(size_t *arr, size_t n) {
|
||||
size_t i;
|
||||
for (i = n - 1; i >= 1; i--) {
|
||||
size_t tmp, j;
|
||||
j = secp256k1_testrand_int(i + 1);
|
||||
tmp = arr[i];
|
||||
arr[i] = arr[j];
|
||||
arr[j] = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
void rand_xonly_pk(secp256k1_xonly_pubkey *pk) {
|
||||
unsigned char seckey[32];
|
||||
secp256k1_keypair keypair;
|
||||
secp256k1_testrand256(seckey);
|
||||
CHECK(secp256k1_keypair_create(ctx, &keypair, seckey) == 1);
|
||||
CHECK(secp256k1_keypair_xonly_pub(ctx, pk, NULL, &keypair) == 1);
|
||||
}
|
||||
|
||||
void test_xonly_sort_api(void) {
|
||||
int ecount = 0;
|
||||
secp256k1_xonly_pubkey pks[2];
|
||||
const secp256k1_xonly_pubkey *pks_ptr[2];
|
||||
secp256k1_context *none = api_test_context(SECP256K1_CONTEXT_NONE, &ecount);
|
||||
|
||||
pks_ptr[0] = &pks[0];
|
||||
pks_ptr[1] = &pks[1];
|
||||
|
||||
rand_xonly_pk(&pks[0]);
|
||||
rand_xonly_pk(&pks[1]);
|
||||
|
||||
CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1);
|
||||
CHECK(secp256k1_xonly_sort(none, NULL, 2) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_xonly_sort(none, pks_ptr, 0) == 1);
|
||||
/* Test illegal public keys */
|
||||
memset(&pks[0], 0, sizeof(pks[0]));
|
||||
CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1);
|
||||
CHECK(ecount == 2);
|
||||
memset(&pks[1], 0, sizeof(pks[1]));
|
||||
CHECK(secp256k1_xonly_sort(none, pks_ptr, 2) == 1);
|
||||
CHECK(ecount > 2);
|
||||
|
||||
secp256k1_context_destroy(none);
|
||||
}
|
||||
|
||||
void test_xonly_sort(void) {
|
||||
secp256k1_xonly_pubkey pk[5];
|
||||
unsigned char pk_ser[5][32];
|
||||
int i;
|
||||
size_t pk_order[5] = { 0, 1, 2, 3, 4 };
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
memset(pk_ser[i], 0, sizeof(pk_ser[i]));
|
||||
}
|
||||
pk_ser[0][0] = 5;
|
||||
pk_ser[1][0] = 8;
|
||||
pk_ser[2][0] = 0x0a;
|
||||
pk_ser[3][0] = 0x0b;
|
||||
pk_ser[4][0] = 0x0c;
|
||||
for (i = 0; i < 5; i++) {
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i]));
|
||||
}
|
||||
|
||||
permute(pk_order, 1);
|
||||
test_xonly_sort_helper(pk, pk_order, 1);
|
||||
permute(pk_order, 2);
|
||||
test_xonly_sort_helper(pk, pk_order, 2);
|
||||
permute(pk_order, 3);
|
||||
test_xonly_sort_helper(pk, pk_order, 3);
|
||||
for (i = 0; i < count; i++) {
|
||||
permute(pk_order, 4);
|
||||
test_xonly_sort_helper(pk, pk_order, 4);
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
permute(pk_order, 5);
|
||||
test_xonly_sort_helper(pk, pk_order, 5);
|
||||
}
|
||||
/* Check that sorting also works for random pubkeys */
|
||||
for (i = 0; i < count; i++) {
|
||||
int j;
|
||||
const secp256k1_xonly_pubkey *pk_ptr[5];
|
||||
for (j = 0; j < 5; j++) {
|
||||
rand_xonly_pk(&pk[j]);
|
||||
pk_ptr[j] = &pk[j];
|
||||
}
|
||||
secp256k1_xonly_sort(ctx, pk_ptr, 5);
|
||||
for (j = 1; j < 5; j++) {
|
||||
CHECK(secp256k1_xonly_sort_cmp(&pk_ptr[j - 1], &pk_ptr[j], ctx) <= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run_extrakeys_tests(void) {
|
||||
/* xonly key test cases */
|
||||
test_xonly_pubkey();
|
||||
@ -582,6 +728,10 @@ void run_extrakeys_tests(void) {
|
||||
/* keypair tests */
|
||||
test_keypair();
|
||||
test_keypair_add();
|
||||
|
||||
test_hsort();
|
||||
test_xonly_sort_api();
|
||||
test_xonly_sort();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -40,7 +40,7 @@ int create_keypair(const secp256k1_context* ctx, unsigned char *seckey, secp256k
|
||||
}
|
||||
|
||||
/* Sign a message hash with the given key pairs and store the result in sig */
|
||||
int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey* pubkeys, const unsigned char* msg32, unsigned char *sig64) {
|
||||
int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_xonly_pubkey** pubkeys, const unsigned char* msg32, unsigned char *sig64) {
|
||||
secp256k1_musig_session musig_session[N_SIGNERS];
|
||||
unsigned char nonce_commitment[N_SIGNERS][32];
|
||||
const unsigned char *nonce_commitment_ptr[N_SIGNERS];
|
||||
@ -72,7 +72,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
|
||||
}
|
||||
fclose(frand);
|
||||
/* Initialize session */
|
||||
if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, i, seckeys[i])) {
|
||||
if (!secp256k1_musig_session_init(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, &pre_session, N_SIGNERS, seckeys[i])) {
|
||||
return 0;
|
||||
}
|
||||
nonce_commitment_ptr[i] = &nonce_commitment[i][0];
|
||||
@ -117,7 +117,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
|
||||
* fine to first verify the combined sig, and only verify the individual
|
||||
* sigs if it does not work.
|
||||
*/
|
||||
if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], &pubkeys[j])) {
|
||||
if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], pubkeys[j])) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -130,6 +130,7 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
|
||||
int i;
|
||||
unsigned char seckeys[N_SIGNERS][32];
|
||||
secp256k1_xonly_pubkey pubkeys[N_SIGNERS];
|
||||
const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!";
|
||||
unsigned char sig[64];
|
||||
@ -142,16 +143,17 @@ int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp25
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
pubkeys_ptr[i] = &pubkeys[i];
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Combining public keys...");
|
||||
if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys, N_SIGNERS)) {
|
||||
if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys_ptr, N_SIGNERS)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
printf("ok\n");
|
||||
printf("Signing message.........");
|
||||
if (!sign(ctx, seckeys, pubkeys, msg, sig)) {
|
||||
if (!sign(ctx, seckeys, pubkeys_ptr, msg, sig)) {
|
||||
printf("FAILED\n");
|
||||
return 1;
|
||||
}
|
||||
|
@ -13,14 +13,14 @@
|
||||
#include "hash.h"
|
||||
|
||||
/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */
|
||||
static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey *pk, size_t np) {
|
||||
static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_xonly_pubkey * const* pk, size_t np) {
|
||||
secp256k1_sha256 sha;
|
||||
size_t i;
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
for (i = 0; i < np; i++) {
|
||||
unsigned char ser[32];
|
||||
if (!secp256k1_xonly_pubkey_serialize(ctx, ser, &pk[i])) {
|
||||
if (!secp256k1_xonly_pubkey_serialize(ctx, ser, pk[i])) {
|
||||
return 0;
|
||||
}
|
||||
secp256k1_sha256_write(&sha, ser, 32);
|
||||
@ -30,79 +30,82 @@ static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned ch
|
||||
}
|
||||
|
||||
/* Initializes SHA256 with fixed midstate. This midstate was computed by applying
|
||||
* SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */
|
||||
* SHA256 to SHA256("KeyAgg coefficient")||SHA256("KeyAgg coefficient"). */
|
||||
static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) {
|
||||
secp256k1_sha256_initialize(sha);
|
||||
|
||||
sha->s[0] = 0x0fd0690cul;
|
||||
sha->s[1] = 0xfefeae97ul;
|
||||
sha->s[2] = 0x996eac7ful;
|
||||
sha->s[3] = 0x5c30d864ul;
|
||||
sha->s[4] = 0x8c4a0573ul;
|
||||
sha->s[5] = 0xaca1a22ful;
|
||||
sha->s[6] = 0x6f43b801ul;
|
||||
sha->s[7] = 0x85ce27cdul;
|
||||
sha->s[0] = 0x6ef02c5aul;
|
||||
sha->s[1] = 0x06a480deul;
|
||||
sha->s[2] = 0x1f298665ul;
|
||||
sha->s[3] = 0x1d1134f2ul;
|
||||
sha->s[4] = 0x56a0b063ul;
|
||||
sha->s[5] = 0x52da4147ul;
|
||||
sha->s[6] = 0xf280d9d4ul;
|
||||
sha->s[7] = 0x4484be15ul;
|
||||
sha->bytes = 64;
|
||||
}
|
||||
|
||||
/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */
|
||||
static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) {
|
||||
/* Compute KeyAgg coefficient which is constant 1 for the second pubkey and
|
||||
* SHA256(ell, x) otherwise. second_pk_x can be NULL in case there is no
|
||||
* second_pk. Assumes both field elements x and second_pk_x are normalized. */
|
||||
static void secp256k1_musig_keyaggcoef_internal(secp256k1_scalar *r, const unsigned char *ell, secp256k1_fe *x, const secp256k1_fe *second_pk_x) {
|
||||
secp256k1_sha256 sha;
|
||||
unsigned char buf[32];
|
||||
size_t i;
|
||||
|
||||
if (secp256k1_fe_cmp_var(x, second_pk_x) == 0) {
|
||||
secp256k1_scalar_set_int(r, 1);
|
||||
} else {
|
||||
secp256k1_musig_sha256_init_tagged(&sha);
|
||||
secp256k1_sha256_write(&sha, ell, 32);
|
||||
/* We're hashing the index of the signer instead of its public key as specified
|
||||
* in the MuSig paper. This reduces the total amount of data that needs to be
|
||||
* hashed.
|
||||
* Additionally, it prevents creating identical musig_coefficients for identical
|
||||
* public keys. A participant Bob could choose his public key to be the same as
|
||||
* Alice's, then replay Alice's messages (nonce and partial signature) to create
|
||||
* a valid partial signature. This is not a problem for MuSig per se, but could
|
||||
* result in subtle issues with protocols building on threshold signatures.
|
||||
* With the assumption that public keys are unique, hashing the index is
|
||||
* equivalent to hashing the public key. Because the public key can be
|
||||
* identified by the index given the ordered list of public keys (included in
|
||||
* ell), the index is just a different encoding of the public key.*/
|
||||
for (i = 0; i < sizeof(uint32_t); i++) {
|
||||
unsigned char c = idx;
|
||||
secp256k1_sha256_write(&sha, &c, 1);
|
||||
idx >>= 8;
|
||||
}
|
||||
secp256k1_fe_get_b32(buf, x);
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
secp256k1_scalar_set_b32(r, buf, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Assumes both field elements x and second_pk_x are normalized. */
|
||||
static void secp256k1_musig_keyaggcoef(secp256k1_scalar *r, const secp256k1_musig_pre_session *pre_session, secp256k1_fe *x) {
|
||||
secp256k1_fe second_pk_x;
|
||||
secp256k1_fe_set_b32(&second_pk_x, pre_session->second_pk);
|
||||
secp256k1_musig_keyaggcoef_internal(r, pre_session->pk_hash, x, &second_pk_x);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const secp256k1_context *ctx;
|
||||
unsigned char ell[32];
|
||||
const secp256k1_xonly_pubkey *pks;
|
||||
const secp256k1_xonly_pubkey * const* pks;
|
||||
secp256k1_fe second_pk_x;
|
||||
} secp256k1_musig_pubkey_combine_ecmult_data;
|
||||
|
||||
/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */
|
||||
static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) {
|
||||
secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data;
|
||||
secp256k1_musig_coefficient(sc, ctx->ell, idx);
|
||||
return secp256k1_xonly_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]);
|
||||
int ret;
|
||||
ret = secp256k1_xonly_pubkey_load(ctx->ctx, pt, ctx->pks[idx]);
|
||||
/* pubkey_load can't fail because the same pks have already been loaded (and
|
||||
* we test this) */
|
||||
VERIFY_CHECK(ret);
|
||||
secp256k1_musig_keyaggcoef_internal(sc, ctx->ell, &pt->x, &ctx->second_pk_x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) {
|
||||
uint32_t i;
|
||||
for (i = 0; i < n_signers; i++) {
|
||||
memset(&signers[i], 0, sizeof(signers[i]));
|
||||
signers[i].index = i;
|
||||
signers[i].present = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const uint64_t pre_session_magic = 0xf4adbbdf7c7dd304UL;
|
||||
|
||||
int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey *pubkeys, size_t n_pubkeys) {
|
||||
int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_xonly_pubkey *combined_pk, secp256k1_musig_pre_session *pre_session, const secp256k1_xonly_pubkey * const* pubkeys, size_t n_pubkeys) {
|
||||
secp256k1_musig_pubkey_combine_ecmult_data ecmult_data;
|
||||
secp256k1_gej pkj;
|
||||
secp256k1_ge pkp;
|
||||
int pk_parity;
|
||||
size_t i;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(combined_pk != NULL);
|
||||
@ -112,14 +115,28 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat
|
||||
|
||||
ecmult_data.ctx = ctx;
|
||||
ecmult_data.pks = pubkeys;
|
||||
/* No point on the curve has an X coordinate equal to 0 */
|
||||
secp256k1_fe_set_int(&ecmult_data.second_pk_x, 0);
|
||||
for (i = 1; i < n_pubkeys; i++) {
|
||||
secp256k1_ge pt;
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pt, pubkeys[i])) {
|
||||
return 0;
|
||||
}
|
||||
if (secp256k1_memcmp_var(pubkeys[0], pubkeys[i], sizeof(*pubkeys[0])) != 0) {
|
||||
ecmult_data.second_pk_x = pt.x;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) {
|
||||
return 0;
|
||||
}
|
||||
if (!secp256k1_ecmult_multi_var(&ctx->error_callback, &ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) {
|
||||
/* The current implementation of ecmult_multi_var makes this code unreachable with tests. */
|
||||
return 0;
|
||||
}
|
||||
secp256k1_ge_set_gej(&pkp, &pkj);
|
||||
secp256k1_fe_normalize(&pkp.y);
|
||||
secp256k1_fe_normalize_var(&pkp.y);
|
||||
pk_parity = secp256k1_extrakeys_ge_even_y(&pkp);
|
||||
secp256k1_xonly_pubkey_save(combined_pk, &pkp);
|
||||
|
||||
@ -128,12 +145,14 @@ int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scrat
|
||||
memcpy(pre_session->pk_hash, ecmult_data.ell, 32);
|
||||
pre_session->pk_parity = pk_parity;
|
||||
pre_session->is_tweaked = 0;
|
||||
secp256k1_fe_get_b32(pre_session->second_pk, &ecmult_data.second_pk_x);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_musig_pre_session *pre_session, secp256k1_pubkey *output_pubkey, const secp256k1_xonly_pubkey *internal_pubkey, const unsigned char *tweak32) {
|
||||
secp256k1_ge pk;
|
||||
int ret;
|
||||
|
||||
VERIFY_CHECK(ctx != NULL);
|
||||
ARG_CHECK(pre_session != NULL);
|
||||
@ -150,16 +169,17 @@ int secp256k1_musig_pubkey_tweak_add(const secp256k1_context* ctx, secp256k1_mus
|
||||
memcpy(pre_session->tweak, tweak32, 32);
|
||||
pre_session->is_tweaked = 1;
|
||||
|
||||
if (!secp256k1_pubkey_load(ctx, &pk, output_pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
ret = secp256k1_pubkey_load(ctx, &pk, output_pubkey);
|
||||
/* Successful xonly_pubkey_tweak_add always returns valid output_pubkey */
|
||||
VERIFY_CHECK(ret);
|
||||
|
||||
pre_session->pk_parity = secp256k1_extrakeys_ge_even_y(&pk);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const uint64_t session_magic = 0xd92e6fc1ee41b4cbUL;
|
||||
|
||||
int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, size_t my_index, const unsigned char *seckey) {
|
||||
int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_xonly_pubkey *combined_pk, const secp256k1_musig_pre_session *pre_session, size_t n_signers, const unsigned char *seckey) {
|
||||
unsigned char combined_ser[32];
|
||||
int overflow;
|
||||
secp256k1_scalar secret;
|
||||
@ -183,7 +203,6 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s
|
||||
|
||||
ARG_CHECK(n_signers > 0);
|
||||
ARG_CHECK(n_signers <= UINT32_MAX);
|
||||
ARG_CHECK(my_index < n_signers);
|
||||
|
||||
memset(session, 0, sizeof(*session));
|
||||
|
||||
@ -206,7 +225,11 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s
|
||||
secp256k1_scalar_clear(&secret);
|
||||
return 0;
|
||||
}
|
||||
secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, (uint32_t) my_index);
|
||||
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret);
|
||||
secp256k1_ge_set_gej(&p, &pj);
|
||||
secp256k1_fe_normalize_var(&p.x);
|
||||
secp256k1_musig_keyaggcoef(&mu, &session->pre_session, &p.x);
|
||||
/* Compute the signer's public key point and determine if the secret is
|
||||
* negated before signing. That happens if if the signer's pubkey has an odd
|
||||
* Y coordinate XOR the MuSig-combined pubkey has an odd Y coordinate XOR
|
||||
@ -215,7 +238,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s
|
||||
* This can be seen by looking at the secret key belonging to `combined_pk`.
|
||||
* Let's define
|
||||
* P' := mu_0*|P_0| + ... + mu_n*|P_n| where P_i is the i-th public key
|
||||
* point x_i*G, mu_i is the i-th musig coefficient and |.| is a function
|
||||
* point x_i*G, mu_i is the i-th KeyAgg coefficient and |.| is a function
|
||||
* that normalizes a point to an even Y by negating if necessary similar to
|
||||
* secp256k1_extrakeys_ge_even_y. Then we have
|
||||
* P := |P'| + t*G where t is the tweak.
|
||||
@ -226,9 +249,7 @@ int secp256k1_musig_session_init(const secp256k1_context* ctx, secp256k1_musig_s
|
||||
* b_i = -1 if (P_i != |P_i| XOR P' != |P'| XOR P != |P|) and 1
|
||||
* otherwise.
|
||||
*/
|
||||
secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pj, &secret);
|
||||
secp256k1_ge_set_gej(&p, &pj);
|
||||
secp256k1_fe_normalize(&p.y);
|
||||
secp256k1_fe_normalize_var(&p.y);
|
||||
if((secp256k1_fe_is_odd(&p.y)
|
||||
+ session->pre_session.pk_parity
|
||||
+ (session->pre_session.is_tweaked
|
||||
@ -596,10 +617,13 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
|
||||
secp256k1_musig_compute_messagehash(ctx, msghash, session);
|
||||
secp256k1_scalar_set_b32(&e, msghash, NULL);
|
||||
|
||||
/* Multiplying the messagehash by the musig coefficient is equivalent
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
/* Multiplying the messagehash by the KeyAgg coefficient is equivalent
|
||||
* to multiplying the signer's public key by the coefficient, except
|
||||
* much easier to do. */
|
||||
secp256k1_musig_coefficient(&mu, session->pre_session.pk_hash, signer->index);
|
||||
secp256k1_musig_keyaggcoef(&mu, &session->pre_session, &pkp.x);
|
||||
secp256k1_scalar_mul(&e, &e, &mu);
|
||||
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &rp, &signer->nonce)) {
|
||||
@ -619,9 +643,7 @@ int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp2
|
||||
|
||||
/* Compute rj = s*G + (-e)*pkj */
|
||||
secp256k1_scalar_negate(&e, &e);
|
||||
if (!secp256k1_xonly_pubkey_load(ctx, &pkp, pubkey)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
secp256k1_gej_set_ge(&pkj, &pkp);
|
||||
secp256k1_ecmult(&ctx->ecmult_ctx, &rj, &pkj, &e, &s);
|
||||
|
||||
|
102
src/modules/musig/musig-spec.mediawiki
Normal file
102
src/modules/musig/musig-spec.mediawiki
Normal file
@ -0,0 +1,102 @@
|
||||
<pre>
|
||||
Title: MuSig Key Aggregation
|
||||
Author:
|
||||
Status: Draft
|
||||
License: BSD-2-Clause
|
||||
Created: 2020-01-19
|
||||
</pre>
|
||||
|
||||
== Introduction ==
|
||||
|
||||
=== Abstract ===
|
||||
|
||||
This document describes MuSig Key Aggregation in libsecp256k1-zkp.
|
||||
|
||||
=== Copyright ===
|
||||
|
||||
This document is licensed under the 2-clause BSD license.
|
||||
|
||||
=== Motivation ===
|
||||
|
||||
== Description ==
|
||||
|
||||
=== Design ===
|
||||
|
||||
* A function for sorting public keys allows to aggregate keys independent of the (initial) order.
|
||||
* The KeyAgg coefficient is computed by hashing the key instead of key index. Otherwise, if the pubkey list gets sorted, the signer needs to translate between key indices pre- and post-sorting.
|
||||
* The second unique key in the pubkey list gets the constant KeyAgg coefficient 1 which saves an exponentiation (see the MuSig2* appendix in the [https://eprint.iacr.org/2020/1261 MuSig2 paper]).
|
||||
|
||||
|
||||
=== Specification ===
|
||||
|
||||
The following conventions are used, with constants as defined for [https://www.secg.org/sec2-v2.pdf secp256k1]. We note that adapting this specification to other elliptic curves is not straightforward and can result in an insecure scheme<ref>Among other pitfalls, using the specification with a curve whose order is not close to the size of the range of the nonce derivation function is insecure.</ref>.
|
||||
* Lowercase variables represent integers or byte arrays.
|
||||
** The constant ''p'' refers to the field size, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F''.
|
||||
** The constant ''n'' refers to the curve order, ''0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141''.
|
||||
* Uppercase variables refer to points on the curve with equation ''y<sup>2</sup> = x<sup>3</sup> + 7'' over the integers modulo ''p''.
|
||||
** ''is_infinite(P)'' returns whether or not ''P'' is the point at infinity.
|
||||
** ''x(P)'' and ''y(P)'' are integers in the range ''0..p-1'' and refer to the X and Y coordinates of a point ''P'' (assuming it is not infinity).
|
||||
** The constant ''G'' refers to the base point, for which ''x(G) = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798'' and ''y(G) = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8''.
|
||||
** Addition of points refers to the usual [https://en.wikipedia.org/wiki/Elliptic_curve#The_group_law elliptic curve group operation].
|
||||
** [https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication Multiplication (⋅) of an integer and a point] refers to the repeated application of the group operation.
|
||||
* Functions and operations:
|
||||
** ''||'' refers to byte array concatenation.
|
||||
** The function ''x[i:j]'', where ''x'' is a byte array and ''i, j ≥ 0'', returns a ''(j - i)''-byte array with a copy of the ''i''-th byte (inclusive) to the ''j''-th byte (exclusive) of ''x''.
|
||||
** The function ''bytes(x)'', where ''x'' is an integer, returns the 32-byte encoding of ''x'', most significant byte first.
|
||||
** The function ''bytes(P)'', where ''P'' is a point, returns ''bytes(x(P))''.
|
||||
** The function ''int(x)'', where ''x'' is a 32-byte array, returns the 256-bit unsigned integer whose most significant byte first encoding is ''x''.
|
||||
** The function ''has_even_y(P)'', where ''P'' is a point for which ''not is_infinite(P)'', returns ''y(P) mod 2 = 0''.
|
||||
** The function ''lift_x(x)'', where ''x'' is an integer in range ''0..p-1'', returns the point ''P'' for which ''x(P) = x''<ref>
|
||||
Given a candidate X coordinate ''x'' in the range ''0..p-1'', there exist either exactly two or exactly zero valid Y coordinates. If no valid Y coordinate exists, then ''x'' is not a valid X coordinate either, i.e., no point ''P'' exists for which ''x(P) = x''. The valid Y coordinates for a given candidate ''x'' are the square roots of ''c = x<sup>3</sup> + 7 mod p'' and they can be computed as ''y = ±c<sup>(p+1)/4</sup> mod p'' (see [https://en.wikipedia.org/wiki/Quadratic_residue#Prime_or_prime_power_modulus Quadratic residue]) if they exist, which can be checked by squaring and comparing with ''c''.</ref> and ''has_even_y(P)'', or fails if no such point exists. The function ''lift_x(x)'' is equivalent to the following pseudocode:
|
||||
*** Let ''c = x<sup>3</sup> + 7 mod p''.
|
||||
*** Let ''y = c<sup>(p+1)/4</sup> mod p''.
|
||||
*** Fail if ''c ≠ y<sup>2</sup> mod p''.
|
||||
*** Return the unique point ''P'' such that ''x(P) = x'' and ''y(P) = y'' if ''y mod 2 = 0'' or ''y(P) = p-y'' otherwise.
|
||||
** The function ''hash<sub>tag</sub>(x)'' where ''tag'' is a UTF-8 encoded tag name and ''x'' is a byte array returns the 32-byte hash ''SHA256(SHA256(tag) || SHA256(tag) || x)''.
|
||||
|
||||
|
||||
==== Key Sorting ====
|
||||
|
||||
Input:
|
||||
* The number ''u'' of signatures with ''0 < u < 2^32''
|
||||
* The public keys ''pk<sub>1..u</sub>'': ''u'' 32-byte arrays
|
||||
|
||||
The algorithm ''KeySort(pk<sub>1..u</sub>)'' is defined as:
|
||||
* Return ''pk<sub>1..u</sub>'' sorted in lexicographical order.
|
||||
|
||||
==== Key Aggregation ====
|
||||
|
||||
Input:
|
||||
* The number ''u'' of signatures with ''0 < u < 2^32''
|
||||
* The public keys ''pk<sub>1..u</sub>'': ''u'' 32-byte arrays
|
||||
|
||||
The algorithm ''KeyAgg(pk<sub>1..u</sub>)'' is defined as:
|
||||
* For ''i = 1 .. u'':
|
||||
** Let ''a<sub>i</sub> = KeyAggCoeff(pk<sub>1..u</sub>, i)''.
|
||||
** Let ''P<sub>i</sub> = lift_x(int(pk<sub>i</sub>))''; fail if it fails.
|
||||
* Let ''S = a<sub>1</sub>⋅P<sub>1</sub> + a<sub>2</sub>⋅P<sub>1</sub> + ... + a<sub>u</sub>⋅P<sub>u</sub>''
|
||||
* Fail if ''is_infinite(S)''.
|
||||
* Return ''bytes(S)''.
|
||||
|
||||
The algorithm ''HashKeys(pk<sub>1..u</sub>)'' is defined as:
|
||||
* Return ''hash(pk<sub>1</sub> || pk<sub>2</sub> || ... || pk<sub>u</sub>)''
|
||||
|
||||
The algorithm ''IsSecond(pk<sub>1..u</sub>, i)'' is defined as:
|
||||
* For ''j = 1 .. u'':
|
||||
** If ''pk<sub>j</sub> ≠ pk<sub>1</sub>'':
|
||||
*** Return ''true'' if ''pk<sub>j</sub> = pk<sub>i</sub>'', otherwise return ''false''.
|
||||
* Return ''false''
|
||||
|
||||
The algorithm ''KeyAggCoeff(pk<sub>1..u</sub>, i)'' is defined as:
|
||||
* Let ''L = HashKeys(pk<sub>1..u</sub>)''.
|
||||
* Return 1 if ''IsSecond(pk<sub>1..u</sub>, i)'', otherwise return ''int(hash<sub>KeyAgg coefficient</sub>(L || pk) mod n''.
|
||||
|
||||
== Applications ==
|
||||
|
||||
== Test Vectors and Reference Code ==
|
||||
|
||||
== Footnotes ==
|
||||
|
||||
<references />
|
||||
|
||||
== Acknowledgements ==
|
@ -30,6 +30,7 @@ void musig_simple_test(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
unsigned char session_id[2][32];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
const secp256k1_xonly_pubkey *pk_ptr[2];
|
||||
const unsigned char *ncs[2];
|
||||
unsigned char public_nonce[3][32];
|
||||
secp256k1_musig_partial_signature partial_sig[2];
|
||||
@ -41,12 +42,14 @@ void musig_simple_test(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(msg);
|
||||
|
||||
pk_ptr[0] = &pk[0];
|
||||
pk_ptr[1] = &pk[1];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1);
|
||||
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
@ -98,11 +101,16 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
secp256k1_musig_pre_session pre_session_uninitialized;
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
const secp256k1_xonly_pubkey *pk_ptr[2];
|
||||
secp256k1_xonly_pubkey invalid_pk;
|
||||
const secp256k1_xonly_pubkey *invalid_pk_ptr2[2];
|
||||
const secp256k1_xonly_pubkey *invalid_pk_ptr3[3];
|
||||
unsigned char tweak[32];
|
||||
|
||||
unsigned char sec_adaptor[32];
|
||||
unsigned char sec_adaptor1[32];
|
||||
secp256k1_pubkey adaptor;
|
||||
int i;
|
||||
|
||||
/** setup **/
|
||||
secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
|
||||
@ -123,6 +131,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
|
||||
* structs. */
|
||||
memset(&pre_session_uninitialized, 0, sizeof(pre_session_uninitialized));
|
||||
memset(&session_uninitialized, 0, sizeof(session_uninitialized));
|
||||
memset(&invalid_pk, 0, sizeof(invalid_pk));
|
||||
|
||||
secp256k1_testrand256(session_id[0]);
|
||||
secp256k1_testrand256(session_id[1]);
|
||||
@ -132,43 +141,56 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_testrand256(sec_adaptor);
|
||||
secp256k1_testrand256(tweak);
|
||||
|
||||
pk_ptr[0] = &pk[0];
|
||||
pk_ptr[1] = &pk[1];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
invalid_pk_ptr2[i] = &invalid_pk;
|
||||
invalid_pk_ptr3[i] = &pk[i];
|
||||
}
|
||||
/* invalid_pk_ptr3 has two valid, one invalid pk, which is important to test
|
||||
* musig_pubkeys_combine */
|
||||
invalid_pk_ptr3[2] = &invalid_pk;
|
||||
|
||||
/** main test body **/
|
||||
|
||||
/* Key combination */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk, 2) == 0);
|
||||
CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk, 2) == 0);
|
||||
CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
CHECK(ecount == 2);
|
||||
/* pubkey_combine does not require a scratch space */
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
CHECK(ecount == 2);
|
||||
/* A small scratch space works too, but will result in using an ineffecient algorithm */
|
||||
scratch_small = secp256k1_scratch_space_create(ctx, 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
secp256k1_scratch_space_destroy(ctx, scratch_small);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk, 2) == 0);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, &pre_session, pk_ptr, 2) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk_ptr, 2) == 1);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 2) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 0) == 0);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, invalid_pk_ptr2, 2) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, invalid_pk_ptr3, 3) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 0) == 0);
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, NULL, 0) == 0);
|
||||
CHECK(ecount == 8);
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
|
||||
/** Tweaking */
|
||||
ecount = 0;
|
||||
@ -208,47 +230,47 @@ void musig_api_tests(secp256k1_scratch_space *scratch) {
|
||||
|
||||
/** Session creation **/
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 1);
|
||||
CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1);
|
||||
CHECK(ecount == 2);
|
||||
CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 3);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 4);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 5);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, &pre_session, 2, sk[0]) == 1);
|
||||
CHECK(ecount == 6);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, &pre_session, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 7);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 8);
|
||||
/* Uninitialized pre_session */
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session_uninitialized, 2, sk[0]) == 0);
|
||||
CHECK(ecount == 9);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 0, sk[0]) == 0);
|
||||
CHECK(ecount == 10);
|
||||
/* If more than UINT32_MAX fits in a size_t, test that session_init
|
||||
* rejects n_signers that high. */
|
||||
if (SIZE_MAX > UINT32_MAX) {
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, ((size_t) UINT32_MAX) + 2, sk[0]) == 0);
|
||||
CHECK(ecount == 11);
|
||||
} else {
|
||||
ecount = 11;
|
||||
}
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, NULL) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, NULL) == 0);
|
||||
CHECK(ecount == 12);
|
||||
/* secret key overflows */
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, ones) == 0);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, ones) == 0);
|
||||
CHECK(ecount == 12);
|
||||
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1);
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
|
||||
@ -517,6 +539,7 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25
|
||||
secp256k1_musig_session_signer_data signers_tmp[2];
|
||||
unsigned char sk_dummy[32];
|
||||
secp256k1_xonly_pubkey pks_tmp[2];
|
||||
const secp256k1_xonly_pubkey *pks_tmp_ptr[2];
|
||||
secp256k1_xonly_pubkey combined_pk_tmp;
|
||||
secp256k1_musig_pre_session pre_session_tmp;
|
||||
unsigned char nonce[32];
|
||||
@ -525,10 +548,12 @@ void musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp25
|
||||
secp256k1_testrand256(sk_dummy);
|
||||
pks_tmp[0] = pks[0];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pks_tmp[1], sk_dummy) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, 1, sk_dummy) == 1);
|
||||
pks_tmp_ptr[0] = &pks_tmp[0];
|
||||
pks_tmp_ptr[1] = &pks_tmp[1];
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, &pre_session_tmp, pks_tmp_ptr, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, &pre_session_tmp, 2, sk_dummy) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 0, sk) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1);
|
||||
CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0);
|
||||
/* Call get_public_nonce with different signers than the signers the session was
|
||||
* initialized with. */
|
||||
@ -557,7 +582,7 @@ int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_xonly_pubkey *
|
||||
|
||||
/* Initialize new signers */
|
||||
secp256k1_testrand256(session_id);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, 1, sk) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pre_session, 2, sk) == 1);
|
||||
ncs[0] = nonce_commitment_other;
|
||||
ncs[1] = nonce_commitment;
|
||||
CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, nonce, ncs, 2, NULL) == 1);
|
||||
@ -589,7 +614,7 @@ void musig_state_machine_late_msg_test(secp256k1_xonly_pubkey *pks, secp256k1_xo
|
||||
secp256k1_musig_partial_signature partial_sig;
|
||||
|
||||
secp256k1_context_set_illegal_callback(ctx_tmp, counting_illegal_callback_fn, &ecount);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, 1, sk) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session, signers, nonce_commitment, session_id, NULL, combined_pk, pre_session, 2, sk) == 1);
|
||||
ncs[0] = nonce_commitment_other;
|
||||
ncs[1] = nonce_commitment;
|
||||
|
||||
@ -625,6 +650,7 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
|
||||
unsigned char msg[32];
|
||||
unsigned char sk[2][32];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
const secp256k1_xonly_pubkey *pk_ptr[2];
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
unsigned char nonce[2][32];
|
||||
@ -647,11 +673,13 @@ void musig_state_machine_tests(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_testrand256(sk[0]);
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(msg);
|
||||
pk_ptr[0] = &pk[0];
|
||||
pk_ptr[1] = &pk[1];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, 0, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, 1, sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, &pre_session, 2, sk[0]) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, &pre_session, 2, sk[1]) == 1);
|
||||
/* Can't combine nonces unless we're through round 1 already */
|
||||
ecount = 0;
|
||||
CHECK(secp256k1_musig_session_combine_nonces(ctx_tmp, &session[0], signers0, 2, NULL, NULL) == 0);
|
||||
@ -736,7 +764,9 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
|
||||
unsigned char seckey_a[2][32];
|
||||
unsigned char seckey_b[2][32];
|
||||
secp256k1_xonly_pubkey pk_a[2];
|
||||
const secp256k1_xonly_pubkey *pk_a_ptr[2];
|
||||
secp256k1_xonly_pubkey pk_b[2];
|
||||
const secp256k1_xonly_pubkey *pk_b_ptr[2];
|
||||
secp256k1_musig_pre_session pre_session_a;
|
||||
secp256k1_musig_pre_session pre_session_b;
|
||||
secp256k1_xonly_pubkey combined_pk_a;
|
||||
@ -765,22 +795,26 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_testrand256(seckey_b[1]);
|
||||
secp256k1_testrand256(sec_adaptor);
|
||||
|
||||
pk_a_ptr[0] = &pk_a[0];
|
||||
pk_a_ptr[1] = &pk_a[1];
|
||||
pk_b_ptr[0] = &pk_b[0];
|
||||
pk_b_ptr[1] = &pk_b[1];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_a[0], seckey_a[0]));
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_a[1], seckey_a[1]));
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_b[0], seckey_b[0]));
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk_b[1], seckey_b[1]));
|
||||
CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor));
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a, 2));
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b, 2));
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, &pre_session_a, pk_a_ptr, 2));
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, &pre_session_b, pk_b_ptr, 2));
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 0, seckey_a[0]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, 1, seckey_a[1]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[0]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, &pre_session_a, 2, seckey_a[1]));
|
||||
noncommit_a_ptr[0] = noncommit_a[0];
|
||||
noncommit_a_ptr[1] = noncommit_a[1];
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 0, seckey_b[0]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, 1, seckey_b[1]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, seckey_b[0]));
|
||||
CHECK(secp256k1_musig_session_init(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, &pre_session_b, 2, seckey_b[1]));
|
||||
noncommit_b_ptr[0] = noncommit_b[0];
|
||||
noncommit_b_ptr[1] = noncommit_b[1];
|
||||
|
||||
@ -829,7 +863,7 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) {
|
||||
/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the
|
||||
* expected state. */
|
||||
void sha256_tag_test(void) {
|
||||
char tag[17] = "MuSig coefficient";
|
||||
char tag[18] = "KeyAgg coefficient";
|
||||
secp256k1_sha256 sha;
|
||||
secp256k1_sha256 sha_tagged;
|
||||
unsigned char buf[32];
|
||||
@ -837,9 +871,9 @@ void sha256_tag_test(void) {
|
||||
size_t i;
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, (unsigned char *) tag, 17);
|
||||
secp256k1_sha256_write(&sha, (unsigned char *) tag, sizeof(tag));
|
||||
secp256k1_sha256_finalize(&sha, buf);
|
||||
/* buf = SHA256("MuSig coefficient") */
|
||||
/* buf = SHA256("KeyAgg coefficient") */
|
||||
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
secp256k1_sha256_write(&sha, buf, 32);
|
||||
@ -881,8 +915,8 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, cons
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk0) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk1) == 1);
|
||||
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, 0, sk0) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, 1, sk1) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, combined_pubkey, pre_session, 2, sk0) == 1);
|
||||
CHECK(secp256k1_musig_session_init(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, combined_pubkey, pre_session, 2, sk1) == 1);
|
||||
/* Set nonce commitments */
|
||||
ncs[0] = nonce_commitment[0];
|
||||
ncs[1] = nonce_commitment[1];
|
||||
@ -909,6 +943,7 @@ void musig_tweak_test_helper(const secp256k1_xonly_pubkey* combined_pubkey, cons
|
||||
void musig_tweak_test(secp256k1_scratch_space *scratch) {
|
||||
unsigned char sk[2][32];
|
||||
secp256k1_xonly_pubkey pk[2];
|
||||
const secp256k1_xonly_pubkey *pk_ptr[2];
|
||||
secp256k1_musig_pre_session pre_session_P;
|
||||
secp256k1_musig_pre_session pre_session_Q;
|
||||
secp256k1_xonly_pubkey P;
|
||||
@ -927,9 +962,11 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) {
|
||||
secp256k1_testrand256(sk[1]);
|
||||
secp256k1_testrand256(contract);
|
||||
|
||||
pk_ptr[0] = &pk[0];
|
||||
pk_ptr[1] = &pk[1];
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[0], sk[0]) == 1);
|
||||
CHECK(secp256k1_xonly_pubkey_create(&pk[1], sk[1]) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk, 2) == 1);
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &P, &pre_session_P, pk_ptr, 2) == 1);
|
||||
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, P_serialized, &P) == 1);
|
||||
secp256k1_sha256_initialize(&sha);
|
||||
@ -950,6 +987,145 @@ void musig_tweak_test(secp256k1_scratch_space *scratch) {
|
||||
musig_tweak_test_helper(&Q_xonly, sk[0], sk[1], &pre_session_Q);
|
||||
}
|
||||
|
||||
void musig_test_vectors_helper(unsigned char pk_ser[][32], int n_pks, const unsigned char *combined_pk_expected, int has_second_pk, int second_pk_idx) {
|
||||
secp256k1_xonly_pubkey *pk = malloc(n_pks * sizeof(*pk));
|
||||
const secp256k1_xonly_pubkey **pk_ptr = malloc(n_pks * sizeof(*pk_ptr));
|
||||
secp256k1_xonly_pubkey combined_pk;
|
||||
unsigned char combined_pk_ser[32];
|
||||
secp256k1_musig_pre_session pre_session;
|
||||
secp256k1_fe second_pk_x;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n_pks; i++) {
|
||||
CHECK(secp256k1_xonly_pubkey_parse(ctx, &pk[i], pk_ser[i]));
|
||||
pk_ptr[i] = &pk[i];
|
||||
}
|
||||
|
||||
CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, &pre_session, pk_ptr, n_pks) == 1);
|
||||
CHECK(secp256k1_fe_set_b32(&second_pk_x, pre_session.second_pk));
|
||||
CHECK(secp256k1_fe_is_zero(&second_pk_x) == !has_second_pk);
|
||||
if (!secp256k1_fe_is_zero(&second_pk_x)) {
|
||||
CHECK(secp256k1_memcmp_var(&pk_ser[second_pk_idx], &pre_session.second_pk, sizeof(pk_ser[second_pk_idx])) == 0);
|
||||
}
|
||||
CHECK(secp256k1_xonly_pubkey_serialize(ctx, combined_pk_ser, &combined_pk));
|
||||
/* TODO: remove when test vectors are not expected to change anymore */
|
||||
/* int k, l; */
|
||||
/* printf("const unsigned char combined_pk_expected[32] = {\n"); */
|
||||
/* for (k = 0; k < 4; k++) { */
|
||||
/* printf(" "); */
|
||||
/* for (l = 0; l < 8; l++) { */
|
||||
/* printf("0x%02X, ", combined_pk_ser[k*8+l]); */
|
||||
/* } */
|
||||
/* printf("\n"); */
|
||||
/* } */
|
||||
/* printf("};\n"); */
|
||||
CHECK(secp256k1_memcmp_var(combined_pk_ser, combined_pk_expected, sizeof(combined_pk_ser)) == 0);
|
||||
free(pk);
|
||||
free(pk_ptr);
|
||||
}
|
||||
|
||||
void musig_test_vectors(void) {
|
||||
size_t i;
|
||||
unsigned char pk_ser_tmp[4][32];
|
||||
unsigned char pk_ser[3][32] = {
|
||||
/* X1 */
|
||||
{
|
||||
0xF9, 0x30, 0x8A, 0x01, 0x92, 0x58, 0xC3, 0x10,
|
||||
0x49, 0x34, 0x4F, 0x85, 0xF8, 0x9D, 0x52, 0x29,
|
||||
0xB5, 0x31, 0xC8, 0x45, 0x83, 0x6F, 0x99, 0xB0,
|
||||
0x86, 0x01, 0xF1, 0x13, 0xBC, 0xE0, 0x36, 0xF9
|
||||
},
|
||||
/* X2 */
|
||||
{
|
||||
0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, 0x5F,
|
||||
0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, 0xBE,
|
||||
0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, 0xD8,
|
||||
0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, 0x59
|
||||
},
|
||||
/* X3 */
|
||||
{
|
||||
0x35, 0x90, 0xA9, 0x4E, 0x76, 0x8F, 0x8E, 0x18,
|
||||
0x15, 0xC2, 0xF2, 0x4B, 0x4D, 0x80, 0xA8, 0xE3,
|
||||
0x14, 0x93, 0x16, 0xC3, 0x51, 0x8C, 0xE7, 0xB7,
|
||||
0xAD, 0x33, 0x83, 0x68, 0xD0, 0x38, 0xCA, 0x66
|
||||
}
|
||||
};
|
||||
const unsigned char combined_pk_expected[4][32] = {
|
||||
{ /* 0 */
|
||||
0xEA, 0x06, 0x7B, 0x01, 0x67, 0x24, 0x5A, 0x6F,
|
||||
0xED, 0xB1, 0xB1, 0x22, 0xBB, 0x03, 0xAB, 0x7E,
|
||||
0x5D, 0x48, 0x6C, 0x81, 0x83, 0x42, 0xE0, 0xE9,
|
||||
0xB6, 0x41, 0x79, 0xAD, 0x32, 0x8D, 0x9D, 0x19,
|
||||
},
|
||||
{ /* 1 */
|
||||
0x14, 0xE1, 0xF8, 0x3E, 0x9E, 0x25, 0x60, 0xFB,
|
||||
0x2A, 0x6C, 0x04, 0x24, 0x55, 0x6C, 0x86, 0x8D,
|
||||
0x9F, 0xB4, 0x63, 0x35, 0xD4, 0xF7, 0x8D, 0x22,
|
||||
0x7D, 0x5D, 0x1D, 0x3C, 0x89, 0x90, 0x6F, 0x1E,
|
||||
},
|
||||
{ /* 2 */
|
||||
0x70, 0x28, 0x8D, 0xF2, 0xB7, 0x60, 0x3D, 0xBE,
|
||||
0xA0, 0xC7, 0xB7, 0x41, 0xDD, 0xAA, 0xB9, 0x46,
|
||||
0x81, 0x14, 0x4E, 0x0B, 0x19, 0x08, 0x6C, 0x69,
|
||||
0xB2, 0x34, 0x89, 0xE4, 0xF5, 0xB7, 0x01, 0x9A,
|
||||
},
|
||||
{ /* 3 */
|
||||
0x93, 0xEE, 0xD8, 0x24, 0xF2, 0x3C, 0x5A, 0xE1,
|
||||
0xC1, 0x05, 0xE7, 0x31, 0x09, 0x97, 0x3F, 0xCD,
|
||||
0x4A, 0xE3, 0x3A, 0x9F, 0xA0, 0x2F, 0x0A, 0xC8,
|
||||
0x5A, 0x3E, 0x55, 0x89, 0x07, 0x53, 0xB0, 0x67,
|
||||
},
|
||||
};
|
||||
|
||||
for (i = 0; i < sizeof(combined_pk_expected)/sizeof(combined_pk_expected[0]); i++) {
|
||||
size_t n_pks;
|
||||
int has_second_pk;
|
||||
int second_pk_idx;
|
||||
switch (i) {
|
||||
case 0:
|
||||
/* [X1, X2, X3] */
|
||||
n_pks = 3;
|
||||
memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0]));
|
||||
memcpy(pk_ser_tmp[1], pk_ser[1], sizeof(pk_ser_tmp[1]));
|
||||
memcpy(pk_ser_tmp[2], pk_ser[2], sizeof(pk_ser_tmp[2]));
|
||||
has_second_pk = 1;
|
||||
second_pk_idx = 1;
|
||||
break;
|
||||
case 1:
|
||||
/* [X3, X2, X1] */
|
||||
n_pks = 3;
|
||||
memcpy(pk_ser_tmp[2], pk_ser[0], sizeof(pk_ser_tmp[0]));
|
||||
memcpy(pk_ser_tmp[1], pk_ser[1], sizeof(pk_ser_tmp[1]));
|
||||
memcpy(pk_ser_tmp[0], pk_ser[2], sizeof(pk_ser_tmp[2]));
|
||||
has_second_pk = 1;
|
||||
second_pk_idx = 1;
|
||||
break;
|
||||
case 2:
|
||||
/* [X1, X1, X1] */
|
||||
n_pks = 3;
|
||||
memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0]));
|
||||
memcpy(pk_ser_tmp[1], pk_ser[0], sizeof(pk_ser_tmp[1]));
|
||||
memcpy(pk_ser_tmp[2], pk_ser[0], sizeof(pk_ser_tmp[2]));
|
||||
has_second_pk = 0;
|
||||
second_pk_idx = 0; /* unchecked */
|
||||
break;
|
||||
case 3:
|
||||
/* [X1, X1, X2, X2] */
|
||||
n_pks = 4;
|
||||
memcpy(pk_ser_tmp[0], pk_ser[0], sizeof(pk_ser_tmp[0]));
|
||||
memcpy(pk_ser_tmp[1], pk_ser[0], sizeof(pk_ser_tmp[1]));
|
||||
memcpy(pk_ser_tmp[2], pk_ser[1], sizeof(pk_ser_tmp[2]));
|
||||
memcpy(pk_ser_tmp[3], pk_ser[1], sizeof(pk_ser_tmp[3]));
|
||||
has_second_pk = 1;
|
||||
second_pk_idx = 3;
|
||||
break;
|
||||
default:
|
||||
CHECK(0);
|
||||
}
|
||||
musig_test_vectors_helper(pk_ser_tmp, n_pks, combined_pk_expected[i], has_second_pk, second_pk_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void run_musig_tests(void) {
|
||||
int i;
|
||||
secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024);
|
||||
@ -965,6 +1141,7 @@ void run_musig_tests(void) {
|
||||
scriptless_atomic_swap(scratch);
|
||||
musig_tweak_test(scratch);
|
||||
}
|
||||
musig_test_vectors();
|
||||
sha256_tag_test();
|
||||
|
||||
secp256k1_scratch_space_destroy(ctx, scratch);
|
||||
|
Loading…
x
Reference in New Issue
Block a user