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:
Jonas Nick 2021-07-18 17:55:57 +00:00
commit 5d2df05419
No known key found for this signature in database
GPG Key ID: 4861DBF262123605
12 changed files with 760 additions and 123 deletions

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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

View 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

View 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

View File

@ -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);

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View 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 &ge; 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 = &plusmn;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 &ne; 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> &ne; 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 ==

View File

@ -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);