1use std::collections::HashMap;
2
3use rand_core::{RngCore, CryptoRng};
4
5use ciphersuite::Ciphersuite;
6pub use dkg_recovery::recover_key;
7
8use crate::{
9 Curve, Participant, ThresholdKeys, FrostError,
10 algorithm::{Algorithm, Hram, IetfSchnorr},
11 sign::{Writable, PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine},
12};
13
14pub mod nonces;
16use nonces::test_multi_nonce;
17
18pub mod vectors;
20
21#[cfg(test)]
23mod literal;
24
25pub const PARTICIPANTS: u16 = 5;
27pub const THRESHOLD: u16 = ((PARTICIPANTS * 2) / 3) + 1;
29
30pub fn key_gen<R: RngCore + CryptoRng, C: Ciphersuite>(
32 rng: &mut R,
33) -> HashMap<Participant, ThresholdKeys<C>> {
34 let res = dkg_dealer::key_gen::<R, C>(rng, THRESHOLD, PARTICIPANTS).unwrap();
35 assert_eq!(
36 C::generator() * *recover_key(&res.values().cloned().collect::<Vec<_>>()).unwrap(),
37 res.values().next().unwrap().group_key()
38 );
39 res
40}
41
42pub fn clone_without<K: Clone + core::cmp::Eq + core::hash::Hash, V: Clone>(
44 map: &HashMap<K, V>,
45 without: &K,
46) -> HashMap<K, V> {
47 let mut res = map.clone();
48 res.remove(without).unwrap();
49 res
50}
51
52pub fn algorithm_machines_without_clone<R: RngCore, C: Curve, A: Algorithm<C>>(
54 rng: &mut R,
55 keys: &HashMap<Participant, ThresholdKeys<C>>,
56 machines: HashMap<Participant, AlgorithmMachine<C, A>>,
57) -> HashMap<Participant, AlgorithmMachine<C, A>> {
58 let mut included = vec![];
59 while included.len() < usize::from(keys[&Participant::new(1).unwrap()].params().t()) {
60 let n = Participant::new(
61 u16::try_from((rng.next_u64() % u64::try_from(keys.len()).unwrap()) + 1).unwrap(),
62 )
63 .unwrap();
64 if included.contains(&n) {
65 continue;
66 }
67 included.push(n);
68 }
69
70 machines
71 .into_iter()
72 .filter_map(|(i, machine)| if included.contains(&i) { Some((i, machine)) } else { None })
73 .collect()
74}
75
76pub fn algorithm_machines<R: RngCore, C: Curve, A: Clone + Algorithm<C>>(
78 rng: &mut R,
79 algorithm: &A,
80 keys: &HashMap<Participant, ThresholdKeys<C>>,
81) -> HashMap<Participant, AlgorithmMachine<C, A>> {
82 algorithm_machines_without_clone(
83 rng,
84 keys,
85 keys
86 .values()
87 .map(|keys| (keys.params().i(), AlgorithmMachine::new(algorithm.clone(), keys.clone())))
88 .collect(),
89 )
90}
91
92pub(crate) fn preprocess<
94 R: RngCore + CryptoRng,
95 M: PreprocessMachine,
96 F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
97>(
98 rng: &mut R,
99 mut machines: HashMap<Participant, M>,
100 mut cache: F,
101) -> (HashMap<Participant, M::SignMachine>, HashMap<Participant, M::Preprocess>) {
102 let mut commitments = HashMap::new();
103 let mut machines = machines
104 .drain()
105 .map(|(i, machine)| {
106 let (machine, preprocess) = machine.preprocess(rng);
107 commitments.insert(i, {
108 let mut buf = vec![];
109 preprocess.write(&mut buf).unwrap();
110 machine.read_preprocess::<&[u8]>(&mut buf.as_ref()).unwrap()
111 });
112 (i, machine)
113 })
114 .collect::<HashMap<_, _>>();
115
116 cache(rng, &mut machines);
117
118 (machines, commitments)
119}
120
121#[allow(clippy::type_complexity)]
123pub(crate) fn preprocess_and_shares<
124 R: RngCore + CryptoRng,
125 M: PreprocessMachine,
126 F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
127>(
128 rng: &mut R,
129 machines: HashMap<Participant, M>,
130 cache: F,
131 msg: &[u8],
132) -> (
133 HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureMachine>,
134 HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::SignatureShare>,
135) {
136 let (mut machines, commitments) = preprocess(rng, machines, cache);
137
138 let mut shares = HashMap::new();
139 let machines = machines
140 .drain()
141 .map(|(i, machine)| {
142 let (machine, share) = machine.sign(clone_without(&commitments, &i), msg).unwrap();
143 shares.insert(i, {
144 let mut buf = vec![];
145 share.write(&mut buf).unwrap();
146 machine.read_share::<&[u8]>(&mut buf.as_ref()).unwrap()
147 });
148 (i, machine)
149 })
150 .collect::<HashMap<_, _>>();
151
152 (machines, shares)
153}
154
155fn sign_internal<
156 R: RngCore + CryptoRng,
157 M: PreprocessMachine,
158 F: FnMut(&mut R, &mut HashMap<Participant, M::SignMachine>),
159>(
160 rng: &mut R,
161 machines: HashMap<Participant, M>,
162 cache: F,
163 msg: &[u8],
164) -> M::Signature {
165 let (mut machines, shares) = preprocess_and_shares(rng, machines, cache, msg);
166
167 let mut signature = None;
168 for (i, machine) in machines.drain() {
169 let sig = machine.complete(clone_without(&shares, &i)).unwrap();
170 if signature.is_none() {
171 signature = Some(sig.clone());
172 }
173 assert_eq!(&sig, signature.as_ref().unwrap());
174 }
175 signature.unwrap()
176}
177
178pub fn sign_without_caching<R: RngCore + CryptoRng, M: PreprocessMachine>(
182 rng: &mut R,
183 machines: HashMap<Participant, M>,
184 msg: &[u8],
185) -> M::Signature {
186 sign_internal(rng, machines, |_, _| {}, msg)
187}
188
189pub fn sign_without_clone<R: RngCore + CryptoRng, M: PreprocessMachine>(
192 rng: &mut R,
193 mut keys: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
194 mut params: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Params>,
195 machines: HashMap<Participant, M>,
196 msg: &[u8],
197) -> M::Signature {
198 sign_internal(
199 rng,
200 machines,
201 |rng, machines| {
202 let included = machines.keys().copied().collect::<Vec<_>>();
204 for i in included {
205 if (rng.next_u64() % 2) == 0 {
206 let cache = machines.remove(&i).unwrap().cache();
207 machines.insert(
208 i,
209 M::SignMachine::from_cache(params.remove(&i).unwrap(), keys.remove(&i).unwrap(), cache)
210 .0,
211 );
212 }
213 }
214 },
215 msg,
216 )
217}
218
219pub fn sign<
222 R: RngCore + CryptoRng,
223 M: PreprocessMachine<SignMachine: SignMachine<M::Signature, Params: Clone>>,
224>(
225 rng: &mut R,
226 params: &<M::SignMachine as SignMachine<M::Signature>>::Params,
227 keys: HashMap<Participant, <M::SignMachine as SignMachine<M::Signature>>::Keys>,
228 machines: HashMap<Participant, M>,
229 msg: &[u8],
230) -> M::Signature {
231 let params = keys.keys().map(|i| (*i, params.clone())).collect();
232 sign_without_clone(rng, keys, params, machines, msg)
233}
234
235pub fn test_schnorr_with_keys<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(
237 rng: &mut R,
238 keys: &HashMap<Participant, ThresholdKeys<C>>,
239) {
240 const MSG: &[u8] = b"Hello, World!";
241
242 let machines = algorithm_machines(&mut *rng, &IetfSchnorr::<C, H>::ietf(), keys);
243 let sig = sign(&mut *rng, &IetfSchnorr::<C, H>::ietf(), keys.clone(), machines, MSG);
244 let group_key = keys[&Participant::new(1).unwrap()].group_key();
245 assert!(sig.verify(group_key, H::hram(&sig.R, &group_key, MSG)));
246}
247
248pub fn test_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
250 let keys = key_gen(&mut *rng);
251 test_schnorr_with_keys::<_, _, H>(&mut *rng, &keys)
252}
253
254pub fn test_offset_schnorr<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
256 const MSG: &[u8] = b"Hello, World!";
257
258 let mut keys = key_gen(&mut *rng);
259 let group_key = keys[&Participant::new(1).unwrap()].group_key();
260
261 let scalar = C::F::from(3);
262 let offset = C::F::from(5);
263 let offset_key = (group_key * scalar) + (C::generator() * offset);
264 for keys in keys.values_mut() {
265 *keys = keys.clone().scale(scalar).unwrap().offset(offset);
266 assert_eq!(keys.group_key(), offset_key);
267 }
268
269 let machines = algorithm_machines(&mut *rng, &IetfSchnorr::<C, H>::ietf(), &keys);
270 let sig = sign(&mut *rng, &IetfSchnorr::<C, H>::ietf(), keys.clone(), machines, MSG);
271 let group_key = keys[&Participant::new(1).unwrap()].group_key();
272 assert!(sig.verify(offset_key, H::hram(&sig.R, &group_key, MSG)));
273}
274
275pub fn test_schnorr_blame<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
277 const MSG: &[u8] = b"Hello, World!";
278
279 let keys = key_gen(&mut *rng);
280 let machines = algorithm_machines(&mut *rng, &IetfSchnorr::<C, H>::ietf(), &keys);
281
282 let (mut machines, shares) = preprocess_and_shares(&mut *rng, machines, |_, _| {}, MSG);
283
284 for (i, machine) in machines.drain() {
285 let mut shares = clone_without(&shares, &i);
286
287 let participants = shares.keys().collect::<Vec<_>>();
289 let faulty = *participants
290 [usize::try_from(rng.next_u64() % u64::try_from(participants.len()).unwrap()).unwrap()];
291 shares.get_mut(&faulty).unwrap().invalidate();
292
293 assert_eq!(machine.complete(shares).err(), Some(FrostError::InvalidShare(faulty)));
294 }
295}
296
297pub fn test_ciphersuite<R: RngCore + CryptoRng, C: Curve, H: Hram<C>>(rng: &mut R) {
299 test_schnorr::<R, C, H>(rng);
300 test_offset_schnorr::<R, C, H>(rng);
301 test_schnorr_blame::<R, C, H>(rng);
302
303 test_multi_nonce::<R, C>(rng);
304}