Skip to content

Commit b220661

Browse files
Merge #131: Replace MuSig(1) module with MuSig2
ac1e367 musig: turn off multiexponentiation for now (Jonas Nick) 3c79d97 ci: increase timeout for macOS tasks (Jonas Nick) 22c8881 musig: replace MuSig(1) with MuSig2 (Jonas Nick) Pull request description: The main commit comprises `905 insertions(+), 1253 deletions(-)`. The diff isn't as small as I had hoped, but that's mostly because it was possible to simplify the API quite substantially which required rewriting large parts. Sorry, almost all of the changes are in one big commit which makes the diff very hard to read. Perhaps best to re-review most parts from scratch. A few key changes: - Obviously no commitment round. No big session struct and no `verifier` sessions. No `signer` struct. - There's a new `secnonce` struct that is the output of musig_nonce_gen and derived from a uniformly random session_id32. The derivation can be strengthened by adding whatever session parameters (combined_pk, msg) are available. The nonce function is my ad-hoc construction that allows for these optional inputs. Please have a look at that. - The secnonce is made invalid after being used in partial_sign. - Adaptor signatures basically work as before, according to BlockstreamResearch/scriptless-scripts#24 (with the exception that they operate on aggregate instead of partial sigs) - To avoid making this PR overly complex I did not consider how this implementation interacts with nested-MuSig, sign-to-contract, and antiklepto. - Testing should be close to complete. There's no reachable line or branch that isn't exercised by the tests. - [x] ~In the current implementation when a signer sends an invalid nonce (i.e. some garbage that can't be mapped to a group element), it is ignored when combining nonces. Only after receiving the signers partial signature and running `partial_sig_verify` will we notice that the signer misbehaved. The reason for this is that 1) this makes the API simpler and 2) malicious peers don't gain any additional powers because they can always interrupt the protocol by refusing to sign. However, this is up for discussion.~ EDIT: this is not the case anymore since invalid nonces are rejected when they're parsed. - [x] ~For every partial signature we verify we have to parse the pubnonce (two compressed points), despite having parsed it in `process_nonces` already. This is not great. `process_nonces` could optionally output the array of parsed pubnonces.~ EDIT: fixed by having a dedicated type for nonces. - [x] ~I left `src/modules/musig/musig.md` unchanged for now. Perhaps we should merge it with the `musig-spec`~ EDIT: musig.md is updated - [x] partial verification should use multiexp to compute `R1 + b*R2 + c*P`, but this can be done in a separate PR - [x] renaming wishlist - pre_session -> keyagg_cache (because there is no session anymore) - pubkey_combine, nonce_combine, partial_sig_combine -> pubkey_agg, nonce_agg, partial_sig_agg (shorter, matches terminology in musig2) - musig_session_init -> musig_start (shorter, simpler) or [musig_generate_nonce](#131 (comment)) or musig_prepare - musig_partial_signature to musig_partial_sig (shorter) - [x] perhaps remove pubnonces and n_pubnonces argument from process_nonces (and then also add a opaque type for the combined nonce?) - [x] write the `combined_pubkey` into the `pre_session` struct (as suggested [below](#131 (comment)): then 1) session_init and process_nonces don't need a combined_pk argument (and there can't be mix up between tweaked and untweaked keys) and 2) pubkey_tweak doesn't need an input_pubkey and the output_pubkey can be written directly into the pre_session (reducing frustration such as Replace MuSig(1) module with MuSig2 #131 (comment)) - [x] perhaps allow adapting both partial sigs (`partial_sig` struct) and aggregate partial sigs (64 raw bytes) as suggested [below](#131 (comment)). Based on #120. ACKs for top commit: robot-dreams: ACK ac1e367 real-or-random: ACK ac1e367 Tree-SHA512: 916b42811aa5c00649cfb923d2002422c338106a6936a01253ba693015a242f21f7f7b4cce60d5ab5764a129926c6fd6676977c69c9e6e0aedc51b308ac6578d
2 parents 6b87335 + ac1e367 commit b220661

16 files changed

+2910
-2275
lines changed

.cirrus.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ task:
119119
name: "x86_64: macOS Catalina"
120120
macos_instance:
121121
image: catalina-base
122-
# As of d4ca81f48e tasks with valgrind enabled take about 60 minutes
123-
timeout_in: 90m
122+
# tasks with valgrind enabled take about 90 minutes
123+
timeout_in: 120m
124124
env:
125125
HOMEBREW_NO_AUTO_UPDATE: 1
126126
HOMEBREW_NO_INSTALL_CLEANUP: 1

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,5 @@ build-aux/test-driver
6464
src/stamp-h1
6565
libsecp256k1.pc
6666
contrib/gh-pr-create.sh
67+
68+
example_musig

Makefile.am

+9
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ exhaustive_tests_LDFLAGS = -static
129129
TESTS += exhaustive_tests
130130
endif
131131

132+
if ENABLE_MODULE_MUSIG
133+
noinst_PROGRAMS += example_musig
134+
example_musig_SOURCES = examples/musig.c
135+
example_musig_CPPFLAGS = -I$(top_srcdir)/include
136+
example_musig_LDADD = libsecp256k1.la
137+
example_musig_LDFLAGS = -static
138+
TESTS += example_musig
139+
endif
140+
132141
EXTRA_PROGRAMS = gen_ecmult_static_pre_g
133142
gen_ecmult_static_pre_g_SOURCES = src/gen_ecmult_static_pre_g.c
134143
# See Automake manual, Section "Errors with distclean"

examples/musig.c

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/***********************************************************************
2+
* Copyright (c) 2018 Jonas Nick *
3+
* Distributed under the MIT software license, see the accompanying *
4+
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
5+
**********************************************************************/
6+
7+
/**
8+
* This file demonstrates how to use the MuSig module to create a multisignature.
9+
* Additionally, see the documentation in include/secp256k1_musig.h.
10+
*/
11+
12+
#include <stdio.h>
13+
#include <assert.h>
14+
#include <secp256k1.h>
15+
#include <secp256k1_schnorrsig.h>
16+
#include <secp256k1_musig.h>
17+
18+
struct signer_secrets {
19+
secp256k1_keypair keypair;
20+
secp256k1_musig_secnonce secnonce;
21+
};
22+
23+
struct signer {
24+
secp256k1_xonly_pubkey pubkey;
25+
secp256k1_musig_pubnonce pubnonce;
26+
secp256k1_musig_partial_sig partial_sig;
27+
};
28+
29+
/* Number of public keys involved in creating the aggregate signature */
30+
#define N_SIGNERS 3
31+
/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */
32+
int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
33+
unsigned char seckey[32];
34+
FILE *frand = fopen("/dev/urandom", "r");
35+
if (frand == NULL) {
36+
return 0;
37+
}
38+
do {
39+
if(!fread(seckey, sizeof(seckey), 1, frand)) {
40+
fclose(frand);
41+
return 0;
42+
}
43+
/* The probability that this not a valid secret key is approximately 2^-128 */
44+
} while (!secp256k1_ec_seckey_verify(ctx, seckey));
45+
fclose(frand);
46+
if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
47+
return 0;
48+
}
49+
if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) {
50+
return 0;
51+
}
52+
return 1;
53+
}
54+
55+
/* Sign a message hash with the given key pairs and store the result in sig */
56+
int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, unsigned char *sig64) {
57+
int i;
58+
const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS];
59+
const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS];
60+
const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS];
61+
/* The same for all signers */
62+
secp256k1_musig_keyagg_cache cache;
63+
secp256k1_musig_session session;
64+
65+
for (i = 0; i < N_SIGNERS; i++) {
66+
FILE *frand;
67+
unsigned char seckey[32];
68+
unsigned char session_id[32];
69+
/* Create random session ID. It is absolutely necessary that the session ID
70+
* is unique for every call of secp256k1_musig_nonce_gen. Otherwise
71+
* it's trivial for an attacker to extract the secret key! */
72+
frand = fopen("/dev/urandom", "r");
73+
if(frand == NULL) {
74+
return 0;
75+
}
76+
if (!fread(session_id, 32, 1, frand)) {
77+
fclose(frand);
78+
return 0;
79+
}
80+
fclose(frand);
81+
if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) {
82+
return 0;
83+
}
84+
/* Initialize session and create secret nonce for signing and public
85+
* nonce to send to the other signers. */
86+
if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) {
87+
return 0;
88+
}
89+
pubkeys[i] = &signer[i].pubkey;
90+
pubnonces[i] = &signer[i].pubnonce;
91+
}
92+
/* Communication round 1: A production system would exchange public nonces
93+
* here before moving on. */
94+
for (i = 0; i < N_SIGNERS; i++) {
95+
secp256k1_musig_aggnonce agg_pubnonce;
96+
97+
/* Create aggregate pubkey, aggregate nonce and initialize signer data */
98+
if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) {
99+
return 0;
100+
}
101+
if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) {
102+
return 0;
103+
}
104+
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, &cache, NULL)) {
105+
return 0;
106+
}
107+
/* partial_sign will clear the secnonce by setting it to 0. That's because
108+
* you must _never_ reuse the secnonce (or use the same session_id to
109+
* create a secnonce). If you do, you effectively reuse the nonce and
110+
* leak the secret key. */
111+
if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, &cache, &session)) {
112+
return 0;
113+
}
114+
partial_sigs[i] = &signer[i].partial_sig;
115+
}
116+
/* Communication round 2: A production system would exchange
117+
* partial signatures here before moving on. */
118+
for (i = 0; i < N_SIGNERS; i++) {
119+
/* To check whether signing was successful, it suffices to either verify
120+
* the aggregate signature with the aggregate public key using
121+
* secp256k1_schnorrsig_verify, or verify all partial signatures of all
122+
* signers individually. Verifying the aggregate signature is cheaper but
123+
* verifying the individual partial signatures has the advantage that it
124+
* can be used to determine which of the partial signatures are invalid
125+
* (if any), i.e., which of the partial signatures cause the aggregate
126+
* signature to be invalid and thus the protocol run to fail. It's also
127+
* fine to first verify the aggregate sig, and only verify the individual
128+
* sigs if it does not work.
129+
*/
130+
if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, &cache, &session)) {
131+
return 0;
132+
}
133+
}
134+
return secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs, N_SIGNERS);
135+
}
136+
137+
int main(void) {
138+
secp256k1_context* ctx;
139+
int i;
140+
struct signer_secrets signer_secrets[N_SIGNERS];
141+
struct signer signers[N_SIGNERS];
142+
const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS];
143+
secp256k1_xonly_pubkey agg_pk;
144+
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!";
145+
unsigned char sig[64];
146+
147+
/* Create a context for signing and verification */
148+
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
149+
printf("Creating key pairs......");
150+
for (i = 0; i < N_SIGNERS; i++) {
151+
if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) {
152+
printf("FAILED\n");
153+
return 1;
154+
}
155+
pubkeys_ptr[i] = &signers[i].pubkey;
156+
}
157+
printf("ok\n");
158+
printf("Combining public keys...");
159+
if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, NULL, pubkeys_ptr, N_SIGNERS)) {
160+
printf("FAILED\n");
161+
return 1;
162+
}
163+
printf("ok\n");
164+
printf("Signing message.........");
165+
if (!sign(ctx, signer_secrets, signers, msg, sig)) {
166+
printf("FAILED\n");
167+
return 1;
168+
}
169+
printf("ok\n");
170+
printf("Verifying signature.....");
171+
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) {
172+
printf("FAILED\n");
173+
return 1;
174+
}
175+
printf("ok\n");
176+
secp256k1_context_destroy(ctx);
177+
return 0;
178+
}

0 commit comments

Comments
 (0)