Skip to content

Commit 4ead1a7

Browse files
committed
wip
1 parent b8c671c commit 4ead1a7

File tree

9 files changed

+474
-267
lines changed

9 files changed

+474
-267
lines changed

contrib/frost-vectors.py

+221
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#!/usr/bin/env python3
2+
3+
import sys
4+
import json
5+
import textwrap
6+
7+
max_participants = 0
8+
9+
if len(sys.argv) < 2:
10+
print(
11+
"This script converts BIP FROST test vectors in a given directory to a C file that can be used in the test framework."
12+
)
13+
print("Usage: %s <dir>" % sys.argv[0])
14+
sys.exit(1)
15+
16+
17+
def hexstr_to_intarray(str):
18+
return ", ".join([f"0x{b:02X}" for b in bytes.fromhex(str)])
19+
20+
21+
def create_init(name):
22+
return """
23+
static const struct frost_%s_vector frost_%s_vector = {
24+
""" % (
25+
name,
26+
name,
27+
)
28+
29+
30+
def init_array(key):
31+
return textwrap.indent("{ %s },\n" % hexstr_to_intarray(data[key]), 4 * " ")
32+
33+
34+
def init_arrays(key):
35+
s = textwrap.indent("{\n", 4 * " ")
36+
s += textwrap.indent(
37+
",\n".join(["{ %s }" % hexstr_to_intarray(x) for x in data[key]]), 8 * " "
38+
)
39+
s += textwrap.indent("\n},\n", 4 * " ")
40+
return s
41+
42+
43+
def init_nested_arrays(array):
44+
return "{ %s }" % ", ".join(["{ %s }" % hexstr_to_intarray(x) for x in array])
45+
46+
47+
def init_indices(array):
48+
return " %d, { %s }" % (
49+
len(array),
50+
", ".join(map(str, array)) if len(array) > 0 else "0",
51+
)
52+
53+
54+
def init_is_xonly(case):
55+
if len(case.get("tweak_indices", [])) > 0:
56+
return ", ".join("1" if x else "0" for x in case["is_xonly"])
57+
return "0"
58+
59+
60+
def init_optional_expected(case):
61+
return hexstr_to_intarray(case["expected"]) if "expected" in case else "0"
62+
63+
64+
def init_cases(cases, f):
65+
s = textwrap.indent("{\n", 4 * " ")
66+
for (i, case) in enumerate(cases):
67+
s += textwrap.indent("%s\n" % f(case), 8 * " ")
68+
s += textwrap.indent("},\n", 4 * " ")
69+
return s
70+
71+
72+
def finish_init():
73+
return "};\n"
74+
75+
76+
s = (
77+
"""/**
78+
* Automatically generated by %s.
79+
*
80+
* The test vectors for the FROST implementation.
81+
*/
82+
"""
83+
% sys.argv[0]
84+
)
85+
86+
s += """
87+
enum FROST_ERROR {
88+
FROST_PUBKEY,
89+
FROST_PUBSHARE,
90+
FROST_TWEAK,
91+
FROST_PUBNONCE,
92+
FROST_AGGNONCE,
93+
FROST_SECNONCE,
94+
FROST_SIG,
95+
FROST_SIG_VERIFY,
96+
FROST_OTHER
97+
};
98+
"""
99+
100+
# key gen vectors
101+
with open(sys.argv[1] + "/keygen_vectors.json") as f:
102+
data = json.load(f)
103+
104+
num_valid_cases = len(data["valid_test_cases"])
105+
num_pubshare_fail_cases = len(data["pubshare_correctness_fail_test_cases"])
106+
num_group_pubkey_fail_cases = len(data["group_pubkey_correctness_fail_test_cases"])
107+
108+
all_cases = (
109+
data["valid_test_cases"]
110+
+ data["pubshare_correctness_fail_test_cases"]
111+
+ data["group_pubkey_correctness_fail_test_cases"]
112+
)
113+
max_participants = max(
114+
len(test_case["participant_identifiers"]) for test_case in all_cases
115+
)
116+
117+
# Add structures for valid and error cases
118+
s += """
119+
struct frost_key_gen_valid_test_case {
120+
size_t max_participants;
121+
size_t min_participants;
122+
unsigned char group_public_key[33];
123+
size_t participant_identifiers_len;
124+
size_t participant_identifiers[%d];
125+
unsigned char participant_pubshares[%d][33];
126+
unsigned char participant_secshares[%d][32];
127+
};
128+
""" % (
129+
max_participants,
130+
max_participants,
131+
max_participants,
132+
)
133+
s += """
134+
struct frost_key_gen_pubshare_fail_test_case {
135+
size_t max_participants;
136+
size_t min_participants;
137+
unsigned char group_public_key[33];
138+
size_t participant_identifiers_len;
139+
size_t participant_identifiers[%d];
140+
unsigned char participant_pubshares[%d][33];
141+
unsigned char participant_secshares[%d][32];
142+
enum FROST_ERROR error;
143+
};
144+
""" % (
145+
max_participants,
146+
max_participants,
147+
max_participants,
148+
)
149+
s += """
150+
struct frost_key_gen_pubkey_fail_test_case {
151+
size_t max_participants;
152+
size_t min_participants;
153+
unsigned char group_public_key[33];
154+
size_t participant_identifiers_len;
155+
size_t participant_identifiers[%d];
156+
unsigned char participant_pubshares[%d][33];
157+
unsigned char participant_secshares[%d][32];
158+
enum FROST_ERROR error;
159+
};
160+
""" % (
161+
max_participants,
162+
max_participants,
163+
max_participants,
164+
)
165+
166+
# Add structure for entire vector
167+
168+
s += """
169+
struct frost_key_gen_vector {
170+
struct frost_key_gen_valid_test_case valid_cases[%d];
171+
struct frost_key_gen_pubshare_fail_test_case pubshare_fail_cases[%d];
172+
struct frost_key_gen_pubkey_fail_test_case pubkey_fail_cases[%d];
173+
};
174+
""" % (
175+
num_valid_cases,
176+
num_pubshare_fail_cases,
177+
num_group_pubkey_fail_cases,
178+
)
179+
180+
s += create_init("key_gen")
181+
182+
# Add valid cases to the vector
183+
s += init_cases(
184+
data["valid_test_cases"],
185+
lambda case: "{ %d, %d, { %s }, %s, %s, %s },"
186+
% (
187+
case["max_participants"],
188+
case["min_participants"],
189+
hexstr_to_intarray(case["group_public_key"]),
190+
init_indices(case["participant_identifiers"]),
191+
init_nested_arrays(case["participant_pubshares"]),
192+
init_nested_arrays(case["participant_secshares"]),
193+
),
194+
)
195+
196+
def comment_to_error(case):
197+
comment = case["comment"]
198+
if "public key" in comment.lower():
199+
return "FROST_PUBKEY"
200+
elif "pubshare" in comment.lower():
201+
return "FROST_PUBSHARE"
202+
else:
203+
sys.exit("Unknown error")
204+
205+
for cases in ("pubshare_correctness_fail_test_cases", "group_pubkey_correctness_fail_test_cases"):
206+
s += init_cases(
207+
data[cases],
208+
lambda case: "{ %d, %d, { %s }, %s, %s, %s, %s },"
209+
% (
210+
case["max_participants"],
211+
case["min_participants"],
212+
hexstr_to_intarray(case["group_public_key"]),
213+
init_indices(case["participant_identifiers"]),
214+
init_nested_arrays(case["participant_pubshares"]),
215+
init_nested_arrays(case["participant_secshares"]),
216+
comment_to_error(case),
217+
),
218+
)
219+
s += finish_init()
220+
s += "enum { FROST_VECTORS_MAX_PARTICIPANTS = %d };" % max_participants
221+
print(s)

