frost_dalek/keygen.rs
1// -*- mode: rust; -*-
2//
3// This file is part of dalek-frost.
4// Copyright (c) 2020 isis lovecruft
5// See LICENSE for licensing information.
6//
7// Authors:
8// - isis agora lovecruft <isis@patternsinthevoid.net>
9
10//! A variation of Pedersen's distributed key generation (DKG) protocol.
11//!
12//! This implementation uses the [typestate] design pattern (also called session
13//! types) behind the scenes to enforce that more programming errors are discoverable
14//! at compile-time. Additionally, secrets generated for commitment openings, secret keys,
15//! nonces in zero-knowledge proofs, etc., are zeroed-out in memory when they are dropped
16//! out of scope.
17//!
18//! # Details
19//!
20//! ## Round One
21//!
22//! * Step #1: Every participant \\(P\_i\\) samples \\(t\\) random values \\((a\_{i0}, \\dots, a\_{i(t-1)})\\)
23//! uniformly in \\(\mathbb{Z}\_q\\), and uses these values as coefficients to define a
24//! polynomial \\(f\_i\(x\) = \sum\_{j=0}^{t-1} a\_{ij} x^{j}\\) of degree \\( t-1 \\) over
25//! \\(\mathbb{Z}\_q\\).
26//!
27//! (Yes, I know the steps are out-of-order. These are the step numbers as given in the paper. I do them
28//! out-of-order because it saves one scalar multiplication.)
29//!
30//! * Step #3: Every participant \\(P\_i\\) computes a public commitment
31//! \\(C\_i = \[\phi\_{i0}, \\dots, \phi\_{i(t-1)}\]\\), where \\(\phi\_{ij} = g^{a\_{ij}}\\),
32//! \\(0 \le j \le t-1\\).
33//!
34//! * Step #2: Every \\(P\_i\\) computes a proof of knowledge to the corresponding secret key
35//! \\(a\_{i0}\\) by calculating a pseudo-Schnorr signature \\(\sigma\_i = \(s, r\)\\). (In
36//! the FROST paper: \\(\sigma\_i = \(\mu\_i, c\_i\)\\), but we stick with Schnorr's
37//! original notation here.)
38//!
39//! * Step #4: Every participant \\(P\_i\\) broadcasts \\(\(C\_i\\), \\(\sigma\_i\)\\) to all other participants.
40//!
41//! * Step #5: Upon receiving \\((C\_l, \sigma\_l)\\) from participants \\(1 \le l \le n\\), \\(l \ne i\\),
42//! participant \\(P\_i\\) verifies \\(\sigma\_l = (s\_l, r\_l)\\), by checking:
43//! \\(s\_l \stackrel{?}{=} \mathcal{H}(l, \Phi, \phi\_{l0}, g^{r\_l} \cdot \phi\_{l0}^{-s\_i})\\).
44//! If any participants' proofs cannot be verified, return their participant indices.
45//!
46//! ## Round Two
47//!
48//! * Step #1: Each \\(P\_i\\) securely sends to each other participant \\(P\_l\\) a secret share
49//! \\((l, f\_i(l))\\) using their secret polynomial \\(f\_i(l)\\) and keeps \\((i, f\_i(i))\\)
50//! for themselves.
51//!
52//! * Step #2: Each \\(P\_i\\) verifies their shares by calculating:
53//! \\(g^{f\_l(i)} \stackrel{?}{=} \prod\_{k=0}^{n-1} \\)\\(\phi\_{lk}^{i^{k} \mod q}\\),
54//! aborting if the check fails.
55//!
56//! * Step #3: Each \\(P\_i\\) calculates their secret signing key as the product of all the secret
57//! polynomial evaluations (including their own):
58//! \\(a\_i = g^{f\_i(i)} \cdot \prod\_{l=0}^{n-1} g^{f\_l(i)}\\), as well as calculating
59//! the group public key in similar fashion from the commitments from round one:
60//! \\(A = C\_i \cdot \prod\_{l=0}^{n-1} C_l\\).
61//!
62//! # Examples
63//!
64//! ```rust
65//! use frost_dalek::DistributedKeyGeneration;
66//! use frost_dalek::Parameters;
67//! use frost_dalek::Participant;
68//!
69//! # fn do_test() -> Result<(), ()> {
70//! // Set up key shares for a threshold signature scheme which needs at least
71//! // 2-out-of-3 signers.
72//! let params = Parameters { t: 2, n: 3 };
73//!
74//! // Alice, Bob, and Carol each generate their secret polynomial coefficients
75//! // and commitments to them, as well as a zero-knowledge proof of a secret key.
76//! let (alice, alice_coeffs) = Participant::new(¶ms, 1);
77//! let (bob, bob_coeffs) = Participant::new(¶ms, 2);
78//! let (carol, carol_coeffs) = Participant::new(¶ms, 3);
79//!
80//! // They send these values to each of the other participants (out of scope
81//! // for this library), or otherwise publish them somewhere.
82//! //
83//! // alice.send_to(bob);
84//! // alice.send_to(carol);
85//! // bob.send_to(alice);
86//! // bob.send_to(carol);
87//! // carol.send_to(alice);
88//! // carol.send_to(bob);
89//! //
90//! // NOTE: They should only send the `alice`, `bob`, and `carol` structs, *not*
91//! // the `alice_coefficients`, etc.
92//! //
93//! // Bob and Carol verify Alice's zero-knowledge proof by doing:
94//!
95//! alice.proof_of_secret_key.verify(&alice.index, &alice.public_key().unwrap())?;
96//!
97//! // Similarly, Alice and Carol verify Bob's proof:
98//! bob.proof_of_secret_key.verify(&bob.index, &bob.public_key().unwrap())?;
99//!
100//! // And, again, Alice and Bob verify Carol's proof:
101//! carol.proof_of_secret_key.verify(&carol.index, &carol.public_key().unwrap())?;
102//!
103//! // Alice enters round one of the distributed key generation protocol.
104//! let mut alice_other_participants: Vec<Participant> = vec!(bob.clone(), carol.clone());
105//! let alice_state = DistributedKeyGeneration::<_>::new(¶ms, &alice.index, &alice_coeffs,
106//! &mut alice_other_participants).or(Err(()))?;
107//!
108//! // Alice then collects the secret shares which they send to the other participants:
109//! let alice_their_secret_shares = alice_state.their_secret_shares()?;
110//! // send_to_bob(alice_their_secret_shares[0]);
111//! // send_to_carol(alice_their_secret_shares[1]);
112//!
113//! // Bob enters round one of the distributed key generation protocol.
114//! let mut bob_other_participants: Vec<Participant> = vec!(alice.clone(), carol.clone());
115//! let bob_state = DistributedKeyGeneration::<_>::new(¶ms, &bob.index, &bob_coeffs,
116//! &mut bob_other_participants).or(Err(()))?;
117//!
118//! // Bob then collects the secret shares which they send to the other participants:
119//! let bob_their_secret_shares = bob_state.their_secret_shares()?;
120//! // send_to_alice(bob_their_secret_shares[0]);
121//! // send_to_carol(bob_their_secret_shares[1]);
122//!
123//! // Carol enters round one of the distributed key generation protocol.
124//! let mut carol_other_participants: Vec<Participant> = vec!(alice.clone(), bob.clone());
125//! let carol_state = DistributedKeyGeneration::<_>::new(¶ms, &carol.index, &carol_coeffs,
126//! &mut carol_other_participants).or(Err(()))?;
127//!
128//! // Carol then collects the secret shares which they send to the other participants:
129//! let carol_their_secret_shares = carol_state.their_secret_shares()?;
130//! // send_to_alice(carol_their_secret_shares[0]);
131//! // send_to_bob(carol_their_secret_shares[1]);
132//!
133//! // Each participant now has a vector of secret shares given to them by the other participants:
134//! let alice_my_secret_shares = vec!(bob_their_secret_shares[0].clone(),
135//! carol_their_secret_shares[0].clone());
136//! let bob_my_secret_shares = vec!(alice_their_secret_shares[0].clone(),
137//! carol_their_secret_shares[1].clone());
138//! let carol_my_secret_shares = vec!(alice_their_secret_shares[1].clone(),
139//! bob_their_secret_shares[1].clone());
140//!
141//! // The participants then use these secret shares from the other participants to advance to
142//! // round two of the distributed key generation protocol.
143//! let alice_state = alice_state.to_round_two(alice_my_secret_shares)?;
144//! let bob_state = bob_state.to_round_two(bob_my_secret_shares)?;
145//! let carol_state = carol_state.to_round_two(carol_my_secret_shares)?;
146//!
147//! // Each participant can now derive their long-lived secret keys and the group's
148//! // public key.
149//! let (alice_group_key, alice_secret_key) = alice_state.finish(alice.public_key().unwrap())?;
150//! let (bob_group_key, bob_secret_key) = bob_state.finish(bob.public_key().unwrap())?;
151//! let (carol_group_key, carol_secret_key) = carol_state.finish(carol.public_key().unwrap())?;
152//!
153//! // They should all derive the same group public key.
154//! assert!(alice_group_key == bob_group_key);
155//! assert!(carol_group_key == bob_group_key);
156//!
157//! // Alice, Bob, and Carol can now create partial threshold signatures over an agreed upon
158//! // message with their respective secret keys, which they can then give to a
159//! // [`SignatureAggregator`] to create a 2-out-of-3 threshold signature.
160//! # Ok(())}
161//! # fn main() { assert!(do_test().is_ok()); }
162//! ```
163//!
164//! [typestate]: http://cliffle.com/blog/rust-typestate/
165
166#[cfg(feature = "std")]
167use std::boxed::Box;
168#[cfg(feature = "std")]
169use std::vec::Vec;
170
171#[cfg(feature = "std")]
172use std::cmp::Ordering;
173#[cfg(not(feature = "std"))]
174use core::cmp::Ordering;
175
176#[cfg(feature = "alloc")]
177use alloc::boxed::Box;
178#[cfg(feature = "alloc")]
179use alloc::vec::Vec;
180
181use curve25519_dalek::constants::RISTRETTO_BASEPOINT_TABLE;
182use curve25519_dalek::ristretto::CompressedRistretto;
183use curve25519_dalek::ristretto::RistrettoPoint;
184use curve25519_dalek::scalar::Scalar;
185use curve25519_dalek::traits::Identity;
186
187use rand::rngs::OsRng;
188
189use zeroize::Zeroize;
190
191use crate::nizk::NizkOfSecretKey;
192use crate::parameters::Parameters;
193
194/// A struct for holding a shard of the shared secret, in order to ensure that
195/// the shard is overwritten with zeroes when it falls out of scope.
196#[derive(Zeroize)]
197#[zeroize(drop)]
198pub struct Coefficients(pub(crate) Vec<Scalar>);
199
200/// A commitment to the dealer's secret polynomial coefficients for Feldman's
201/// verifiable secret sharing scheme.
202#[derive(Clone, Debug)]
203pub struct VerifiableSecretSharingCommitment(pub(crate) Vec<RistrettoPoint>);
204
205/// A participant created by a trusted dealer.
206///
207/// This can be used to create the participants' keys and secret shares without
208/// having to do secret sharing or zero-knowledge proofs. It's mostly provided
209/// for testing and debugging purposes, but there is nothing wrong with using it
210/// if you have trust in the dealer to not forge rogue signatures.
211#[derive(Clone, Debug)]
212pub struct DealtParticipant {
213 pub(crate) secret_share: SecretShare,
214 pub(crate) public_key: IndividualPublicKey,
215 pub(crate) group_key: RistrettoPoint,
216}
217
218/// A participant in a threshold signing.
219#[derive(Clone, Debug)]
220pub struct Participant {
221 /// The index of this participant, to keep the participants in order.
222 pub index: u32,
223 /// A vector of Pedersen commitments to the coefficients of this
224 /// participant's private polynomial.
225 pub commitments: Vec<RistrettoPoint>,
226 /// The zero-knowledge proof of knowledge of the secret key (a.k.a. the
227 /// first coefficient in the private polynomial). It is constructed as a
228 /// Schnorr signature using \\( a_{i0} \\) as the signing key.
229 pub proof_of_secret_key: NizkOfSecretKey,
230}
231
232impl Participant {
233 /// Have a trusted dealer generate all participants' key material and
234 /// associated commitments for distribution to the participants.
235 ///
236 /// # Warning
237 ///
238 /// Each participant MUST verify with all other n-1 participants that the
239 /// [`VerifiableSecretSharingCommitment`] given to them by the dealer is
240 /// identical. Otherwise, the participants' secret shares could be formed
241 /// with respect to different polynomials and they will fail to create
242 /// threshold signatures which validate.
243 pub fn dealer(parameters: &Parameters) -> (Vec<DealtParticipant>, VerifiableSecretSharingCommitment) {
244 let mut rng: OsRng = OsRng;
245 let secret = Scalar::random(&mut rng);
246
247 generate_shares(parameters, secret, rng)
248 }
249
250 /// Construct a new participant for the distributed key generation protocol.
251 ///
252 /// # Inputs
253 ///
254 /// * The protocol instance [`Parameters`], and
255 /// * This participant's `index`.
256 ///
257 /// # Usage
258 ///
259 /// After a new participant is constructed, the `participant.index`,
260 /// `participant.commitments`, and `participant.proof_of_secret_key` should
261 /// be sent to every other participant in the protocol.
262 ///
263 /// # Returns
264 ///
265 /// A distributed key generation protocol [`Participant`] and that
266 /// participant's secret polynomial `Coefficients` which must be kept
267 /// private.
268 pub fn new(parameters: &Parameters, index: u32) -> (Self, Coefficients) {
269 // Step 1: Every participant P_i samples t random values (a_{i0}, ..., a_{i(t-1)})
270 // uniformly in ZZ_q, and uses these values as coefficients to define a
271 // polynomial f_i(x) = \sum_{j=0}^{t-1} a_{ij} x^{j} of degree t-1 over
272 // ZZ_q.
273 let t: usize = parameters.t as usize;
274 let mut rng: OsRng = OsRng;
275 let mut coefficients: Vec<Scalar> = Vec::with_capacity(t);
276 let mut commitments: Vec<RistrettoPoint> = Vec::with_capacity(t);
277
278 for _ in 0..t {
279 coefficients.push(Scalar::random(&mut rng));
280 }
281
282 let coefficients = Coefficients(coefficients);
283
284 // Step 3: Every participant P_i computes a public commitment
285 // C_i = [\phi_{i0}, ..., \phi_{i(t-1)}], where \phi_{ij} = g^{a_{ij}},
286 // 0 ≤ j ≤ t-1.
287 for j in 0..t {
288 commitments.push(&coefficients.0[j] * &RISTRETTO_BASEPOINT_TABLE);
289 }
290
291 // Yes, I know the steps are out of order. It saves one scalar multiplication.
292
293 // Step 2: Every P_i computes a proof of knowledge to the corresponding secret
294 // a_{i0} by calculating a Schnorr signature \alpha_i = (s, R). (In
295 // the FROST paper: \alpha_i = (\mu_i, c_i), but we stick with Schnorr's
296 // original notation here.)
297 let proof: NizkOfSecretKey = NizkOfSecretKey::prove(&index, &coefficients.0[0], &commitments[0], rng);
298
299 // Step 4: Every participant P_i broadcasts C_i, \alpha_i to all other participants.
300 (Participant { index, commitments, proof_of_secret_key: proof }, coefficients)
301 }
302
303 /// Retrieve \\( \alpha_{i0} * B \\), where \\( B \\) is the Ristretto basepoint.
304 ///
305 /// This is used to pass into the final call to `DistributedKeyGeneration::<RoundTwo>.finish()`.
306 pub fn public_key(&self) -> Option<&RistrettoPoint> {
307 if !self.commitments.is_empty() {
308 return Some(&self.commitments[0]);
309 }
310 None
311 }
312}
313
314fn generate_shares(parameters: &Parameters, secret: Scalar, mut rng: OsRng) -> (Vec<DealtParticipant>, VerifiableSecretSharingCommitment) {
315 let mut participants: Vec<DealtParticipant> = Vec::with_capacity(parameters.n as usize);
316
317 // STEP 1: Every participant P_i samples t random values (a_{i0}, ..., a_{i(t-1)})
318 // uniformly in ZZ_q, and uses these values as coefficients to define a
319 // polynomial f_i(x) = \sum_{j=0}^{t-1} a_{ij} x^{j} of degree t-1 over
320 // ZZ_q.
321 let t: usize = parameters.t as usize;
322 let mut coefficients: Vec<Scalar> = Vec::with_capacity(t as usize);
323 let mut commitment = VerifiableSecretSharingCommitment(Vec::with_capacity(t as usize));
324
325 coefficients.push(secret);
326 for _ in 0..t-1 {
327 coefficients.push(Scalar::random(&mut rng));
328 }
329
330 let coefficients = Coefficients(coefficients);
331
332 // Step 3: Every participant P_i computes a public commitment
333 // C_i = [\phi_{i0}, ..., \phi_{i(t-1)}], where \phi_{ij} = g^{a_{ij}},
334 // 0 ≤ j ≤ t-1.
335 for j in 0..t {
336 commitment.0.push(&coefficients.0[j] * &RISTRETTO_BASEPOINT_TABLE);
337 }
338
339 // Generate secret shares here
340 let group_key = &RISTRETTO_BASEPOINT_TABLE * &coefficients.0[0];
341
342 // Only one polynomial because dealer, then secret shards are dependent upon index.
343 for i in 1..parameters.n + 1 {
344 let secret_share = SecretShare::evaluate_polynomial(&i, &coefficients);
345 let public_key = IndividualPublicKey {
346 index: i,
347 share: &RISTRETTO_BASEPOINT_TABLE * &secret_share.polynomial_evaluation,
348 };
349
350 participants.push(DealtParticipant { secret_share, public_key, group_key });
351 }
352 (participants, commitment)
353}
354
355impl PartialOrd for Participant {
356 fn partial_cmp(&self, other: &Participant) -> Option<Ordering> {
357 match self.index.cmp(&other.index) {
358 Ordering::Less => Some(Ordering::Less),
359 Ordering::Equal => None, // Participants cannot have the same index.
360 Ordering::Greater => Some(Ordering::Greater),
361 }
362 }
363}
364
365impl PartialEq for Participant {
366 fn eq(&self, other: &Participant) -> bool {
367 self.index == other.index
368 }
369}
370
371/// Module to implement trait sealing so that `DkgState` cannot be
372/// implemented for externally declared types.
373mod private {
374 pub trait Sealed {}
375
376 impl Sealed for super::RoundOne {}
377 impl Sealed for super::RoundTwo {}
378}
379
380/// State machine structures for holding intermediate values during a
381/// distributed key generation protocol run, to prevent misuse.
382#[derive(Clone, Debug)]
383pub struct DistributedKeyGeneration<S: DkgState> {
384 state: Box<ActualState>,
385 data: S,
386}
387
388/// Shared state which occurs across all rounds of a threshold signing protocol run.
389#[derive(Clone, Debug)]
390struct ActualState {
391 /// The parameters for this instantiation of a threshold signature.
392 parameters: Parameters,
393 /// A vector of tuples containing the index of each participant and that
394 /// respective participant's commitments to their private polynomial
395 /// coefficients.
396 their_commitments: Vec<(u32, VerifiableSecretSharingCommitment)>,
397 /// A secret share for this participant.
398 my_secret_share: SecretShare,
399 /// The secret shares this participant has calculated for all the other participants.
400 their_secret_shares: Option<Vec<SecretShare>>,
401 /// The secret shares this participant has received from all the other participants.
402 my_secret_shares: Option<Vec<SecretShare>>,
403}
404
405/// Marker trait to designate valid rounds in the distributed key generation
406/// protocol's state machine. It is implemented using the [sealed trait design
407/// pattern][sealed] pattern to prevent external types from implementing further
408/// valid states.
409///
410/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
411pub trait DkgState: private::Sealed {}
412
413impl DkgState for RoundOne {}
414impl DkgState for RoundTwo {}
415
416/// Marker trait to designate valid variants of [`RoundOne`] in the distributed
417/// key generation protocol's state machine. It is implemented using the
418/// [sealed trait design pattern][sealed] pattern to prevent external types from
419/// implementing further valid states.
420///
421/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
422pub trait Round1: private::Sealed {}
423
424/// Marker trait to designate valid variants of [`RoundTwo`] in the distributed
425/// key generation protocol's state machine. It is implemented using the
426/// [sealed trait design pattern][sealed] pattern to prevent external types from
427/// implementing further valid states.
428///
429/// [sealed]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
430pub trait Round2: private::Sealed {}
431
432impl Round1 for RoundOne {}
433impl Round2 for RoundTwo {}
434
435/// Every participant in the distributed key generation has sent a vector of
436/// commitments and a zero-knowledge proof of a secret key to every other
437/// participant in the protocol. During round one, each participant checks the
438/// zero-knowledge proofs of secret keys of all other participants.
439#[derive(Clone, Debug)]
440pub struct RoundOne {}
441
442impl DistributedKeyGeneration<RoundOne> {
443 /// Check the zero-knowledge proofs of knowledge of secret keys of all the
444 /// other participants.
445 ///
446 /// # Note
447 ///
448 /// The `participants` will be sorted by their indices.
449 ///
450 /// # Returns
451 ///
452 /// An updated state machine for the distributed key generation protocol if
453 /// all of the zero-knowledge proofs verified successfully, otherwise a
454 /// vector of participants whose zero-knowledge proofs were incorrect.
455 pub fn new(
456 parameters: &Parameters,
457 my_index: &u32,
458 my_coefficients: &Coefficients,
459 other_participants: &mut Vec<Participant>,
460 ) -> Result<Self, Vec<u32>>
461 {
462 let mut their_commitments: Vec<(u32, VerifiableSecretSharingCommitment)> = Vec::with_capacity(parameters.t as usize);
463 let mut misbehaving_participants: Vec<u32> = Vec::new();
464
465 // Bail if we didn't get enough participants.
466 if other_participants.len() != parameters.n as usize - 1 {
467 return Err(misbehaving_participants);
468 }
469
470 // Step 5: Upon receiving C_l, \sigma_l from participants 1 \le l \le n, l \ne i,
471 // participant P_i verifies \sigma_l = (s_l, r_l), by checking:
472 //
473 // s_l ?= H(l, \Phi, \phi_{l0}, g^{r_l} \mdot \phi_{l0}^{-s_i})
474 for p in other_participants.iter() {
475 let public_key = match p.commitments.get(0) {
476 Some(key) => key,
477 None => {
478 misbehaving_participants.push(p.index);
479 continue;
480 }
481 };
482 match p.proof_of_secret_key.verify(&p.index, &public_key) {
483 Ok(_) => their_commitments.push((p.index, VerifiableSecretSharingCommitment(p.commitments.clone()))),
484 Err(_) => misbehaving_participants.push(p.index),
485 }
486 }
487
488 // [DIFFERENT_TO_PAPER] If any participant was misbehaving, return their indices.
489 if !misbehaving_participants.is_empty() {
490 return Err(misbehaving_participants);
491 }
492
493 // [DIFFERENT_TO_PAPER] We pre-calculate the secret shares from Round 2
494 // Step 1 here since it doesn't require additional online activity.
495 //
496 // Round 2
497 // Step 1: Each P_i securely sends to each other participant P_l a secret share
498 // (l, f_i(l)) and keeps (i, f_i(i)) for themselves.
499 let mut their_secret_shares: Vec<SecretShare> = Vec::with_capacity(parameters.n as usize - 1);
500
501 // XXX need a way to index their_secret_shares
502 for p in other_participants.iter() {
503 their_secret_shares.push(SecretShare::evaluate_polynomial(&p.index, my_coefficients));
504 }
505
506 let my_secret_share = SecretShare::evaluate_polynomial(my_index, my_coefficients);
507 let state = ActualState {
508 parameters: *parameters,
509 their_commitments,
510 my_secret_share,
511 their_secret_shares: Some(their_secret_shares),
512 my_secret_shares: None,
513 };
514
515 Ok(DistributedKeyGeneration::<RoundOne> {
516 state: Box::new(state),
517 data: RoundOne {},
518 })
519 }
520
521 /// Retrieve a secret share for each other participant, to be given to them
522 /// at the end of `DistributedKeyGeneration::<RoundOne>`.
523 pub fn their_secret_shares(&self) -> Result<&Vec<SecretShare>, ()> {
524 self.state.their_secret_shares.as_ref().ok_or(())
525 }
526
527 /// Progress to round two of the DKG protocol once we have sent each share
528 /// from `DistributedKeyGeneration::<RoundOne>.their_secret_shares()` to its
529 /// respective other participant, and collected our shares from the other
530 /// participants in turn.
531 #[allow(clippy::wrong_self_convention)]
532 pub fn to_round_two(
533 mut self,
534 my_secret_shares: Vec<SecretShare>,
535 ) -> Result<DistributedKeyGeneration<RoundTwo>, ()>
536 {
537 // Zero out the other participants secret shares from memory.
538 if self.state.their_secret_shares.is_some() {
539 self.state.their_secret_shares.unwrap().zeroize();
540 // XXX Does setting this to None always call drop()?
541 self.state.their_secret_shares = None;
542 }
543
544 if my_secret_shares.len() != self.state.parameters.n as usize - 1 {
545 return Err(());
546 }
547
548 // Step 2: Each P_i verifies their shares by calculating:
549 // g^{f_l(i)} ?= \Prod_{k=0}^{t-1} \phi_{lk}^{i^{k} mod q},
550 // aborting if the check fails.
551 for share in my_secret_shares.iter() {
552 // XXX TODO implement sorting for SecretShare and also for a new Commitment type
553 for (index, commitment) in self.state.their_commitments.iter() {
554 if index == &share.index {
555 share.verify(commitment)?;
556 }
557 }
558 }
559 self.state.my_secret_shares = Some(my_secret_shares);
560
561 Ok(DistributedKeyGeneration::<RoundTwo> {
562 state: self.state,
563 data: RoundTwo {},
564 })
565 }
566}
567
568/// A secret share calculated by evaluating a polynomial with secret
569/// coefficients for some indeterminant.
570#[derive(Clone, Debug, Zeroize)]
571#[zeroize(drop)]
572pub struct SecretShare {
573 /// The participant index that this secret share was calculated for.
574 pub index: u32,
575 /// The final evaluation of the polynomial for the participant-respective
576 /// indeterminant.
577 pub(crate) polynomial_evaluation: Scalar,
578}
579
580impl SecretShare {
581 /// Evaluate the polynomial, `f(x)` for the secret coefficients at the value of `x`.
582 //
583 // XXX [PAPER] [CFRG] The participant index CANNOT be 0, or the secret share ends up being Scalar::zero().
584 pub(crate) fn evaluate_polynomial(index: &u32, coefficients: &Coefficients) -> SecretShare {
585 let term: Scalar = (*index).into();
586 let mut sum: Scalar = Scalar::zero();
587
588 // Evaluate using Horner's method.
589 for (index, coefficient) in coefficients.0.iter().rev().enumerate() {
590 // The secret is the constant term in the polynomial
591 sum += coefficient;
592
593 if index != (coefficients.0.len() - 1) {
594 sum *= term;
595 }
596 }
597 SecretShare { index: *index, polynomial_evaluation: sum }
598 }
599
600 /// Verify that this secret share was correctly computed w.r.t. some secret
601 /// polynomial coefficients attested to by some `commitment`.
602 pub(crate) fn verify(&self, commitment: &VerifiableSecretSharingCommitment) -> Result<(), ()> {
603 let lhs = &RISTRETTO_BASEPOINT_TABLE * &self.polynomial_evaluation;
604 let term: Scalar = self.index.into();
605 let mut rhs: RistrettoPoint = RistrettoPoint::identity();
606
607 for (index, com) in commitment.0.iter().rev().enumerate() {
608 rhs += com;
609
610 if index != (commitment.0.len() - 1) {
611 rhs *= term;
612 }
613 }
614
615 match lhs.compress() == rhs.compress() {
616 true => Ok(()),
617 false => Err(()),
618 }
619 }
620}
621
622/// During round two each participant verifies their secret shares they received
623/// from each other participant.
624#[derive(Clone, Debug)]
625pub struct RoundTwo {}
626
627impl DistributedKeyGeneration<RoundTwo> {
628 /// Calculate this threshold signing protocol participant's long-lived
629 /// secret signing keyshare and the group's public verification key.
630 ///
631 /// # Example
632 ///
633 /// ```ignore
634 /// let (group_key, secret_key) = state.finish(participant.public_key()?)?;
635 /// ```
636 pub fn finish(mut self, my_commitment: &RistrettoPoint) -> Result<(GroupKey, SecretKey), ()> {
637 let secret_key = self.calculate_signing_key()?;
638 let group_key = self.calculate_group_key(my_commitment)?;
639
640 self.state.my_secret_share.zeroize();
641 self.state.my_secret_shares.zeroize();
642
643 Ok((group_key, secret_key))
644 }
645
646 /// Calculate this threshold signing participant's long-lived secret signing
647 /// key by summing all of the polynomial evaluations from the other
648 /// participants.
649 pub(crate) fn calculate_signing_key(&self) -> Result<SecretKey, ()> {
650 let my_secret_shares = self.state.my_secret_shares.as_ref().ok_or(())?;
651 let mut key = my_secret_shares.iter().map(|x| x.polynomial_evaluation).sum();
652
653 key += self.state.my_secret_share.polynomial_evaluation;
654
655 Ok(SecretKey { index: self.state.my_secret_share.index, key })
656 }
657
658 /// Calculate the group public key used for verifying threshold signatures.
659 ///
660 /// # Returns
661 ///
662 /// A [`GroupKey`] for the set of participants.
663 pub(crate) fn calculate_group_key(&self, my_commitment: &RistrettoPoint) -> Result<GroupKey, ()> {
664 let mut keys: Vec<RistrettoPoint> = Vec::with_capacity(self.state.parameters.n as usize);
665
666 for commitment in self.state.their_commitments.iter() {
667 match commitment.1.0.get(0) {
668 Some(key) => keys.push(*key),
669 None => return Err(()),
670 }
671 }
672 keys.push(*my_commitment);
673
674 Ok(GroupKey(keys.iter().sum()))
675 }
676}
677
678/// A public verification share for a participant.
679///
680/// Any participant can recalculate the public verification share, which is the
681/// public half of a [`SecretKey`], of any other participant in the protocol.
682#[derive(Clone, Debug)]
683pub struct IndividualPublicKey {
684 /// The participant index to which this key belongs.
685 pub index: u32,
686 /// The public verification share.
687 pub share: RistrettoPoint,
688}
689
690impl IndividualPublicKey {
691 /// Any participant can compute the public verification share of any other participant.
692 ///
693 /// This is done by re-computing each [`IndividualPublicKey`] as \\(Y\_i\\) s.t.:
694 ///
695 /// \\[
696 /// Y\_i = \prod\_{j=1}^{n} \prod\_{k=0}^{t-1} \phi\_{jk}^{i^{k} \mod q}
697 /// \\]
698 ///
699 /// for each [`Participant`] index \\(i\\).
700 ///
701 /// # Inputs
702 ///
703 /// * The [`Parameters`] of this threshold signing instance, and
704 /// * A vector of `commitments` regarding the secret polynomial
705 /// [`Coefficients`] that this [`IndividualPublicKey`] was generated with.
706 ///
707 /// # Returns
708 ///
709 /// A `Result` with either an empty `Ok` or `Err` value, depending on
710 /// whether or not the verification was successful.
711 #[allow(unused)]
712 pub fn verify(
713 &self,
714 parameters: &Parameters,
715 commitments: &[RistrettoPoint],
716 ) -> Result<(), ()>
717 {
718 let rhs = RistrettoPoint::identity();
719
720 for j in 1..parameters.n {
721 for k in 0..parameters.t {
722 // XXX ah shit we need the incoming commitments to be sorted or have indices
723 }
724 }
725 unimplemented!()
726 }
727}
728
729/// A secret key, used by one participant in a threshold signature scheme, to sign a message.
730#[derive(Debug, Zeroize)]
731#[zeroize(drop)]
732pub struct SecretKey {
733 /// The participant index to which this key belongs.
734 pub(crate) index: u32,
735 /// The participant's long-lived secret share of the group signing key.
736 pub(crate) key: Scalar,
737}
738
739impl SecretKey {
740 /// Derive the corresponding public key for this secret key.
741 pub fn to_public(&self) -> IndividualPublicKey {
742 let share = &RISTRETTO_BASEPOINT_TABLE * &self.key;
743
744 IndividualPublicKey {
745 index: self.index,
746 share,
747 }
748 }
749}
750
751impl From<&SecretKey> for IndividualPublicKey {
752 fn from(source: &SecretKey) -> IndividualPublicKey {
753 source.to_public()
754 }
755}
756
757/// A public key, used to verify a signature made by a threshold of a group of participants.
758#[derive(Clone, Copy, Debug, Eq)]
759pub struct GroupKey(pub(crate) RistrettoPoint);
760
761impl PartialEq for GroupKey {
762 fn eq(&self, other: &Self) -> bool {
763 self.0.compress() == other.0.compress()
764 }
765}
766
767impl GroupKey {
768 /// Serialise this group public key to an array of bytes.
769 pub fn to_bytes(&self) -> [u8; 32] {
770 self.0.compress().to_bytes()
771 }
772
773 /// Deserialise this group public key from an array of bytes.
774 pub fn from_bytes(bytes: [u8; 32]) -> Result<GroupKey, ()> {
775 let point = CompressedRistretto(bytes).decompress().ok_or(())?;
776
777 Ok(GroupKey(point))
778 }
779}
780
781#[cfg(test)]
782mod test {
783 use super::*;
784
785 #[cfg(feature = "std")]
786 use crate::precomputation::generate_commitment_share_lists;
787
788 #[cfg(feature = "std")]
789 use crate::signature::{calculate_lagrange_coefficients, compute_message_hash};
790 #[cfg(feature = "std")]
791 use crate::signature::SignatureAggregator;
792
793 #[cfg(feature = "std")]
794 /// Reconstruct the secret from enough (at least the threshold) already-verified shares.
795 fn reconstruct_secret(participants: &Vec<&DealtParticipant>) -> Result<Scalar, &'static str> {
796 let all_participant_indices: Vec<u32> = participants.iter().map(|p| p.public_key.index).collect();
797 let mut secret = Scalar::zero();
798
799 for this_participant in participants {
800 let my_coeff = calculate_lagrange_coefficients(&this_participant.public_key.index,
801 &all_participant_indices)?;
802
803 secret += my_coeff * this_participant.secret_share.polynomial_evaluation;
804 }
805 Ok(secret)
806 }
807
808 #[test]
809 fn nizk_of_secret_key() {
810 let params = Parameters { n: 3, t: 2 };
811 let (p, _) = Participant::new(¶ms, 0);
812 let result = p.proof_of_secret_key.verify(&p.index, &p.commitments[0]);
813
814 assert!(result.is_ok());
815 }
816
817 #[cfg(feature = "std")]
818 #[test]
819 fn verify_secret_sharing_from_dealer() {
820 let params = Parameters { n: 3, t: 2 };
821 let mut rng: OsRng = OsRng;
822 let secret = Scalar::random(&mut rng);
823 let (participants, _commitment) = generate_shares(¶ms, secret, rng);
824
825 let mut subset_participants = Vec::new();
826 for i in 0..params.t{
827 subset_participants.push(&participants[i as usize]);
828 }
829 let supposed_secret = reconstruct_secret(&subset_participants);
830 assert!(secret == supposed_secret.unwrap());
831 }
832
833 #[test]
834 fn dkg_with_dealer() {
835 let params = Parameters { t: 1, n: 2 };
836 let (participants, commitment) = Participant::dealer(¶ms);
837 let (_, commitment2) = Participant::dealer(¶ms);
838
839 // Verify each of the participants' secret shares.
840 for p in participants.iter() {
841 let result = p.secret_share.verify(&commitment);
842
843 assert!(result.is_ok(), "participant {} failed to receive a valid secret share", p.public_key.index);
844
845 let result = p.secret_share.verify(&commitment2);
846
847 assert!(!result.is_ok(), "Should not validate with invalid commitment");
848 }
849 }
850
851 #[cfg(feature = "std")]
852 #[test]
853 fn dkg_with_dealer_and_signing() {
854 let params = Parameters { t: 1, n: 2 };
855 let (participants, commitment) = Participant::dealer(¶ms);
856
857 // Verify each of the participants' secret shares.
858 for p in participants.iter() {
859 let result = p.secret_share.verify(&commitment);
860
861 assert!(result.is_ok(), "participant {} failed to receive a valid secret share", p.public_key.index);
862 }
863
864 let context = b"CONTEXT STRING STOLEN FROM DALEK TEST SUITE";
865 let message = b"This is a test of the tsunami alert system. This is only a test.";
866 let (p1_public_comshares, mut p1_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 1, 1);
867 let (p2_public_comshares, mut p2_secret_comshares) = generate_commitment_share_lists(&mut OsRng, 2, 1);
868
869 let p1_sk = SecretKey {
870 index: participants[0].secret_share.index,
871 key: participants[0].secret_share.polynomial_evaluation,
872 };
873 let p2_sk = SecretKey {
874 index: participants[1].secret_share.index,
875 key: participants[1].secret_share.polynomial_evaluation,
876 };
877
878 let group_key = GroupKey(participants[0].group_key);
879
880 let mut aggregator = SignatureAggregator::new(params, group_key, &context[..], &message[..]);
881
882 aggregator.include_signer(1, p1_public_comshares.commitments[0], (&p1_sk).into());
883 aggregator.include_signer(2, p2_public_comshares.commitments[0], (&p2_sk).into());
884
885 let signers = aggregator.get_signers();
886 let message_hash = compute_message_hash(&context[..], &message[..]);
887
888 let p1_partial = p1_sk.sign(&message_hash, &group_key, &mut p1_secret_comshares, 0, signers).unwrap();
889 let p2_partial = p2_sk.sign(&message_hash, &group_key, &mut p2_secret_comshares, 0, signers).unwrap();
890
891 aggregator.include_partial_signature(p1_partial);
892 aggregator.include_partial_signature(p2_partial);
893
894 let aggregator = aggregator.finalize().unwrap();
895 let signing_result = aggregator.aggregate();
896
897 assert!(signing_result.is_ok());
898
899 let threshold_signature = signing_result.unwrap();
900
901 let verification_result = threshold_signature.verify(&group_key, &message_hash);
902
903 println!("{:?}", verification_result);
904
905 assert!(verification_result.is_ok());
906 }
907
908 #[test]
909 fn secret_share_from_one_coefficients() {
910 let mut coeffs: Vec<Scalar> = Vec::new();
911
912 for _ in 0..5 {
913 coeffs.push(Scalar::one());
914 }
915
916 let coefficients = Coefficients(coeffs);
917 let share = SecretShare::evaluate_polynomial(&1, &coefficients);
918
919 assert!(share.polynomial_evaluation == Scalar::from(5u8));
920
921 let mut commitments = VerifiableSecretSharingCommitment(Vec::new());
922
923 for i in 0..5 {
924 commitments.0.push(&RISTRETTO_BASEPOINT_TABLE * &coefficients.0[i]);
925 }
926
927 assert!(share.verify(&commitments).is_ok());
928 }
929
930 #[test]
931 fn secret_share_participant_index_zero() {
932 let mut coeffs: Vec<Scalar> = Vec::new();
933
934 for _ in 0..5 {
935 coeffs.push(Scalar::one());
936 }
937
938 let coefficients = Coefficients(coeffs);
939 let share = SecretShare::evaluate_polynomial(&0, &coefficients);
940
941 assert!(share.polynomial_evaluation == Scalar::one());
942
943 let mut commitments = VerifiableSecretSharingCommitment(Vec::new());
944
945 for i in 0..5 {
946 commitments.0.push(&RISTRETTO_BASEPOINT_TABLE * &coefficients.0[i]);
947 }
948
949 assert!(share.verify(&commitments).is_ok());
950 }
951
952 #[test]
953 fn single_party_keygen() {
954 let params = Parameters { n: 1, t: 1 };
955
956 let (p1, p1coeffs) = Participant::new(¶ms, 1);
957
958 p1.proof_of_secret_key.verify(&p1.index, &p1.commitments[0]).unwrap();
959
960 let mut p1_other_participants: Vec<Participant> = Vec::new();
961 let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
962 &p1.index,
963 &p1coeffs,
964 &mut p1_other_participants).unwrap();
965 let p1_my_secret_shares = Vec::new();
966 let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
967 let result = p1_state.finish(p1.public_key().unwrap());
968
969 assert!(result.is_ok());
970
971 let (p1_group_key, p1_secret_key) = result.unwrap();
972
973 assert!(p1_group_key.0.compress() == (&p1_secret_key.key * &RISTRETTO_BASEPOINT_TABLE).compress());
974 }
975
976 #[test]
977 fn keygen_3_out_of_5() {
978 let params = Parameters { n: 5, t: 3 };
979
980 let (p1, p1coeffs) = Participant::new(¶ms, 1);
981 let (p2, p2coeffs) = Participant::new(¶ms, 2);
982 let (p3, p3coeffs) = Participant::new(¶ms, 3);
983 let (p4, p4coeffs) = Participant::new(¶ms, 4);
984 let (p5, p5coeffs) = Participant::new(¶ms, 5);
985
986 p1.proof_of_secret_key.verify(&p1.index, &p1.public_key().unwrap()).unwrap();
987 p2.proof_of_secret_key.verify(&p2.index, &p2.public_key().unwrap()).unwrap();
988 p3.proof_of_secret_key.verify(&p3.index, &p3.public_key().unwrap()).unwrap();
989 p4.proof_of_secret_key.verify(&p4.index, &p4.public_key().unwrap()).unwrap();
990 p5.proof_of_secret_key.verify(&p5.index, &p5.public_key().unwrap()).unwrap();
991
992 let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone(), p4.clone(), p5.clone());
993 let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
994 &p1.index,
995 &p1coeffs,
996 &mut p1_other_participants).unwrap();
997 let p1_their_secret_shares = p1_state.their_secret_shares().unwrap();
998
999 let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone(), p4.clone(), p5.clone());
1000 let p2_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1001 &p2.index,
1002 &p2coeffs,
1003 &mut p2_other_participants).unwrap();
1004 let p2_their_secret_shares = p2_state.their_secret_shares().unwrap();
1005
1006 let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p4.clone(), p5.clone());
1007 let p3_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1008 &p3.index,
1009 &p3coeffs,
1010 &mut p3_other_participants).unwrap();
1011 let p3_their_secret_shares = p3_state.their_secret_shares().unwrap();
1012
1013 let mut p4_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p5.clone());
1014 let p4_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1015 &p4.index,
1016 &p4coeffs,
1017 &mut p4_other_participants).unwrap();
1018 let p4_their_secret_shares = p4_state.their_secret_shares().unwrap();
1019
1020 let mut p5_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone(), p3.clone(), p4.clone());
1021 let p5_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1022 &p5.index,
1023 &p5coeffs,
1024 &mut p5_other_participants).unwrap();
1025 let p5_their_secret_shares = p5_state.their_secret_shares().unwrap();
1026
1027 let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(), // XXX FIXME indexing
1028 p3_their_secret_shares[0].clone(),
1029 p4_their_secret_shares[0].clone(),
1030 p5_their_secret_shares[0].clone());
1031
1032 let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone(),
1033 p3_their_secret_shares[1].clone(),
1034 p4_their_secret_shares[1].clone(),
1035 p5_their_secret_shares[1].clone());
1036
1037 let p3_my_secret_shares = vec!(p1_their_secret_shares[1].clone(),
1038 p2_their_secret_shares[1].clone(),
1039 p4_their_secret_shares[2].clone(),
1040 p5_their_secret_shares[2].clone());
1041
1042 let p4_my_secret_shares = vec!(p1_their_secret_shares[2].clone(),
1043 p2_their_secret_shares[2].clone(),
1044 p3_their_secret_shares[2].clone(),
1045 p5_their_secret_shares[3].clone());
1046
1047 let p5_my_secret_shares = vec!(p1_their_secret_shares[3].clone(),
1048 p2_their_secret_shares[3].clone(),
1049 p3_their_secret_shares[3].clone(),
1050 p4_their_secret_shares[3].clone());
1051
1052 let p1_state = p1_state.to_round_two(p1_my_secret_shares).unwrap();
1053 let p2_state = p2_state.to_round_two(p2_my_secret_shares).unwrap();
1054 let p3_state = p3_state.to_round_two(p3_my_secret_shares).unwrap();
1055 let p4_state = p4_state.to_round_two(p4_my_secret_shares).unwrap();
1056 let p5_state = p5_state.to_round_two(p5_my_secret_shares).unwrap();
1057
1058 let (p1_group_key, _p1_secret_key) = p1_state.finish(p1.public_key().unwrap()).unwrap();
1059 let (p2_group_key, _p2_secret_key) = p2_state.finish(p2.public_key().unwrap()).unwrap();
1060 let (p3_group_key, _p3_secret_key) = p3_state.finish(p3.public_key().unwrap()).unwrap();
1061 let (p4_group_key, _p4_secret_key) = p4_state.finish(p4.public_key().unwrap()).unwrap();
1062 let (p5_group_key, _p5_secret_key) = p5_state.finish(p5.public_key().unwrap()).unwrap();
1063
1064 assert!(p1_group_key.0.compress() == p2_group_key.0.compress());
1065 assert!(p2_group_key.0.compress() == p3_group_key.0.compress());
1066 assert!(p3_group_key.0.compress() == p4_group_key.0.compress());
1067 assert!(p4_group_key.0.compress() == p5_group_key.0.compress());
1068
1069 assert!(p5_group_key.0.compress() ==
1070 (p1.public_key().unwrap() +
1071 p2.public_key().unwrap() +
1072 p3.public_key().unwrap() +
1073 p4.public_key().unwrap() +
1074 p5.public_key().unwrap()).compress());
1075 }
1076
1077
1078 #[test]
1079 fn keygen_2_out_of_3() {
1080 fn do_test() -> Result<(), ()> {
1081 let params = Parameters { n: 3, t: 2 };
1082
1083 let (p1, p1coeffs) = Participant::new(¶ms, 1);
1084 let (p2, p2coeffs) = Participant::new(¶ms, 2);
1085 let (p3, p3coeffs) = Participant::new(¶ms, 3);
1086
1087 p1.proof_of_secret_key.verify(&p1.index, &p1.public_key().unwrap())?;
1088 p2.proof_of_secret_key.verify(&p2.index, &p2.public_key().unwrap())?;
1089 p3.proof_of_secret_key.verify(&p3.index, &p3.public_key().unwrap())?;
1090
1091 let mut p1_other_participants: Vec<Participant> = vec!(p2.clone(), p3.clone());
1092 let p1_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1093 &p1.index,
1094 &p1coeffs,
1095 &mut p1_other_participants).or(Err(()))?;
1096 let p1_their_secret_shares = p1_state.their_secret_shares()?;
1097
1098 let mut p2_other_participants: Vec<Participant> = vec!(p1.clone(), p3.clone());
1099 let p2_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1100 &p2.index,
1101 &p2coeffs,
1102 &mut p2_other_participants).or(Err(()))?;
1103 let p2_their_secret_shares = p2_state.their_secret_shares()?;
1104
1105 let mut p3_other_participants: Vec<Participant> = vec!(p1.clone(), p2.clone());
1106 let p3_state = DistributedKeyGeneration::<RoundOne>::new(¶ms,
1107 &p3.index,
1108 &p3coeffs,
1109 &mut p3_other_participants).or(Err(()))?;
1110 let p3_their_secret_shares = p3_state.their_secret_shares()?;
1111
1112 let p1_my_secret_shares = vec!(p2_their_secret_shares[0].clone(), // XXX FIXME indexing
1113 p3_their_secret_shares[0].clone());
1114 let p2_my_secret_shares = vec!(p1_their_secret_shares[0].clone(),
1115 p3_their_secret_shares[1].clone());
1116 let p3_my_secret_shares = vec!(p1_their_secret_shares[1].clone(),
1117 p2_their_secret_shares[1].clone());
1118
1119 let p1_state = p1_state.to_round_two(p1_my_secret_shares)?;
1120 let p2_state = p2_state.to_round_two(p2_my_secret_shares)?;
1121 let p3_state = p3_state.to_round_two(p3_my_secret_shares)?;
1122
1123 let (p1_group_key, _p1_secret_key) = p1_state.finish(p1.public_key().unwrap())?;
1124 let (p2_group_key, _p2_secret_key) = p2_state.finish(p2.public_key().unwrap())?;
1125 let (p3_group_key, _p3_secret_key) = p3_state.finish(p3.public_key().unwrap())?;
1126
1127 assert!(p1_group_key.0.compress() == p2_group_key.0.compress());
1128 assert!(p2_group_key.0.compress() == p3_group_key.0.compress());
1129
1130 Ok(())
1131 }
1132 assert!(do_test().is_ok());
1133 }
1134}