elastic_elgamal/
dkg.rs

1//! Committed Pedersen's distributed key generation (DKG).
2//!
3//! DKG allows to securely generate shared secret without a need for a trusted
4//! dealer. Compare with Feldman's verifiable secret sharing implemented in the [`sharing`] module
5//! which requires a trusted dealer.
6//!
7//! This implementation is based on [Pedersen's DKG], which was shown by [Gennaro et al.]
8//! to contain a flaw allowing an adversary to bias distribution of the shared public key.
9//! We try to prevent this kind of possible attacks by forcing the parties to
10//! commit to their public key shares before receiving public shares from other
11//! parties.
12//!
13//! [Pedersen's DKG]: https://link.springer.com/content/pdf/10.1007/3-540-46416-6_47.pdf
14//! [Gennaro et al.]: https://link.springer.com/content/pdf/10.1007/3-540-48910-X_21.pdf
15//!
16//! # Examples
17//!
18//! Decentralized key generation for 2-of-3 threshold encryption.
19//!
20//! ```
21//! # use elastic_elgamal::{
22//! #     group::Ristretto, dkg::*, sharing::Params,
23//! # };
24//! # use std::error::Error as StdError;
25//! # fn main() -> Result<(), Box<dyn StdError>> {
26//! let mut rng = rand::rng();
27//! let params = Params::new(3, 2);
28//!
29//! // Initialize participants.
30//! let participants = (0..3).map(|i| {
31//!     ParticipantCollectingCommitments::<Ristretto>::new(params, i, &mut rng)
32//! });
33//! let mut participants: Vec<_> = participants.collect();
34//!
35//! // Publish commitments from all participants...
36//! let commitments: Vec<_> = participants
37//!     .iter()
38//!     .map(|participant| participant.commitment())
39//!     .collect();
40//! // ...and consume them from each participant's perspective.
41//! for (i, participant) in participants.iter_mut().enumerate() {
42//!     for (j, &commitment) in commitments.iter().enumerate() {
43//!         if i != j {
44//!             participant.insert_commitment(j, commitment);
45//!         }
46//!     }
47//! }
48//!
49//! // Transition all participants to the next stage: exchanging polynomials.
50//! let mut participants: Vec<_> = participants
51//!     .into_iter()
52//!     .map(|participant| participant.finish_commitment_phase())
53//!     .collect();
54//! // Publish each participant's polynomial...
55//! let infos: Vec<_> = participants
56//!     .iter()
57//!     .map(|participant| participant.public_info().into_owned())
58//!     .collect();
59//! // ...and consume them from each participant's perspective.
60//! for (i, participant) in participants.iter_mut().enumerate() {
61//!     for (j, info) in infos.iter().enumerate() {
62//!         if i != j {
63//!             participant.insert_public_polynomial(j, info.clone())?;
64//!         }
65//!     }
66//! }
67//!
68//! // Transition all participants to the final phase: exchanging secrets.
69//! let mut participants: Vec<_> = participants
70//!     .into_iter()
71//!     .map(|participant| participant.finish_polynomials_phase())
72//!     .collect();
73//! // Exchange shares (this should happen over secure peer-to-peer channels).
74//! for i in 0..3 {
75//!     for j in 0..3 {
76//!         if i == j { continue; }
77//!         let share = participants[i].secret_share_for_participant(j);
78//!         participants[j].insert_secret_share(i, share)?;
79//!     }
80//! }
81//!
82//! // Finalize all participants.
83//! let participants = participants
84//!     .into_iter()
85//!     .map(|participant| participant.complete())
86//!     .collect::<Result<Vec<_>, _>>()?;
87//! // Check that the shared key is the same for all participants.
88//! let expected_key = participants[0].key_set().shared_key();
89//! for participant in &participants {
90//!     assert_eq!(participant.key_set().shared_key(), expected_key);
91//! }
92//!
93//! // Participants can then jointly decrypt messages as showcased
94//! // in the example for the `sharing` module.
95//! # Ok(())
96//! # }
97//! ```
98
99use core::fmt;
100
101use elliptic_curve::{rand_core::CryptoRng, zeroize::Zeroizing};
102#[cfg(feature = "serde")]
103use serde::{Deserialize, Serialize};
104use sha2::{Digest, Sha256};
105
106#[cfg(feature = "serde")]
107use crate::serde::{ElementHelper, VecHelper};
108use crate::{
109    PublicKey, SecretKey,
110    alloc::{Cow, Vec, vec},
111    group::Group,
112    proofs::ProofOfPossession,
113    sharing::{self, ActiveParticipant, Dealer, Params, PublicKeySet, PublicPolynomial},
114};
115
116/// Errors that can occur during the distributed key generation.
117#[derive(Debug)]
118#[non_exhaustive]
119pub enum Error {
120    /// Secret received from the party does not correspond to their commitment via
121    /// the public polynomial.
122    InvalidSecret,
123    /// Provided commitment does not correspond to the party's public key share.
124    InvalidCommitment,
125    /// Secret share for this participant was already provided.
126    DuplicateShare,
127    /// Provided proof of possession or public polynomial is malformed.
128    MalformedParticipantProof(sharing::Error),
129    /// Public shares obtained from accumulated public polynomial are inconsistent.
130    InconsistentPublicShares(sharing::Error),
131}
132
133impl fmt::Display for Error {
134    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
135        match self {
136            Self::InvalidSecret => formatter.write_str(
137                "secret received from the party does not correspond to their commitment via \
138                public polynomial",
139            ),
140            Self::InvalidCommitment => formatter.write_str(
141                "public polynomial received from one of the parties does not correspond \
142                to their commitment",
143            ),
144            Self::DuplicateShare => {
145                formatter.write_str("secret share for this participant was already provided")
146            }
147            Self::MalformedParticipantProof(err) => write!(
148                formatter,
149                "provided proof of possession or public polynomial is malformed: {err}"
150            ),
151            Self::InconsistentPublicShares(err) => write!(
152                formatter,
153                "public shares obtained from accumulated public polynomial \
154                 are inconsistent: {err}"
155            ),
156        }
157    }
158}
159
160#[cfg(feature = "std")]
161impl std::error::Error for Error {
162    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
163        match self {
164            Self::InconsistentPublicShares(err) | Self::MalformedParticipantProof(err) => Some(err),
165            _ => None,
166        }
167    }
168}
169
170fn create_commitment<G: Group>(element: &G::Element, opening: &[u8]) -> [u8; 32] {
171    let mut hasher = Sha256::new();
172    let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
173    G::serialize_element(element, &mut bytes);
174    hasher.update(&bytes);
175    hasher.update(opening);
176    hasher.finalize().into()
177}
178
179/// Opening for a hash commitment used in Pedersen's distributed key generation.
180#[derive(Debug, Clone)]
181pub struct Opening(pub(crate) Zeroizing<[u8; 32]>);
182
183/// Participant state during the first stage of the committed Pedersen's distributed key generation.
184///
185/// During this stage, participants exchange commitments to their public keys via
186/// a public bulletin board (e.g., a blockchain).
187#[derive(Debug, Clone)]
188#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
189#[cfg_attr(feature = "serde", serde(bound = ""))]
190pub struct ParticipantCollectingCommitments<G: Group> {
191    params: Params,
192    index: usize,
193    dealer: Dealer<G>,
194    commitments: Vec<Option<[u8; 32]>>,
195    opening: Opening,
196}
197
198impl<G: Group> ParticipantCollectingCommitments<G> {
199    /// Instantiates a distributed key generation participant.
200    ///
201    /// # Panics
202    ///
203    /// Panics if `index` is greater or equal to the number of shares.
204    pub fn new<R: CryptoRng>(params: Params, index: usize, rng: &mut R) -> Self {
205        assert!(index < params.shares);
206
207        let dealer = Dealer::new(params, rng);
208        let mut opening = Zeroizing::new([0_u8; 32]);
209        rng.fill_bytes(&mut *opening);
210
211        let mut commitments = vec![None; params.shares];
212        let (public_poly, _) = dealer.public_info();
213        commitments[index] = Some(create_commitment::<G>(&public_poly[0], opening.as_slice()));
214        Self {
215            params,
216            index,
217            dealer,
218            commitments,
219            opening: Opening(opening),
220        }
221    }
222
223    /// Returns params of this threshold ElGamal encryption scheme.
224    pub fn params(&self) -> &Params {
225        &self.params
226    }
227
228    /// Returns 0-based index of this participant.
229    pub fn index(&self) -> usize {
230        self.index
231    }
232
233    /// Returns the commitment of participant's share of the joint public key.
234    ///
235    /// # Panics
236    ///
237    /// Panics if the commitment is missing which can only happen if this struct got corrupted
238    /// (e.g., after deserialization).
239    pub fn commitment(&self) -> [u8; 32] {
240        self.commitments[self.index].unwrap()
241    }
242
243    /// Inserts a commitment from the participant with index `participant_index`.
244    ///
245    /// # Panics
246    ///
247    /// Panics if commitment for given participant was already provided or
248    /// `participant_index` is out of bounds.
249    pub fn insert_commitment(&mut self, participant_index: usize, commitment: [u8; 32]) {
250        assert!(
251            self.commitments[participant_index].is_none(),
252            "Commitment for participant {participant_index} is already provided"
253        );
254        self.commitments[participant_index] = Some(commitment);
255    }
256
257    /// Returns indices of parties whose commitments were not provided.
258    pub fn missing_commitments(&self) -> impl Iterator<Item = usize> + '_ {
259        self.commitments
260            .iter()
261            .enumerate()
262            .filter_map(|(i, commitment)| commitment.is_none().then_some(i))
263    }
264
265    /// Proceeds to the next step of the DKG protocol, in which participants exchange public
266    /// polynomials.
267    ///
268    /// # Panics
269    ///
270    /// Panics if any commitments are missing. If this is not known statically, check
271    /// with [`Self::missing_commitments()`] before calling this method.
272    pub fn finish_commitment_phase(self) -> ParticipantCollectingPolynomials<G> {
273        if let Some(missing_idx) = self.missing_commitments().next() {
274            panic!("Missing commitment for participant {missing_idx}");
275        }
276
277        let (public_polynomial, _) = self.dealer.public_info();
278        let mut public_polynomials = vec![None; self.params.shares];
279        public_polynomials[self.index] = Some(PublicPolynomial::new(public_polynomial));
280        ParticipantCollectingPolynomials {
281            params: self.params,
282            index: self.index,
283            dealer: self.dealer,
284            opening: self.opening,
285            commitments: self.commitments.into_iter().map(Option::unwrap).collect(),
286            // ^ `unwrap()` is safe due to the above checks
287            public_polynomials,
288        }
289    }
290}
291
292/// Public participant information in the distributed key generation protocol. Returned by
293/// [`ParticipantCollectingPolynomials::public_info()`].
294#[derive(Debug, Clone)]
295#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
296#[cfg_attr(feature = "serde", serde(bound = ""))]
297pub struct PublicInfo<'a, G: Group> {
298    /// Participant's public polynomial.
299    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
300    pub polynomial: Vec<G::Element>,
301    /// Proof of possession for the secret polynomial that corresponds to `polynomial`.
302    pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
303    /// Opening for the participant's key commitment.
304    pub opening: Opening,
305}
306
307impl<G: Group> PublicInfo<'_, G> {
308    /// Converts this information to the owned form.
309    pub fn into_owned(self) -> PublicInfo<'static, G> {
310        PublicInfo {
311            polynomial: self.polynomial,
312            proof_of_possession: Cow::Owned(self.proof_of_possession.into_owned()),
313            opening: self.opening,
314        }
315    }
316}
317
318/// Participant state during the second stage of the committed Pedersen's distributed key generation.
319///
320/// During this stage, participants exchange public polynomials and openings for the commitments
321/// exchanged on the previous stage. The exchange happens using a public bulletin board
322/// (e.g., a blockchain).
323#[derive(Debug, Clone)]
324#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
325#[cfg_attr(feature = "serde", serde(bound = ""))]
326pub struct ParticipantCollectingPolynomials<G: Group> {
327    params: Params,
328    index: usize,
329    dealer: Dealer<G>,
330    opening: Opening,
331    commitments: Vec<[u8; 32]>,
332    public_polynomials: Vec<Option<PublicPolynomial<G>>>,
333}
334
335impl<G: Group> ParticipantCollectingPolynomials<G> {
336    /// Returns params of this threshold ElGamal encryption scheme.
337    pub fn params(&self) -> &Params {
338        &self.params
339    }
340
341    /// Returns 0-based index of this participant.
342    pub fn index(&self) -> usize {
343        self.index
344    }
345
346    /// Returns public participant information: participant's public polynomial,
347    /// proof of possession for the corresponding secret polynomial and the opening of
348    /// the participant's public key share commitment.
349    pub fn public_info(&self) -> PublicInfo<'_, G> {
350        let (polynomial, proof) = self.dealer.public_info();
351        PublicInfo {
352            polynomial,
353            proof_of_possession: Cow::Borrowed(proof),
354            opening: self.opening.clone(),
355        }
356    }
357
358    /// Returns the indices of parties whose public polynomials were not provided.
359    pub fn missing_public_polynomials(&self) -> impl Iterator<Item = usize> + '_ {
360        self.public_polynomials
361            .iter()
362            .enumerate()
363            .filter_map(|(i, poly)| poly.is_none().then_some(i))
364    }
365
366    /// Inserts public polynomial from participant with index `participant_index`
367    /// their proof of possession of the public polynomial and opening of
368    /// their previously provided commitment.
369    ///
370    /// # Errors
371    ///
372    /// Returns an error if provided polynomial doesn't correspond to the previous
373    /// commitment or the proof of possession is not valid.
374    ///
375    /// # Panics
376    ///
377    /// Panics if `participant_index` is out of bounds.
378    pub fn insert_public_polynomial(
379        &mut self,
380        participant_index: usize,
381        info: PublicInfo<'_, G>,
382    ) -> Result<(), Error> {
383        let opening = info.opening.0.as_slice();
384        let commitment = create_commitment::<G>(&info.polynomial[0], opening);
385        if self.commitments[participant_index] != commitment {
386            // provided commitment doesn't match the given public key share
387            return Err(Error::InvalidCommitment);
388        }
389
390        PublicKeySet::validate(self.params, &info.polynomial, &info.proof_of_possession)
391            .map_err(Error::MalformedParticipantProof)?;
392        self.public_polynomials[participant_index] = Some(PublicPolynomial::new(info.polynomial));
393        Ok(())
394    }
395
396    /// Proceeds to the next step of the DKG protocol, in which participants exchange
397    /// secret shares.
398    ///
399    /// # Panics
400    ///
401    /// Panics if any public polynomials are missing. If this is not known statically, check
402    /// with [`Self::missing_public_polynomials()`] before calling this method.
403    pub fn finish_polynomials_phase(self) -> ParticipantExchangingSecrets<G> {
404        if let Some(missing_idx) = self.missing_public_polynomials().next() {
405            panic!("Missing public polynomial for participant {missing_idx}");
406        }
407
408        let mut shares_received = vec![false; self.params.shares];
409        shares_received[self.index] = true;
410        ParticipantExchangingSecrets {
411            params: self.params,
412            index: self.index,
413            public_polynomials: self.public_polynomials.into_iter().flatten().collect(),
414            accumulated_share: self.dealer.secret_share_for_participant(self.index),
415            dealer: self.dealer,
416            shares_received,
417        }
418    }
419}
420
421/// Participant state during the third and final stage of the committed Pedersen's
422/// distributed key generation.
423///
424/// During this stage, participants exchange secret shares corresponding to the polynomials
425/// exchanged on the previous stage. The exchange happens using secure peer-to-peer channels
426/// established between pairs of participants.
427#[derive(Debug, Clone)]
428#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
429#[cfg_attr(feature = "serde", serde(bound = ""))]
430pub struct ParticipantExchangingSecrets<G: Group> {
431    params: Params,
432    index: usize,
433    dealer: Dealer<G>,
434    public_polynomials: Vec<PublicPolynomial<G>>,
435    accumulated_share: SecretKey<G>,
436    shares_received: Vec<bool>,
437}
438
439impl<G: Group> ParticipantExchangingSecrets<G> {
440    /// Returns params of this threshold ElGamal encryption scheme.
441    pub fn params(&self) -> &Params {
442        &self.params
443    }
444
445    /// Returns 0-based index of this participant.
446    pub fn index(&self) -> usize {
447        self.index
448    }
449
450    /// Returns the secret share for a participant with the specified `participant_index`.
451    pub fn secret_share_for_participant(&self, participant_index: usize) -> SecretKey<G> {
452        self.dealer.secret_share_for_participant(participant_index)
453    }
454
455    /// Returns indices of parties whose secret shares were not provided.
456    pub fn missing_shares(&self) -> impl Iterator<Item = usize> + '_ {
457        self.shares_received
458            .iter()
459            .enumerate()
460            .filter_map(|(i, &is_received)| (!is_received).then_some(i))
461    }
462
463    /// Inserts a secret share from participant with index `participant_index` and
464    /// checks that the share is valid.
465    ///
466    /// # Errors
467    ///
468    /// Returns an error if provided secret share doesn't correspond to the participant's
469    /// public polynomial collected on the previous step of the DKG protocol.
470    ///
471    /// # Panics
472    ///
473    /// Panics if `participant_index` is out of bounds.
474    pub fn insert_secret_share(
475        &mut self,
476        participant_index: usize,
477        secret_share: SecretKey<G>,
478    ) -> Result<(), Error> {
479        if self.shares_received[participant_index] {
480            return Err(Error::DuplicateShare);
481        }
482
483        let polynomial = &self.public_polynomials[participant_index];
484        let idx = (self.index as u64 + 1).into();
485        let public_share = PublicKey::<G>::from_element(polynomial.value_at(idx));
486
487        if public_share.as_element() != G::mul_generator(secret_share.expose_scalar()) {
488            // point corresponding to the received secret share doesn't lie
489            // on the public polynomial
490            return Err(Error::InvalidSecret);
491        }
492
493        self.accumulated_share += secret_share;
494        self.shares_received[participant_index] = true;
495        Ok(())
496    }
497
498    /// Completes the distributed key generation protocol returning an [`ActiveParticipant`].
499    ///
500    /// # Errors
501    ///
502    /// Returns error if secret shares from some parties were not provided,
503    /// or if the [`PublicKeySet`] cannot be created from participants' keys.
504    ///
505    /// # Panics
506    ///
507    /// Panics if shares from any participants are missing. If this is not known statically, check
508    /// with [`Self::missing_shares()`] before calling this method.
509    pub fn complete(self) -> Result<ActiveParticipant<G>, Error> {
510        if let Some(missing_idx) = self.missing_shares().next() {
511            panic!("Missing secret share from participant {missing_idx}");
512        }
513
514        let accumulated_polynomial = self
515            .public_polynomials
516            .into_iter()
517            .reduce(|mut acc, poly| {
518                acc += &poly;
519                acc
520            })
521            .unwrap(); // safe: we have at least ourselves as a participant
522
523        let participant_keys = (0..self.params.shares)
524            .map(|idx| {
525                let idx = (idx as u64 + 1).into();
526                PublicKey::from_element(accumulated_polynomial.value_at(idx))
527            })
528            .collect();
529        let key_set = PublicKeySet::from_participants(self.params, participant_keys)
530            .map_err(Error::InconsistentPublicShares)?;
531
532        let active_participant =
533            ActiveParticipant::new(key_set, self.index, self.accumulated_share)
534                .map_err(Error::InconsistentPublicShares)?;
535        Ok(active_participant)
536    }
537}
538
539#[cfg(test)]
540mod tests {
541    use super::*;
542    use crate::{encryption::DiscreteLogTable, group::Ristretto, sharing::Params};
543
544    #[test]
545    fn dkg_shared_2_of_3_key() {
546        let mut rng = rand::rng();
547        let params = Params::new(3, 2);
548
549        let mut alice = ParticipantCollectingCommitments::<Ristretto>::new(params, 0, &mut rng);
550        assert_eq!(alice.params().shares, params.shares);
551        assert_eq!(alice.params().threshold, params.threshold);
552        assert_eq!(alice.index(), 0);
553        let mut bob = ParticipantCollectingCommitments::<Ristretto>::new(params, 1, &mut rng);
554        assert_eq!(bob.index(), 1);
555        let mut carol = ParticipantCollectingCommitments::<Ristretto>::new(params, 2, &mut rng);
556        assert_eq!(carol.index(), 2);
557
558        assert_eq!(
559            alice.missing_commitments().collect::<Vec<_>>(),
560            [bob.index(), carol.index()]
561        );
562        exchange_commitments(&mut alice, &mut bob, &mut carol);
563
564        let mut alice = alice.finish_commitment_phase();
565        assert_eq!(alice.params().shares, params.shares);
566        assert_eq!(alice.params().threshold, params.threshold);
567        assert_eq!(alice.index(), 0);
568        let mut bob = bob.finish_commitment_phase();
569        assert_eq!(bob.index(), 1);
570        let mut carol = carol.finish_commitment_phase();
571        assert_eq!(carol.index(), 2);
572
573        assert_eq!(
574            alice.missing_public_polynomials().collect::<Vec<_>>(),
575            [bob.index(), carol.index()]
576        );
577        exchange_polynomials(&mut alice, &mut bob, &mut carol).unwrap();
578
579        let mut alice = alice.finish_polynomials_phase();
580        assert_eq!(alice.params().shares, params.shares);
581        assert_eq!(alice.params().threshold, params.threshold);
582        assert_eq!(alice.index(), 0);
583        let mut bob = bob.finish_polynomials_phase();
584        assert_eq!(bob.index(), 1);
585        let mut carol = carol.finish_polynomials_phase();
586        assert_eq!(carol.index(), 2);
587
588        exchange_secret_shares(&mut alice, &mut bob, &mut carol).unwrap();
589
590        let alice = alice.complete().unwrap();
591        let bob = bob.complete().unwrap();
592        carol.complete().unwrap();
593        let key_set = alice.key_set();
594
595        let ciphertext = key_set.shared_key().encrypt(15_u64, &mut rng);
596        let (alice_share, proof) = alice.decrypt_share(ciphertext, &mut rng);
597        key_set
598            .verify_share(alice_share.into(), ciphertext, alice.index(), &proof)
599            .unwrap();
600
601        let (bob_share, proof) = bob.decrypt_share(ciphertext, &mut rng);
602        key_set
603            .verify_share(bob_share.into(), ciphertext, bob.index(), &proof)
604            .unwrap();
605
606        let combined = params
607            .combine_shares([(alice.index(), alice_share), (bob.index(), bob_share)])
608            .unwrap();
609        let lookup_table = DiscreteLogTable::<Ristretto>::new(0..20);
610
611        assert_eq!(combined.decrypt(ciphertext, &lookup_table), Some(15));
612    }
613
614    fn exchange_commitments(
615        alice: &mut ParticipantCollectingCommitments<Ristretto>,
616        bob: &mut ParticipantCollectingCommitments<Ristretto>,
617        carol: &mut ParticipantCollectingCommitments<Ristretto>,
618    ) {
619        let alice_commitment = alice.commitment();
620        let bob_commitment = bob.commitment();
621        let carol_commitment = carol.commitment();
622
623        alice.insert_commitment(bob.index(), bob_commitment);
624        alice.insert_commitment(carol.index(), carol_commitment);
625        bob.insert_commitment(alice.index(), alice_commitment);
626        bob.insert_commitment(carol.index(), carol_commitment);
627        carol.insert_commitment(alice.index(), alice_commitment);
628        carol.insert_commitment(bob.index(), bob_commitment);
629    }
630
631    fn exchange_polynomials(
632        alice: &mut ParticipantCollectingPolynomials<Ristretto>,
633        bob: &mut ParticipantCollectingPolynomials<Ristretto>,
634        carol: &mut ParticipantCollectingPolynomials<Ristretto>,
635    ) -> Result<(), Error> {
636        let alice_info = alice.public_info().into_owned();
637        let bob_info = bob.public_info().into_owned();
638        let carol_info = carol.public_info().into_owned();
639
640        alice.insert_public_polynomial(bob.index(), bob_info.clone())?;
641        alice.insert_public_polynomial(carol.index(), carol_info.clone())?;
642        bob.insert_public_polynomial(alice.index(), alice_info.clone())?;
643        bob.insert_public_polynomial(carol.index(), carol_info)?;
644        carol.insert_public_polynomial(alice.index(), alice_info)?;
645        carol.insert_public_polynomial(bob.index(), bob_info)?;
646        Ok(())
647    }
648
649    fn exchange_secret_shares(
650        alice: &mut ParticipantExchangingSecrets<Ristretto>,
651        bob: &mut ParticipantExchangingSecrets<Ristretto>,
652        carol: &mut ParticipantExchangingSecrets<Ristretto>,
653    ) -> Result<(), Error> {
654        alice.insert_secret_share(bob.index(), bob.secret_share_for_participant(alice.index()))?;
655        alice.insert_secret_share(
656            carol.index(),
657            carol.secret_share_for_participant(alice.index()),
658        )?;
659
660        bob.insert_secret_share(
661            alice.index(),
662            alice.secret_share_for_participant(bob.index()),
663        )?;
664        bob.insert_secret_share(
665            carol.index(),
666            carol.secret_share_for_participant(bob.index()),
667        )?;
668
669        carol.insert_secret_share(
670            alice.index(),
671            alice.secret_share_for_participant(carol.index()),
672        )?;
673        carol.insert_secret_share(bob.index(), bob.secret_share_for_participant(carol.index()))?;
674        Ok(())
675    }
676}