-
Notifications
You must be signed in to change notification settings - Fork 210
/
frost.c
304 lines (280 loc) · 11.6 KB
/
frost.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/***********************************************************************
* Copyright (c) 2021 Jesse Posner, Jonas Nick *
* Distributed under the MIT software license, see the accompanying *
* file COPYING or https://www.opensource.org/licenses/mit-license.php.*
**********************************************************************/
/**
* This file demonstrates how to use the FROST module to create a threshold signature.
* Additionally, see the documentation in include/secp256k1_frost.h.
*/
#include <stdio.h>
#include <assert.h>
#include <secp256k1.h>
#include <secp256k1_schnorrsig.h>
#include <secp256k1_musig.h>
#include <secp256k1_frost.h>
/* Number of public keys involved in creating the aggregate signature */
#define N_SIGNERS 5
/* Threshold required in creating the aggregate signature */
#define THRESHOLD 3
struct signer_secrets {
secp256k1_keypair keypair;
secp256k1_frost_share agg_share;
secp256k1_musig_secnonce secnonce;
};
struct signer {
secp256k1_xonly_pubkey pubkey;
secp256k1_musig_pubnonce pubnonce;
secp256k1_musig_partial_sig partial_sig;
secp256k1_pubkey pubcoeff[THRESHOLD];
unsigned char vss_hash[32];
};
/* Create a key pair and store it in seckey and pubkey */
int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
unsigned char seckey[32];
FILE *frand = fopen("/dev/urandom", "r");
if (frand == NULL) {
return 0;
}
do {
if(!fread(seckey, sizeof(seckey), 1, frand)) {
fclose(frand);
return 0;
}
/* The probability that this not a valid secret key is approximately 2^-128 */
} while (!secp256k1_ec_seckey_verify(ctx, seckey));
fclose(frand);
if (!secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
return 0;
}
if (!secp256k1_keypair_xonly_pub(ctx, &signer->pubkey, NULL, &signer_secrets->keypair)) {
return 0;
}
return 1;
}
/* Create shares and coefficient commitments */
int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const secp256k1_musig_keyagg_cache *keyagg_cache) {
int i;
secp256k1_frost_share shares[N_SIGNERS][N_SIGNERS];
const secp256k1_pubkey *pubcoeffs[N_SIGNERS];
for (i = 0; i < N_SIGNERS; i++) {
FILE *frand;
unsigned char seckey[32];
unsigned char session_id[32];
/* Create random session ID. */
frand = fopen("/dev/urandom", "r");
if(frand == NULL) {
return 0;
}
if (!fread(session_id, 32, 1, frand)) {
fclose(frand);
return 0;
}
fclose(frand);
if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) {
return 0;
}
/* Initialize session and create secret nonce for VSS signing and public
* nonce to send to the other signers. */
/* TODO: create nonce helper function */
if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, NULL, NULL, NULL)) {
return 0;
}
if (!secp256k1_frost_share_gen(ctx, signer[i].pubcoeff, shares[i], THRESHOLD, N_SIGNERS, &signer_secrets[i].keypair, keyagg_cache)) {
return 0;
}
}
/* Communication round 1: exchange shares, nonce commitments, and coefficient commitments */
for (i = 0; i < N_SIGNERS; i++) {
pubcoeffs[i] = signer[i].pubcoeff;
}
for (i = 0; i < N_SIGNERS; i++) {
int j;
const secp256k1_frost_share *assigned_shares[N_SIGNERS];
for (j = 0; j < N_SIGNERS; j++) {
assigned_shares[j] = &shares[j][i];
}
if (!secp256k1_frost_share_agg(ctx, &signer_secrets[i].agg_share, signer[i].vss_hash, assigned_shares, pubcoeffs, N_SIGNERS, THRESHOLD)) {
return 0;
}
}
return 1;
}
/* Sign the VSS proofs with the Musig scheme */
int sign_vss(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, unsigned char *sig64) {
int i;
const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS];
const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS];
const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS];
/* The same for all signers */
secp256k1_musig_keyagg_cache cache;
secp256k1_musig_session session;
for (i = 0; i < N_SIGNERS; i++) {
pubkeys[i] = &signer[i].pubkey;
pubnonces[i] = &signer[i].pubnonce;
}
for (i = 0; i < N_SIGNERS; i++) {
secp256k1_musig_aggnonce agg_pubnonce;
/* Create aggregate pubkey, aggregate nonce and initialize signer data */
if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) {
return 0;
}
if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, N_SIGNERS)) {
return 0;
}
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, signer[i].vss_hash, &cache, NULL)) {
return 0;
}
/* partial_sign will clear the secnonce by setting it to 0. That's because */
/* you must _never_ reuse the secnonce (or use the same session_id to */
/* create a secnonce). If you do, you effectively reuse the nonce and */
/* leak the secret key. */
if (!secp256k1_musig_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].keypair, &cache, &session)) {
return 0;
}
partial_sigs[i] = &signer[i].partial_sig;
}
/* Communication round 2: A production system would exchange */
/* partial signatures here before moving on. */
for (i = 0; i < N_SIGNERS; i++) {
/* To check whether signing was successful, it suffices to either verify */
/* the aggregate signature with the aggregate public key using */
/* secp256k1_schnorrsig_verify, or verify all partial signatures of all */
/* signers individually. Verifying the aggregate signature is cheaper but */
/* verifying the individual partial signatures has the advantage that it */
/* can be used to determine which of the partial signatures are invalid */
/* (if any), i.e., which of the partial signatures cause the aggregate */
/* signature to be invalid and thus the protocol run to fail. It's also */
/* fine to first verify the aggregate sig, and only verify the individual */
/* sigs if it does not work. */
if (!secp256k1_musig_partial_sig_verify(ctx, &signer[i].partial_sig, &signer[i].pubnonce, &signer[i].pubkey, &cache, &session)) {
return 0;
}
}
return secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs, N_SIGNERS);
}
/* Sign a message hash with the given key pairs and store the result in sig */
int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer, const unsigned char* msg32, unsigned char *sig64) {
int i;
const secp256k1_xonly_pubkey *pubkeys[N_SIGNERS];
const secp256k1_musig_pubnonce *pubnonces[N_SIGNERS];
const secp256k1_musig_partial_sig *partial_sigs[N_SIGNERS];
/* The same for all signers */
secp256k1_musig_keyagg_cache cache;
secp256k1_musig_session session;
size_t participants[THRESHOLD];
for (i = 0; i < N_SIGNERS; i++) {
FILE *frand;
unsigned char seckey[32];
unsigned char session_id[32];
/* Create random session ID. It is absolutely necessary that the session ID
* is unique for every call of secp256k1_musig_nonce_gen. Otherwise
* it's trivial for an attacker to extract the secret key! */
frand = fopen("/dev/urandom", "r");
if(frand == NULL) {
return 0;
}
if (!fread(session_id, 32, 1, frand)) {
fclose(frand);
return 0;
}
fclose(frand);
if (!secp256k1_keypair_sec(ctx, seckey, &signer_secrets[i].keypair)) {
return 0;
}
/* Initialize session and create secret nonce for signing and public
* nonce to send to the other signers. */
if (!secp256k1_musig_nonce_gen(ctx, &signer_secrets[i].secnonce, &signer[i].pubnonce, session_id, seckey, msg32, NULL, NULL)) {
return 0;
}
pubkeys[i] = &signer[i].pubkey;
pubnonces[i] = &signer[i].pubnonce;
}
for (i = 0; i < THRESHOLD; i++) {
participants[i] = i+1;
}
if (!secp256k1_musig_pubkey_agg(ctx, NULL, NULL, &cache, pubkeys, N_SIGNERS)) {
return 0;
}
/* Communication round 1: Exchange nonces */
for (i = 0; i < THRESHOLD; i++) {
secp256k1_musig_aggnonce agg_pubnonce;
/* Aggregate nonce and initialize signer data */
if (!secp256k1_musig_nonce_agg(ctx, &agg_pubnonce, pubnonces, THRESHOLD)) {
return 0;
}
if (!secp256k1_musig_nonce_process(ctx, &session, &agg_pubnonce, msg32, &cache, NULL)) {
return 0;
}
/* partial_sign will clear the secnonce by setting it to 0. That's because
* you must _never_ reuse the secnonce (or use the same session_id to
* create a secnonce). If you do, you effectively reuse the nonce and
* leak the secret key. */
if (!secp256k1_frost_partial_sign(ctx, &signer[i].partial_sig, &signer_secrets[i].secnonce, &signer_secrets[i].agg_share, &session, THRESHOLD, participants, i+1)) {
return 0;
}
partial_sigs[i] = &signer[i].partial_sig;
}
/* Communication round 2: Exchange partial signatures */
return secp256k1_musig_partial_sig_agg(ctx, sig64, &session, partial_sigs, THRESHOLD);
}
int main(void) {
secp256k1_context* ctx;
int i;
struct signer_secrets signer_secrets[N_SIGNERS];
struct signer signers[N_SIGNERS];
const secp256k1_xonly_pubkey *pubkeys_ptr[N_SIGNERS];
secp256k1_musig_keyagg_cache cache;
secp256k1_xonly_pubkey agg_pk;
unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!";
unsigned char sig[64];
/* Create a context for signing and verification */
ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY);
printf("Creating key pairs......");
for (i = 0; i < N_SIGNERS; i++) {
if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) {
printf("FAILED\n");
return 1;
}
pubkeys_ptr[i] = &signers[i].pubkey;
}
printf("ok\n");
printf("Combining public keys...");
if (!secp256k1_musig_pubkey_agg(ctx, NULL, &agg_pk, &cache, pubkeys_ptr, N_SIGNERS)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Creating shares......");
if (!create_shares(ctx, signer_secrets, signers, &cache)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Signing VSS proofs with MuSig......");
if (!sign_vss(ctx, signer_secrets, signers, sig)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Verifying VSS proof signature.....");
if (!secp256k1_schnorrsig_verify(ctx, sig, signers[0].vss_hash, 32, &agg_pk)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Signing message with FROST.........");
if (!sign(ctx, signer_secrets, signers, msg, sig)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
printf("Verifying signature.....");
if (!secp256k1_schnorrsig_verify(ctx, sig, msg, 32, &agg_pk)) {
printf("FAILED\n");
return 1;
}
printf("ok\n");
secp256k1_context_destroy(ctx);
return 0;
}