1use elliptic_curve::{Field, Group, ScalarPrimitive};
2use magikitten::Transcript;
3use rand_core::OsRng;
4
5use crate::compat::CSCurve;
6use crate::crypto::{commit, hash, Digest};
7use crate::math::{GroupPolynomial, Polynomial};
8use crate::participants::{ParticipantCounter, ParticipantList, ParticipantMap};
9use crate::proofs::dlog;
10use crate::protocol::internal::{make_protocol, Context, SharedChannel};
11use crate::protocol::{InitializationError, Participant, Protocol, ProtocolError};
12use crate::serde::encode;
13
14const LABEL: &[u8] = b"cait-sith v0.8.0 keygen";
15
16async fn do_keyshare<C: CSCurve>(
17 mut chan: SharedChannel,
18 participants: ParticipantList,
19 me: Participant,
20 threshold: usize,
21 s_i: C::Scalar,
22 big_s: Option<C::ProjectivePoint>,
23) -> Result<(C::Scalar, C::AffinePoint), ProtocolError> {
24 let mut rng = OsRng;
25 let mut transcript = Transcript::new(LABEL);
26
27 transcript.message(b"group", C::NAME);
29 transcript.message(b"participants", &encode(&participants));
30 transcript.message(
32 b"threshold",
33 &u64::try_from(threshold).unwrap().to_be_bytes(),
34 );
35
36 let f: Polynomial<C> = Polynomial::extend_random(&mut rng, threshold, &s_i);
38
39 let mut big_f = f.commit();
41
42 let (my_commitment, my_randomizer) = commit(&mut rng, &big_f);
44
45 let wait0 = chan.next_waitpoint();
47 chan.send_many(wait0, &my_commitment).await;
48
49 let mut all_commitments = ParticipantMap::new(&participants);
51 all_commitments.put(me, my_commitment);
52 while !all_commitments.full() {
53 let (from, commitment) = chan.recv(wait0).await?;
54 all_commitments.put(from, commitment);
55 }
56
57 let my_confirmation = hash(&all_commitments);
59
60 transcript.message(b"confirmation", my_confirmation.as_ref());
62
63 let wait1 = chan.next_waitpoint();
65 chan.send_many(wait1, &my_confirmation).await;
66
67 let statement = dlog::Statement::<C> {
69 public: &big_f.evaluate_zero(),
70 };
71 let witness = dlog::Witness::<C> {
72 x: &f.evaluate_zero(),
73 };
74 let my_phi_proof = dlog::prove(
75 &mut rng,
76 &mut transcript.forked(b"dlog0", &me.bytes()),
77 statement,
78 witness,
79 );
80
81 let wait2 = chan.next_waitpoint();
83 chan.send_many(wait2, &(&big_f, &my_randomizer, my_phi_proof))
84 .await;
85
86 let wait3 = chan.next_waitpoint();
88 for p in participants.others(me) {
89 let x_i_j: ScalarPrimitive<C> = f.evaluate(&p.scalar::<C>()).into();
90 chan.send_private(wait3, p, &x_i_j).await;
91 }
92 let mut x_i = f.evaluate(&me.scalar::<C>());
93
94 let mut seen = ParticipantCounter::new(&participants);
96 seen.put(me);
97 while !seen.full() {
98 let (from, confirmation): (_, Digest) = chan.recv(wait1).await?;
99 if !seen.put(from) {
100 continue;
101 }
102 if confirmation != my_confirmation {
103 return Err(ProtocolError::AssertionFailed(format!(
104 "confirmation from {from:?} did not match expectation"
105 )));
106 }
107 }
108
109 seen.clear();
111 seen.put(me);
112 while !seen.full() {
113 let (from, (their_big_f, their_randomizer, their_phi_proof)): (
114 _,
115 (GroupPolynomial<C>, _, _),
116 ) = chan.recv(wait2).await?;
117 if !seen.put(from) {
118 continue;
119 }
120
121 if their_big_f.len() != threshold {
122 return Err(ProtocolError::AssertionFailed(format!(
123 "polynomial from {from:?} has the wrong length"
124 )));
125 }
126 if !all_commitments[from].check(&their_big_f, &their_randomizer) {
127 return Err(ProtocolError::AssertionFailed(format!(
128 "commitment from {from:?} did not match revealed F"
129 )));
130 }
131 let statement = dlog::Statement::<C> {
132 public: &their_big_f.evaluate_zero(),
133 };
134 if !dlog::verify(
135 &mut transcript.forked(b"dlog0", &from.bytes()),
136 statement,
137 &their_phi_proof,
138 ) {
139 return Err(ProtocolError::AssertionFailed(format!(
140 "dlog proof from {from:?} failed to verify"
141 )));
142 }
143 big_f += &their_big_f;
144 }
145
146 seen.clear();
148 seen.put(me);
149 while !seen.full() {
150 let (from, x_j_i): (_, ScalarPrimitive<C>) = chan.recv(wait3).await?;
151 if !seen.put(from) {
152 continue;
153 }
154 x_i += C::Scalar::from(x_j_i);
155 }
156
157 if big_f.evaluate(&me.scalar::<C>()) != C::ProjectivePoint::generator() * x_i {
159 return Err(ProtocolError::AssertionFailed(
160 "received bad private share".to_string(),
161 ));
162 }
163
164 let big_x = big_f.evaluate_zero();
166 match big_s {
167 Some(big_s) if big_s != big_x => {
168 return Err(ProtocolError::AssertionFailed(
169 "new public key does not match old public key".to_string(),
170 ))
171 }
172 _ => {}
173 };
174
175 Ok((x_i, big_x.into()))
177}
178
179#[derive(Debug, Clone)]
183pub struct KeygenOutput<C: CSCurve> {
184 pub private_share: C::Scalar,
185 pub public_key: C::AffinePoint,
186}
187
188async fn do_keygen<C: CSCurve>(
189 chan: SharedChannel,
190 participants: ParticipantList,
191 me: Participant,
192 threshold: usize,
193) -> Result<KeygenOutput<C>, ProtocolError> {
194 let s_i = C::Scalar::random(&mut OsRng);
195 let (private_share, public_key) =
196 do_keyshare::<C>(chan, participants, me, threshold, s_i, None).await?;
197 Ok(KeygenOutput {
198 private_share,
199 public_key,
200 })
201}
202
203pub fn keygen<C: CSCurve>(
212 participants: &[Participant],
213 me: Participant,
214 threshold: usize,
215) -> Result<impl Protocol<Output = KeygenOutput<C>>, InitializationError> {
216 if participants.len() < 2 {
217 return Err(InitializationError::BadParameters(format!(
218 "participant count cannot be < 2, found: {}",
219 participants.len()
220 )));
221 };
222 if threshold > participants.len() {
224 return Err(InitializationError::BadParameters(
225 "threshold must be <= participant count".to_string(),
226 ));
227 }
228
229 let participants = ParticipantList::new(participants).ok_or_else(|| {
230 InitializationError::BadParameters("participant list cannot contain duplicates".to_string())
231 })?;
232
233 if !participants.contains(me) {
234 return Err(InitializationError::BadParameters(
235 "participant list must contain this participant".to_string(),
236 ));
237 }
238
239 let ctx = Context::new();
240 let fut = do_keygen(ctx.shared_channel(), participants, me, threshold);
241 Ok(make_protocol(ctx, fut))
242}
243
244async fn do_reshare<C: CSCurve>(
245 chan: SharedChannel,
246 participants: ParticipantList,
247 old_subset: ParticipantList,
248 me: Participant,
249 threshold: usize,
250 my_share: Option<C::Scalar>,
251 public_key: C::AffinePoint,
252) -> Result<C::Scalar, ProtocolError> {
253 let s_i = my_share
254 .map(|x_i| old_subset.lagrange::<C>(me) * x_i)
255 .unwrap_or(C::Scalar::ZERO);
256 let big_s: C::ProjectivePoint = public_key.into();
257 let (private_share, _) =
258 do_keyshare::<C>(chan, participants, me, threshold, s_i, Some(big_s)).await?;
259 Ok(private_share)
260}
261
262pub fn reshare<C: CSCurve>(
273 old_participants: &[Participant],
274 old_threshold: usize,
275 new_participants: &[Participant],
276 new_threshold: usize,
277 me: Participant,
278 my_share: Option<C::Scalar>,
279 public_key: C::AffinePoint,
280) -> Result<impl Protocol<Output = C::Scalar>, InitializationError> {
281 if new_participants.len() < 2 {
282 return Err(InitializationError::BadParameters(format!(
283 "participant count cannot be < 2, found: {}",
284 new_participants.len()
285 )));
286 };
287 if new_threshold > new_participants.len() {
289 return Err(InitializationError::BadParameters(
290 "threshold must be <= participant count".to_string(),
291 ));
292 }
293
294 let new_participants = ParticipantList::new(new_participants).ok_or_else(|| {
295 InitializationError::BadParameters(
296 "new participant list cannot contain duplicates".to_string(),
297 )
298 })?;
299
300 if !new_participants.contains(me) {
301 return Err(InitializationError::BadParameters(
302 "new participant list must contain this participant".to_string(),
303 ));
304 }
305
306 let old_participants = ParticipantList::new(old_participants).ok_or_else(|| {
307 InitializationError::BadParameters(
308 "old participant list cannot contain duplicates".to_string(),
309 )
310 })?;
311
312 let old_subset = old_participants.intersection(&new_participants);
313 if old_subset.len() < old_threshold {
314 return Err(InitializationError::BadParameters(
315 "not enough old participants to reconstruct private key for resharing".to_string(),
316 ));
317 }
318
319 if old_subset.contains(me) && my_share.is_none() {
320 return Err(InitializationError::BadParameters(
321 "this party is present in the old participant list but provided no share".to_string(),
322 ));
323 }
324
325 let ctx = Context::new();
326 let fut = do_reshare::<C>(
327 ctx.shared_channel(),
328 new_participants,
329 old_subset,
330 me,
331 new_threshold,
332 my_share,
333 public_key,
334 );
335 Ok(make_protocol(ctx, fut))
336}
337
338pub fn refresh<C: CSCurve>(
343 participants: &[Participant],
344 threshold: usize,
345 me: Participant,
346 my_share: C::Scalar,
347 public_key: C::AffinePoint,
348) -> Result<impl Protocol<Output = C::Scalar>, InitializationError> {
349 reshare::<C>(
350 participants,
351 threshold,
352 participants,
353 threshold,
354 me,
355 Some(my_share),
356 public_key,
357 )
358}
359
360#[cfg(test)]
361mod test {
362 use std::error::Error;
363
364 use k256::{ProjectivePoint, Scalar, Secp256k1};
365
366 use super::*;
367 use crate::protocol::{run_protocol, Participant};
368
369 #[allow(clippy::type_complexity)]
370 fn do_keygen(
371 participants: &[Participant],
372 threshold: usize,
373 ) -> Result<Vec<(Participant, KeygenOutput<Secp256k1>)>, Box<dyn Error>> {
374 let mut protocols: Vec<(
375 Participant,
376 Box<dyn Protocol<Output = KeygenOutput<Secp256k1>>>,
377 )> = Vec::with_capacity(participants.len());
378
379 for p in participants.iter() {
380 let protocol = keygen(participants, *p, threshold)?;
381 protocols.push((*p, Box::new(protocol)));
382 }
383
384 let result = run_protocol(protocols)?;
385 Ok(result)
386 }
387
388 #[test]
389 fn test_keygen() -> Result<(), Box<dyn Error>> {
390 let participants = vec![
391 Participant::from(0u32),
392 Participant::from(1u32),
393 Participant::from(2u32),
394 ];
395 let threshold = 3;
396
397 let result = do_keygen(&participants, threshold)?;
398 assert!(result.len() == participants.len());
399 assert_eq!(result[0].1.public_key, result[1].1.public_key);
400 assert_eq!(result[1].1.public_key, result[2].1.public_key);
401
402 let pub_key = result[2].1.public_key;
403
404 let participants = vec![result[0].0, result[1].0, result[2].0];
405 let shares = vec![
406 result[0].1.private_share,
407 result[1].1.private_share,
408 result[2].1.private_share,
409 ];
410 let p_list = ParticipantList::new(&participants).unwrap();
411 let x = p_list.lagrange::<Secp256k1>(participants[0]) * shares[0]
412 + p_list.lagrange::<Secp256k1>(participants[1]) * shares[1]
413 + p_list.lagrange::<Secp256k1>(participants[2]) * shares[2];
414 assert_eq!(ProjectivePoint::GENERATOR * x, pub_key);
415
416 Ok(())
417 }
418
419 #[test]
420 fn test_refresh() -> Result<(), Box<dyn Error>> {
421 let participants = vec![
422 Participant::from(0u32),
423 Participant::from(1u32),
424 Participant::from(2u32),
425 ];
426 let threshold = 3;
427
428 let result0 = do_keygen(&participants, threshold)?;
429
430 let pub_key = result0[2].1.public_key;
431
432 let mut protocols: Vec<(Participant, Box<dyn Protocol<Output = Scalar>>)> =
434 Vec::with_capacity(participants.len());
435
436 for (p, out) in result0.iter() {
437 let protocol = refresh::<Secp256k1>(
438 &participants,
439 threshold,
440 *p,
441 out.private_share,
442 out.public_key,
443 )?;
444 protocols.push((*p, Box::new(protocol)));
445 }
446
447 let result1 = run_protocol(protocols)?;
448
449 let participants = vec![result1[0].0, result1[1].0, result1[2].0];
450 let shares = vec![result1[0].1, result1[1].1, result1[2].1];
451 let p_list = ParticipantList::new(&participants).unwrap();
452 let x = p_list.lagrange::<Secp256k1>(participants[0]) * shares[0]
453 + p_list.lagrange::<Secp256k1>(participants[1]) * shares[1]
454 + p_list.lagrange::<Secp256k1>(participants[2]) * shares[2];
455 assert_eq!(ProjectivePoint::GENERATOR * x, pub_key);
456
457 Ok(())
458 }
459
460 #[test]
461 fn test_reshare() -> Result<(), Box<dyn Error>> {
462 let participants = vec![
463 Participant::from(0u32),
464 Participant::from(1u32),
465 Participant::from(2u32),
466 Participant::from(3u32),
467 ];
468 let threshold0 = 3;
469 let threshold1 = 4;
470
471 let result0 = do_keygen(&participants[..3], threshold0)?;
472
473 let pub_key = result0[2].1.public_key;
474
475 let mut setup: Vec<_> = result0
477 .into_iter()
478 .map(|(p, out)| (p, (Some(out.private_share), out.public_key)))
479 .collect();
480 setup.push((Participant::from(3u32), (None, pub_key)));
481
482 let mut protocols: Vec<(Participant, Box<dyn Protocol<Output = Scalar>>)> =
483 Vec::with_capacity(participants.len());
484
485 for (p, out) in setup.iter() {
486 let protocol = reshare::<Secp256k1>(
487 &participants[..3],
488 threshold0,
489 &participants,
490 threshold1,
491 *p,
492 out.0,
493 out.1,
494 )?;
495 protocols.push((*p, Box::new(protocol)));
496 }
497
498 let result1 = run_protocol(protocols)?;
499
500 let participants = vec![result1[0].0, result1[1].0, result1[2].0, result1[3].0];
501 let shares = vec![result1[0].1, result1[1].1, result1[2].1, result1[3].1];
502 let p_list = ParticipantList::new(&participants).unwrap();
503 let x = p_list.lagrange::<Secp256k1>(participants[0]) * shares[0]
504 + p_list.lagrange::<Secp256k1>(participants[1]) * shares[1]
505 + p_list.lagrange::<Secp256k1>(participants[2]) * shares[2]
506 + p_list.lagrange::<Secp256k1>(participants[3]) * shares[3];
507 assert_eq!(ProjectivePoint::GENERATOR * x, pub_key);
508
509 Ok(())
510 }
511}