examples/frost.c

+10-45
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
#include "examples_util.h"
2121

2222
struct signer_secrets {
23-
secp256k1_keypair keypair;
2423
secp256k1_frost_secshare share;
2524
secp256k1_frost_secnonce secnonce;
2625
};
@@ -30,7 +29,6 @@ struct signer {
3029
secp256k1_frost_pubnonce pubnonce;
3130
secp256k1_frost_session session;
3231
secp256k1_frost_partial_sig partial_sig;
33-
unsigned char id[33];
3432
};
3533

3634
/* Threshold required in creating the aggregate signature */
@@ -39,60 +37,33 @@ struct signer {
3937

4038
/* Number of public keys involved in creating the aggregate signature */
4139
#define N_SIGNERS 5
42-
/* Create a key pair, store it in signer_secrets->keypair and signer->pubkey */
43-
static int create_keypair(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
44-
secp256k1_pubkey pubkey_tmp;
45-
unsigned char seckey[32];
46-
size_t size = 33;
47-
while (1) {
48-
if (!fill_random(seckey, sizeof(seckey))) {
49-
printf("Failed to generate randomness\n");
50-
return 1;
51-
}
52-
if (secp256k1_keypair_create(ctx, &signer_secrets->keypair, seckey)) {
53-
break;
54-
}
55-
}
56-
if (!secp256k1_keypair_pub(ctx, &pubkey_tmp, &signer_secrets->keypair)) {
57-
return 0;
58-
}
59-
if (!secp256k1_ec_pubkey_serialize(ctx, signer->id, &size, &pubkey_tmp, SECP256K1_EC_COMPRESSED)) {
60-
return 0;
61-
}
62-
return 1;
63-
}
6440

6541
/* Create shares and coefficient commitments */
6642
static int create_shares(const secp256k1_context* ctx, struct signer_secrets *signer_secrets, struct signer *signer) {
6743
int i;
6844
secp256k1_frost_secshare shares[N_SIGNERS];
6945
secp256k1_pubkey vss_commitment[THRESHOLD];
70-
const unsigned char *ids[N_SIGNERS];
7146
unsigned char seed[32];
7247

7348
if (!fill_random(seed, sizeof(seed))) {
7449
return 0;
7550
}
7651

77-
for (i = 0; i < N_SIGNERS; i++) {
78-
ids[i] = signer[i].id;
79-
}
80-
8152
/* Generate shares for the participants */
82-
if (!secp256k1_frost_shares_gen(ctx, shares, vss_commitment, seed, THRESHOLD, N_SIGNERS, ids)) {
53+
if (!secp256k1_frost_shares_gen(ctx, shares, vss_commitment, seed, THRESHOLD, N_SIGNERS)) {
8354
return 0;
8455
}
8556

8657
/* Distribute shares and VSS commitment */
8758
for (i = 0; i < N_SIGNERS; i++) {
8859
signer_secrets[i].share = shares[i];
8960
/* Each participant verifies their share. */
90-
if (!secp256k1_frost_share_verify(ctx, THRESHOLD, signer[i].id, &shares[i], vss_commitment)) {
61+
if (!secp256k1_frost_share_verify(ctx, THRESHOLD, i, &shares[i], vss_commitment)) {
9162
return 0;
9263
}
9364
/* Each participant generates public verification shares that are
9465
* used for verifying partial signatures. */
95-
if (!secp256k1_frost_compute_pubshare(ctx, &signer[i].pubshare, THRESHOLD, signer[i].id, vss_commitment)) {
66+
if (!secp256k1_frost_compute_pubshare(ctx, &signer[i].pubshare, THRESHOLD, i, vss_commitment)) {
9667
return 0;
9768
}
9869
}
@@ -143,7 +114,7 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr
143114
int signers[THRESHOLD];
144115
int is_signer[N_SIGNERS];
145116
const secp256k1_frost_pubnonce *pubnonces[THRESHOLD];
146-
const unsigned char *ids[THRESHOLD];
117+
size_t ids[THRESHOLD];
147118
const secp256k1_frost_partial_sig *partial_sigs[THRESHOLD];
148119

149120
for (i = 0; i < N_SIGNERS; i++) {
@@ -179,12 +150,12 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr
179150
}
180151
/* Mark signer as assigned */
181152
pubnonces[i] = &signer[signer_id].pubnonce;
182-
ids[i] = signer[signer_id].id;
153+
ids[i] = signer_id;
183154
}
184155
/* Signing communication round 1: Exchange nonces */
185156
for (i = 0; i < THRESHOLD; i++) {
186157
signer_id = signers[i];
187-
if (!secp256k1_frost_nonce_process(ctx, &signer[signer_id].session, pubnonces, THRESHOLD, msg32, signer[signer_id].id, ids, cache, NULL)) {
158+
if (!secp256k1_frost_nonce_process(ctx, &signer[signer_id].session, pubnonces, THRESHOLD, msg32, signer_id, ids, cache, NULL)) {
188159
return 0;
189160
}
190161
/* partial_sign will clear the secnonce by setting it to 0. That's because
@@ -220,36 +191,30 @@ static int sign(const secp256k1_context* ctx, struct signer_secrets *signer_secr
220191

221192
int main(void) {
222193
secp256k1_context* ctx;
223-
int i;
194+
size_t i;
224195
struct signer_secrets signer_secrets[N_SIGNERS];
225196
struct signer signers[N_SIGNERS];
226197
const secp256k1_pubkey *pubshares_ptr[N_SIGNERS];
227198
secp256k1_xonly_pubkey pk;
228199
secp256k1_frost_keygen_cache keygen_cache;
229200
const unsigned char msg[32] = "this_could_be_the_hash_of_a_msg!";
230201
unsigned char sig[64];
231-
const unsigned char *id_ptr[5];
202+
size_t ids[5];
232203

233204
/* Create a context for signing and verification */
234205
ctx = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
235-
printf("Creating key pairs......");
236206
for (i = 0; i < N_SIGNERS; i++) {
237-
if (!create_keypair(ctx, &signer_secrets[i], &signers[i])) {
238-
printf("FAILED\n");
239-
return 1;
240-
}
241207
pubshares_ptr[i] = &signers[i].pubshare;
242-
id_ptr[i] = signers[i].id;
208+
ids[i] = i;
243209
}
244-
printf("ok\n");
245210
printf("Creating shares.........");
246211
if (!create_shares(ctx, signer_secrets, signers)) {
247212
printf("FAILED\n");
248213
return 1;
249214
}
250215
printf("ok\n");
251216
printf("Generating public key...");
252-
if (!secp256k1_frost_pubkey_gen(ctx, &keygen_cache, pubshares_ptr, N_SIGNERS, id_ptr)) {
217+
if (!secp256k1_frost_pubkey_gen(ctx, &keygen_cache, pubshares_ptr, N_SIGNERS, ids)) {
253218
printf("FAILED\n");
254219
return 1;
255220
}

0 commit comments

Comments
 (0)