1use core::ops::Deref;
2
3use std::collections::HashMap;
4#[cfg(test)]
5use std::str::FromStr;
6
7use zeroize::Zeroizing;
8
9use rand_core::{RngCore, CryptoRng, SeedableRng};
10use rand_chacha::ChaCha20Rng;
11
12use ciphersuite::group::{ff::PrimeField, GroupEncoding};
13
14use crate::{
15 curve::Curve,
16 Participant, ThresholdKeys,
17 algorithm::{Hram, IetfSchnorr},
18 sign::{
19 Writable, Nonce, GeneratorCommitments, NonceCommitments, Commitments, Preprocess,
20 PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine,
21 },
22 tests::{clone_without, recover_key, test_ciphersuite},
23};
24
25pub struct Vectors {
27 pub threshold: u16,
28
29 pub group_secret: String,
30 pub group_key: String,
31 pub shares: Vec<String>,
32
33 pub msg: String,
34 pub included: Vec<Participant>,
35
36 pub nonce_randomness: Vec<[String; 2]>,
37 pub nonces: Vec<[String; 2]>,
38 pub commitments: Vec<[String; 2]>,
39
40 pub sig_shares: Vec<String>,
41
42 pub sig: String,
43}
44
45#[cfg(test)]
49impl From<serde_json::Value> for Vectors {
50 fn from(value: serde_json::Value) -> Vectors {
51 let to_str = |value: &serde_json::Value| value.as_str().unwrap().to_string();
52 Vectors {
53 threshold: u16::from_str(value["config"]["NUM_PARTICIPANTS"].as_str().unwrap()).unwrap(),
54
55 group_secret: to_str(&value["inputs"]["group_secret_key"]),
56 group_key: to_str(&value["inputs"]["group_public_key"]),
57 shares: value["inputs"]["participant_shares"]
58 .as_array()
59 .unwrap()
60 .iter()
61 .map(|share| to_str(&share["participant_share"]))
62 .collect(),
63
64 msg: to_str(&value["inputs"]["message"]),
65 included: value["inputs"]["participant_list"]
66 .as_array()
67 .unwrap()
68 .iter()
69 .map(|i| Participant::new(u16::try_from(i.as_u64().unwrap()).unwrap()).unwrap())
70 .collect(),
71
72 nonce_randomness: value["round_one_outputs"]["outputs"]
73 .as_array()
74 .unwrap()
75 .iter()
76 .map(|value| {
77 [to_str(&value["hiding_nonce_randomness"]), to_str(&value["binding_nonce_randomness"])]
78 })
79 .collect(),
80 nonces: value["round_one_outputs"]["outputs"]
81 .as_array()
82 .unwrap()
83 .iter()
84 .map(|value| [to_str(&value["hiding_nonce"]), to_str(&value["binding_nonce"])])
85 .collect(),
86 commitments: value["round_one_outputs"]["outputs"]
87 .as_array()
88 .unwrap()
89 .iter()
90 .map(|value| {
91 [to_str(&value["hiding_nonce_commitment"]), to_str(&value["binding_nonce_commitment"])]
92 })
93 .collect(),
94
95 sig_shares: value["round_two_outputs"]["outputs"]
96 .as_array()
97 .unwrap()
98 .iter()
99 .map(|value| to_str(&value["sig_share"]))
100 .collect(),
101
102 sig: to_str(&value["final_output"]["sig"]),
103 }
104 }
105}
106
107fn vectors_to_multisig_keys<C: Curve>(vectors: &Vectors) -> HashMap<Participant, ThresholdKeys<C>> {
109 let shares = vectors
110 .shares
111 .iter()
112 .map(|secret| C::read_F::<&[u8]>(&mut hex::decode(secret).unwrap().as_ref()).unwrap())
113 .collect::<Vec<_>>();
114 let verification_shares = shares.iter().map(|secret| C::generator() * secret).collect::<Vec<_>>();
115
116 let mut keys = HashMap::new();
117 for i in 1 ..= u16::try_from(shares.len()).unwrap() {
118 let mut serialized = vec![];
120 serialized.extend(u32::try_from(C::ID.len()).unwrap().to_le_bytes());
121 serialized.extend(C::ID);
122 serialized.extend(vectors.threshold.to_le_bytes());
123 serialized.extend(u16::try_from(shares.len()).unwrap().to_le_bytes());
124 serialized.extend(i.to_le_bytes());
125 serialized.push(1);
126 serialized.extend(shares[usize::from(i) - 1].to_repr().as_ref());
127 for share in &verification_shares {
128 serialized.extend(share.to_bytes().as_ref());
129 }
130
131 let these_keys = ThresholdKeys::<C>::read::<&[u8]>(&mut serialized.as_ref()).unwrap();
132 assert_eq!(these_keys.params().t(), vectors.threshold);
133 assert_eq!(usize::from(these_keys.params().n()), shares.len());
134 let participant = Participant::new(i).unwrap();
135 assert_eq!(these_keys.params().i(), participant);
136 assert_eq!(these_keys.original_secret_share().deref(), &shares[usize::from(i - 1)]);
137 assert_eq!(hex::encode(these_keys.group_key().to_bytes().as_ref()), vectors.group_key);
138 keys.insert(participant, these_keys);
139 }
140
141 keys
142}
143
144pub fn test_with_vectors<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
146 rng: &mut R,
147 vectors: &Vectors,
148) {
149 test_ciphersuite::<R, C, H>(rng);
150
151 let keys = vectors_to_multisig_keys::<C>(vectors);
153 {
154 let group_key =
155 <C as Curve>::read_G::<&[u8]>(&mut hex::decode(&vectors.group_key).unwrap().as_ref())
156 .unwrap();
157 let secret =
158 C::read_F::<&[u8]>(&mut hex::decode(&vectors.group_secret).unwrap().as_ref()).unwrap();
159 assert_eq!(C::generator() * secret, group_key);
160 assert_eq!(*recover_key(&keys.values().cloned().collect::<Vec<_>>()).unwrap(), secret);
161
162 let mut machines = vec![];
163 for i in &vectors.included {
164 machines.push((i, AlgorithmMachine::new(IetfSchnorr::<C, H>::ietf(), keys[i].clone())));
165 }
166
167 let mut commitments = HashMap::new();
168 let machines = machines
169 .into_iter()
170 .enumerate()
171 .map(|(c, (i, machine))| {
172 let nonce = |i| {
173 Zeroizing::new(
174 C::read_F::<&[u8]>(&mut hex::decode(&vectors.nonces[c][i]).unwrap().as_ref()).unwrap(),
175 )
176 };
177 let nonces = [nonce(0), nonce(1)];
178 let these_commitments =
179 [C::generator() * nonces[0].deref(), C::generator() * nonces[1].deref()];
180
181 assert_eq!(
182 these_commitments[0].to_bytes().as_ref(),
183 hex::decode(&vectors.commitments[c][0]).unwrap()
184 );
185 assert_eq!(
186 these_commitments[1].to_bytes().as_ref(),
187 hex::decode(&vectors.commitments[c][1]).unwrap()
188 );
189
190 let preprocess = Preprocess {
191 commitments: Commitments {
192 nonces: vec![NonceCommitments {
193 generators: vec![GeneratorCommitments(these_commitments)],
194 }],
195 },
196 addendum: (),
197 };
198 assert_eq!(
201 preprocess.serialize(),
202 hex::decode(vectors.commitments[c][0].clone() + &vectors.commitments[c][1]).unwrap()
203 );
204
205 let machine = machine.unsafe_override_preprocess(vec![Nonce(nonces)], preprocess);
206
207 commitments.insert(
208 *i,
209 machine
210 .read_preprocess::<&[u8]>(
211 &mut [
212 these_commitments[0].to_bytes().as_ref(),
213 these_commitments[1].to_bytes().as_ref(),
214 ]
215 .concat()
216 .as_ref(),
217 )
218 .unwrap(),
219 );
220 (i, machine)
221 })
222 .collect::<Vec<_>>();
223
224 let mut shares = HashMap::new();
225 let machines = machines
226 .into_iter()
227 .enumerate()
228 .map(|(c, (i, machine))| {
229 let (machine, share) = machine
230 .sign(clone_without(&commitments, i), &hex::decode(&vectors.msg).unwrap())
231 .unwrap();
232
233 let share = {
234 let mut buf = vec![];
235 share.write(&mut buf).unwrap();
236 buf
237 };
238 assert_eq!(share, hex::decode(&vectors.sig_shares[c]).unwrap());
239
240 shares.insert(*i, machine.read_share::<&[u8]>(&mut share.as_ref()).unwrap());
241 (i, machine)
242 })
243 .collect::<Vec<_>>();
244
245 for (i, machine) in machines {
246 let sig = machine.complete(clone_without(&shares, i)).unwrap();
247 let mut serialized = sig.R.to_bytes().as_ref().to_vec();
248 serialized.extend(sig.s.to_repr().as_ref());
249 assert_eq!(hex::encode(serialized), vectors.sig);
250 }
251 }
252
253 struct TransparentRng(Vec<[u8; 32]>);
258 impl RngCore for TransparentRng {
259 fn next_u32(&mut self) -> u32 {
260 unimplemented!()
261 }
262 fn next_u64(&mut self) -> u64 {
263 unimplemented!()
264 }
265 fn fill_bytes(&mut self, dest: &mut [u8]) {
266 dest.copy_from_slice(&self.0.remove(0))
267 }
268 fn try_fill_bytes(&mut self, _: &mut [u8]) -> Result<(), rand_core::Error> {
269 unimplemented!()
270 }
271 }
272 impl CryptoRng for TransparentRng {}
276
277 for (i, l) in vectors.included.iter().enumerate() {
279 let l = usize::from(u16::from(*l));
280
281 let share = Zeroizing::new(
283 C::read_F::<&[u8]>(&mut hex::decode(&vectors.shares[l - 1]).unwrap().as_ref()).unwrap(),
284 );
285
286 let randomness = vectors.nonce_randomness[i]
287 .iter()
288 .map(|randomness| hex::decode(randomness).unwrap().try_into().unwrap())
289 .collect::<Vec<_>>();
290
291 let nonces = vectors.nonces[i]
292 .iter()
293 .map(|nonce| {
294 Zeroizing::new(C::read_F::<&[u8]>(&mut hex::decode(nonce).unwrap().as_ref()).unwrap())
295 })
296 .collect::<Vec<_>>();
297
298 for (randomness, nonce) in randomness.iter().zip(&nonces) {
299 assert_eq!(C::random_nonce(&share, &mut TransparentRng(vec![*randomness])), *nonce);
301 }
302
303 let (generated_nonces, commitments) =
305 Commitments::<C>::new::<_>(&mut TransparentRng(randomness), &share, &[vec![C::generator()]]);
306
307 assert_eq!(generated_nonces.len(), 1);
308 assert_eq!(generated_nonces[0].0, [nonces[0].clone(), nonces[1].clone()]);
309
310 let mut commitments_bytes = vec![];
311 commitments.write(&mut commitments_bytes).unwrap();
312 assert_eq!(
313 commitments_bytes,
314 hex::decode(vectors.commitments[i][0].clone() + &vectors.commitments[i][1]).unwrap()
315 );
316 }
317
318 {
323 let mut chacha_seed = [0; 32];
324 rng.fill_bytes(&mut chacha_seed);
325 let mut ours = ChaCha20Rng::from_seed(chacha_seed);
326 let frosts = ours.clone();
327
328 let mut preprocess_seed = [0; 32];
330 ours.fill_bytes(&mut preprocess_seed);
331 let mut ours = ChaCha20Rng::from_seed(preprocess_seed);
332
333 let mut randomness = ([0; 32], [0; 32]);
335 ours.fill_bytes(&mut randomness.0);
336 ours.fill_bytes(&mut randomness.1);
337
338 let mut machines = vec![];
340 for i in &vectors.included {
341 machines.push((i, AlgorithmMachine::new(IetfSchnorr::<C, H>::ietf(), keys[i].clone())));
342 }
343
344 for (i, machine) in machines {
345 let (_, preprocess) = machine.preprocess(&mut frosts.clone());
346
347 let mut expected = (C::generator() *
349 C::random_nonce(
350 keys[i].original_secret_share(),
351 &mut TransparentRng(vec![randomness.0]),
352 )
353 .deref())
354 .to_bytes()
355 .as_ref()
356 .to_vec();
357 expected.extend(
358 (C::generator() *
359 C::random_nonce(
360 keys[i].original_secret_share(),
361 &mut TransparentRng(vec![randomness.1]),
362 )
363 .deref())
364 .to_bytes()
365 .as_ref(),
366 );
367
368 assert_eq!(preprocess.serialize(), expected);
370 }
371 }
372}