From 247316f7d204bfdfa2c0bd5b3e2e4d746b9b1329 Mon Sep 17 00:00:00 2001 From: Jonas Nick Date: Thu, 13 May 2021 09:03:00 +0000 Subject: [PATCH] musig: add nonces_combine function that adds up the received nonces This is useful for reducing the communcation by having a "combiner" as mentioned in the MuSig2 paper. --- include/secp256k1_musig.h | 25 +++++++++++++++ src/modules/musig/main_impl.h | 24 ++++++++++++++ src/modules/musig/tests_impl.h | 57 ++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) diff --git a/include/secp256k1_musig.h b/include/secp256k1_musig.h index cc50c5e8e..fd0e58d4b 100644 --- a/include/secp256k1_musig.h +++ b/include/secp256k1_musig.h @@ -205,6 +205,31 @@ SECP256K1_API int secp256k1_musig_session_init( const unsigned char *extra_input32 ) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); +/** Combines received nonces into a single nonce + * + * This is useful to reduce the communication between signers, because instead + * of everyone sending nonces to everyone else, there can be one party + * receiving all nonces, combining the nonces with this function and then + * sending only the combined nonce back to the signers. The pubnonces argument + * of secp256k1_musig_process_nonces then simply becomes an array whose sole + * element is a pointer to this combined nonce. + * + * Returns: 0 if the arguments are invalid or if all signers sent invalid + * pubnonces, 1 otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: combined_pubnonce66: a 66-byte array to store the combined pubnonce + * In: pubnonces: array of pointers to the 66-byte pubnonces sent + * by the signers + * n_pubnonces: number of elements in the pubnonces array. Must + * be greater than 0. + */ +SECP256K1_API int secp256k1_musig_nonces_combine( + const secp256k1_context* ctx, + unsigned char *combined_pubnonce66, + const unsigned char * const* pubnonces, + size_t n_pubnonces +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + /** Takes the public nonces of all signers and computes a session cache that is * required for signing and verification of partial signatures and a signature * template that is required for combining partial signatures. diff --git a/src/modules/musig/main_impl.h b/src/modules/musig/main_impl.h index 47a0c8a20..51ef9c15e 100644 --- a/src/modules/musig/main_impl.h +++ b/src/modules/musig/main_impl.h @@ -282,6 +282,30 @@ static void secp256k1_musig_sum_nonces(const secp256k1_context* ctx, secp256k1_g } } + +int secp256k1_musig_nonces_combine(const secp256k1_context* ctx, unsigned char *combined_pubnonce66, const unsigned char * const* pubnonces, size_t n_pubnonces) { + secp256k1_gej summed_nonces[2]; + int i; + + ARG_CHECK(combined_pubnonce66 != NULL); + ARG_CHECK(pubnonces != NULL); + ARG_CHECK(n_pubnonces > 0); + + secp256k1_musig_sum_nonces(ctx, summed_nonces, pubnonces, n_pubnonces); + for (i = 0; i < 2; i++) { + int ret; + secp256k1_ge noncep; + size_t len = 33; + if (secp256k1_gej_is_infinity(&summed_nonces[i])) { + return 0; + } + secp256k1_ge_set_gej(&noncep, &summed_nonces[i]); + ret = secp256k1_eckey_pubkey_serialize(&noncep, &combined_pubnonce66[i*33], &len, 1); + VERIFY_CHECK(ret); + } + return 1; +} + static void secp256k1_musig_session_cache_load(secp256k1_scalar *b, secp256k1_scalar *e, int *combined_nonce_parity, const secp256k1_musig_session_cache *cache) { secp256k1_scalar_set_b32(b, &cache->data[0], NULL); secp256k1_scalar_set_b32(e, &cache->data[32], NULL); diff --git a/src/modules/musig/tests_impl.h b/src/modules/musig/tests_impl.h index 551e796aa..db0c7edf3 100644 --- a/src/modules/musig/tests_impl.h +++ b/src/modules/musig/tests_impl.h @@ -110,6 +110,7 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { const unsigned char *invalid_pubnonce_ptr[1]; unsigned char inf_pubnonce[2][66]; const unsigned char *inf_pubnonce_ptr[2]; + unsigned char combined_pnonce[66]; unsigned char msg[32]; secp256k1_xonly_pubkey combined_pk; secp256k1_musig_pre_session pre_session; @@ -274,6 +275,19 @@ void musig_api_tests(secp256k1_scratch_space *scratch) { CHECK(secp256k1_musig_session_init(sign, &secnonce[1], pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1); /** Receive nonces **/ + ecount = 0; + CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, pubnonce_ptr, 2) == 1); + CHECK(secp256k1_musig_nonces_combine(none, NULL, pubnonce_ptr, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, NULL, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, pubnonce_ptr, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, invalid_pubnonce_ptr, 1) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_nonces_combine(none, combined_pnonce, inf_pubnonce_ptr, 2) == 0); + CHECK(ecount == 3); + ecount = 0; CHECK(secp256k1_musig_process_nonces(none, &session_cache, &sig_template, &nonce_parity, pubnonce_ptr, 2, msg, &combined_pk, &pre_session, &adaptor) == 0); CHECK(ecount == 1); @@ -626,6 +640,48 @@ void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { CHECK(secp256k1_schnorrsig_verify(ctx, final_sig_a, msg32_a, &combined_pk_a) == 1); } +void musig_combiner_test(secp256k1_scratch_space *scratch) { + unsigned char sk[2][32]; + secp256k1_keypair keypair[2]; + unsigned char pubnonce[2][66]; + const unsigned char *pubnonce_ptr[2]; + unsigned char combined_pnonce[66]; + const unsigned char *combined_pnonce_ptr[1]; + unsigned char msg[32]; + secp256k1_xonly_pubkey combined_pk; + secp256k1_musig_pre_session pre_session; + unsigned char session_id[2][32]; + secp256k1_musig_secnonce secnonce[2]; + secp256k1_xonly_pubkey pk[2]; + const secp256k1_xonly_pubkey *pk_ptr[2]; + int nonce_parity, nonce_parity2; + secp256k1_musig_template sig_template, sig_template2; + secp256k1_musig_session_cache session_cache, session_cache2; + int i; + + secp256k1_testrand256(msg); + for (i = 0; i < 2; i++) { + secp256k1_testrand256(session_id[i]); + secp256k1_testrand256(sk[i]); + pk_ptr[i] = &pk[i]; + pubnonce_ptr[i] = pubnonce[i]; + CHECK(create_keypair_and_pk(&keypair[i], &pk[i], sk[i])); + } + combined_pnonce_ptr[0] = combined_pnonce; + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, &pre_session, pk_ptr, 2) == 1); + + CHECK(secp256k1_musig_session_init(ctx, &secnonce[1], pubnonce[1], session_id[1], sk[1], NULL, NULL, NULL) == 1); + + CHECK(secp256k1_musig_process_nonces(ctx, &session_cache, &sig_template, &nonce_parity, pubnonce_ptr, 2, msg, &combined_pk, &pre_session, NULL) == 1); + /* Check that process_nonces on the result of nonces_combine gives the same result */ + CHECK(secp256k1_musig_nonces_combine(ctx, combined_pnonce, pubnonce_ptr, 2) == 1); + CHECK(secp256k1_musig_process_nonces(ctx, &session_cache2, &sig_template2, &nonce_parity2, combined_pnonce_ptr, 1, msg, &combined_pk, &pre_session, NULL) == 1); + CHECK(memcmp(&session_cache, &session_cache2, sizeof(session_cache)) == 0); + CHECK(memcmp(&sig_template, &sig_template2, sizeof(sig_template)) == 0); + CHECK(nonce_parity == nonce_parity2); +} + /* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the * expected state. */ void sha256_tag_test(void) { @@ -903,6 +959,7 @@ void run_musig_tests(void) { /* Run multiple times to ensure that pk and nonce have different y * parities */ scriptless_atomic_swap(scratch); + musig_combiner_test(scratch); musig_tweak_test(scratch); } musig_test_vectors();