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 core::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
160impl core::error::Error for Error {
161    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
162        match self {
163            Self::InconsistentPublicShares(err) | Self::MalformedParticipantProof(err) => Some(err),
164            _ => None,
165        }
166    }
167}
168
169fn create_commitment<G: Group>(element: &G::Element, opening: &[u8]) -> [u8; 32] {
170    let mut hasher = Sha256::new();
171    let mut bytes = vec![0_u8; G::ELEMENT_SIZE];
172    G::serialize_element(element, &mut bytes);
173    hasher.update(&bytes);
174    hasher.update(opening);
175    hasher.finalize().into()
176}
177
178/// Opening for a hash commitment used in Pedersen's distributed key generation.
179#[derive(Debug, Clone)]
180pub struct Opening(pub(crate) Zeroizing<[u8; 32]>);
181
182/// Participant state during the first stage of the committed Pedersen's distributed key generation.
183///
184/// During this stage, participants exchange commitments to their public keys via
185/// a public bulletin board (e.g., a blockchain).
186#[derive(Debug, Clone)]
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
188#[cfg_attr(feature = "serde", serde(bound = ""))]
189pub struct ParticipantCollectingCommitments<G: Group> {
190    params: Params,
191    index: usize,
192    dealer: Dealer<G>,
193    commitments: Vec<Option<[u8; 32]>>,
194    opening: Opening,
195}
196
197impl<G: Group> ParticipantCollectingCommitments<G> {
198    /// Instantiates a distributed key generation participant.
199    ///
200    /// # Panics
201    ///
202    /// Panics if `index` is greater or equal to the number of shares.
203    pub fn new<R: CryptoRng>(params: Params, index: usize, rng: &mut R) -> Self {
204        assert!(index < params.shares);
205
206        let dealer = Dealer::new(params, rng);
207        let mut opening = Zeroizing::new([0_u8; 32]);
208        rng.fill_bytes(&mut *opening);
209
210        let mut commitments = vec![None; params.shares];
211        let (public_poly, _) = dealer.public_info();
212        commitments[index] = Some(create_commitment::<G>(&public_poly[0], opening.as_slice()));
213        Self {
214            params,
215            index,
216            dealer,
217            commitments,
218            opening: Opening(opening),
219        }
220    }
221
222    /// Returns params of this threshold ElGamal encryption scheme.
223    pub fn params(&self) -> &Params {
224        &self.params
225    }
226
227    /// Returns 0-based index of this participant.
228    pub fn index(&self) -> usize {
229        self.index
230    }
231
232    /// Returns the commitment of participant's share of the joint public key.
233    ///
234    /// # Panics
235    ///
236    /// Panics if the commitment is missing which can only happen if this struct got corrupted
237    /// (e.g., after deserialization).
238    pub fn commitment(&self) -> [u8; 32] {
239        self.commitments[self.index].unwrap()
240    }
241
242    /// Inserts a commitment from the participant with index `participant_index`.
243    ///
244    /// # Panics
245    ///
246    /// Panics if commitment for given participant was already provided or
247    /// `participant_index` is out of bounds.
248    pub fn insert_commitment(&mut self, participant_index: usize, commitment: [u8; 32]) {
249        assert!(
250            self.commitments[participant_index].is_none(),
251            "Commitment for participant {participant_index} is already provided"
252        );
253        self.commitments[participant_index] = Some(commitment);
254    }
255
256    /// Returns indices of parties whose commitments were not provided.
257    pub fn missing_commitments(&self) -> impl Iterator<Item = usize> + '_ {
258        self.commitments
259            .iter()
260            .enumerate()
261            .filter_map(|(i, commitment)| commitment.is_none().then_some(i))
262    }
263
264    /// Proceeds to the next step of the DKG protocol, in which participants exchange public
265    /// polynomials.
266    ///
267    /// # Panics
268    ///
269    /// Panics if any commitments are missing. If this is not known statically, check
270    /// with [`Self::missing_commitments()`] before calling this method.
271    pub fn finish_commitment_phase(self) -> ParticipantCollectingPolynomials<G> {
272        if let Some(missing_idx) = self.missing_commitments().next() {
273            panic!("Missing commitment for participant {missing_idx}");
274        }
275
276        let (public_polynomial, _) = self.dealer.public_info();
277        let mut public_polynomials = vec![None; self.params.shares];
278        public_polynomials[self.index] = Some(PublicPolynomial::new(public_polynomial));
279        ParticipantCollectingPolynomials {
280            params: self.params,
281            index: self.index,
282            dealer: self.dealer,
283            opening: self.opening,
284            commitments: self.commitments.into_iter().map(Option::unwrap).collect(),
285            // ^ `unwrap()` is safe due to the above checks
286            public_polynomials,
287        }
288    }
289}
290
291/// Public participant information in the distributed key generation protocol. Returned by
292/// [`ParticipantCollectingPolynomials::public_info()`].
293#[derive(Debug, Clone)]
294#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
295#[cfg_attr(feature = "serde", serde(bound = ""))]
296pub struct PublicInfo<'a, G: Group> {
297    /// Participant's public polynomial.
298    #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
299    pub polynomial: Vec<G::Element>,
300    /// Proof of possession for the secret polynomial that corresponds to `polynomial`.
301    pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
302    /// Opening for the participant's key commitment.
303    pub opening: Opening,
304}
305
306impl<G: Group> PublicInfo<'_, G> {
307    /// Converts this information to the owned form.
308    pub fn into_owned(self) -> PublicInfo<'static, G> {
309        PublicInfo {
310            polynomial: self.polynomial,
311            proof_of_possession: Cow::Owned(self.proof_of_possession.into_owned()),
312            opening: self.opening,
313        }
314    }
315}
316
317/// Participant state during the second stage of the committed Pedersen's distributed key generation.
318///
319/// During this stage, participants exchange public polynomials and openings for the commitments
320/// exchanged on the previous stage. The exchange happens using a public bulletin board
321/// (e.g., a blockchain).
322#[derive(Debug, Clone)]
323#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
324#[cfg_attr(feature = "serde", serde(bound = ""))]
325pub struct ParticipantCollectingPolynomials<G: Group> {
326    params: Params,
327    index: usize,
328    dealer: Dealer<G>,
329    opening: Opening,
330    commitments: Vec<[u8; 32]>,
331    public_polynomials: Vec<Option<PublicPolynomial<G>>>,
332}
333
334impl<G: Group> ParticipantCollectingPolynomials<G> {
335    /// Returns params of this threshold ElGamal encryption scheme.
336    pub fn params(&self) -> &Params {
337        &self.params
338    }
339
340    /// Returns 0-based index of this participant.
341    pub fn index(&self) -> usize {
342        self.index
343    }
344
345    /// Returns public participant information: participant's public polynomial,
346    /// proof of possession for the corresponding secret polynomial and the opening of
347    /// the participant's public key share commitment.
348    pub fn public_info(&self) -> PublicInfo<'_, G> {
349        let (polynomial, proof) = self.dealer.public_info();
350        PublicInfo {
351            polynomial,
352            proof_of_possession: Cow::Borrowed(proof),
353            opening: self.opening.clone(),
354        }
355    }
356
357    /// Returns the indices of parties whose public polynomials were not provided.
358    pub fn missing_public_polynomials(&self) -> impl Iterator<Item = usize> + '_ {
359        self.public_polynomials
360            .iter()
361            .enumerate()
362            .filter_map(|(i, poly)| poly.is_none().then_some(i))
363    }
364
365    /// Inserts public polynomial from participant with index `participant_index`
366    /// their proof of possession of the public polynomial and opening of
367    /// their previously provided commitment.
368    ///
369    /// # Errors
370    ///
371    /// Returns an error if provided polynomial doesn't correspond to the previous
372    /// commitment or the proof of possession is not valid.
373    ///
374    /// # Panics
375    ///
376    /// Panics if `participant_index` is out of bounds.
377    pub fn insert_public_polynomial(
378        &mut self,
379        participant_index: usize,
380        info: PublicInfo<'_, G>,
381    ) -> Result<(), Error> {
382        let opening = info.opening.0.as_slice();
383        let commitment = create_commitment::<G>(&info.polynomial[0], opening);
384        if self.commitments[participant_index] != commitment {
385            // provided commitment doesn't match the given public key share
386            return Err(Error::InvalidCommitment);
387        }
388
389        PublicKeySet::validate(self.params, &info.polynomial, &info.proof_of_possession)
390            .map_err(Error::MalformedParticipantProof)?;
391        self.public_polynomials[participant_index] = Some(PublicPolynomial::new(info.polynomial));
392        Ok(())
393    }
394
395    /// Proceeds to the next step of the DKG protocol, in which participants exchange
396    /// secret shares.
397    ///
398    /// # Panics
399    ///
400    /// Panics if any public polynomials are missing. If this is not known statically, check
401    /// with [`Self::missing_public_polynomials()`] before calling this method.
402    pub fn finish_polynomials_phase(self) -> ParticipantExchangingSecrets<G> {
403        if let Some(missing_idx) = self.missing_public_polynomials().next() {
404            panic!("Missing public polynomial for participant {missing_idx}");
405        }
406
407        let mut shares_received = vec![false; self.params.shares];
408        shares_received[self.index] = true;
409        ParticipantExchangingSecrets {
410            params: self.params,
411            index: self.index,
412            public_polynomials: self.public_polynomials.into_iter().flatten().collect(),
413            accumulated_share: self.dealer.secret_share_for_participant(self.index),
414            dealer: self.dealer,
415            shares_received,
416        }
417    }
418}
419
420/// Participant state during the third and final stage of the committed Pedersen's
421/// distributed key generation.
422///
423/// During this stage, participants exchange secret shares corresponding to the polynomials
424/// exchanged on the previous stage. The exchange happens using secure peer-to-peer channels
425/// established between pairs of participants.
426#[derive(Debug, Clone)]
427#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
428#[cfg_attr(feature = "serde", serde(bound = ""))]
429pub struct ParticipantExchangingSecrets<G: Group> {
430    params: Params,
431    index: usize,
432    dealer: Dealer<G>,
433    public_polynomials: Vec<PublicPolynomial<G>>,
434    accumulated_share: SecretKey<G>,
435    shares_received: Vec<bool>,
436}
437
438impl<G: Group> ParticipantExchangingSecrets<G> {
439    /// Returns params of this threshold ElGamal encryption scheme.
440    pub fn params(&self) -> &Params {
441        &self.params
442    }
443
444    /// Returns 0-based index of this participant.
445    pub fn index(&self) -> usize {
446        self.index
447    }
448
449    /// Returns the secret share for a participant with the specified `participant_index`.
450    pub fn secret_share_for_participant(&self, participant_index: usize) -> SecretKey<G> {
451        self.dealer.secret_share_for_participant(participant_index)
452    }
453
454    /// Returns indices of parties whose secret shares were not provided.
455    pub fn missing_shares(&self) -> impl Iterator<Item = usize> + '_ {
456        self.shares_received
457            .iter()
458            .enumerate()
459            .filter_map(|(i, &is_received)| (!is_received).then_some(i))
460    }
461
462    /// Inserts a secret share from participant with index `participant_index` and
463    /// checks that the share is valid.
464    ///
465    /// # Errors
466    ///
467    /// Returns an error if provided secret share doesn't correspond to the participant's
468    /// public polynomial collected on the previous step of the DKG protocol.
469    ///
470    /// # Panics
471    ///
472    /// Panics if `participant_index` is out of bounds.
473    pub fn insert_secret_share(
474        &mut self,
475        participant_index: usize,
476        secret_share: SecretKey<G>,
477    ) -> Result<(), Error> {
478        if self.shares_received[participant_index] {
479            return Err(Error::DuplicateShare);
480        }
481
482        let polynomial = &self.public_polynomials[participant_index];
483        let idx = (self.index as u64 + 1).into();
484        let public_share = PublicKey::<G>::from_element(polynomial.value_at(idx));
485
486        if public_share.as_element() != G::mul_generator(secret_share.expose_scalar()) {
487            // point corresponding to the received secret share doesn't lie
488            // on the public polynomial
489            return Err(Error::InvalidSecret);
490        }
491
492        self.accumulated_share += secret_share;
493        self.shares_received[participant_index] = true;
494        Ok(())
495    }
496
497    /// Completes the distributed key generation protocol returning an [`ActiveParticipant`].
498    ///
499    /// # Errors
500    ///
501    /// Returns error if secret shares from some parties were not provided,
502    /// or if the [`PublicKeySet`] cannot be created from participants' keys.
503    ///
504    /// # Panics
505    ///
506    /// Panics if shares from any participants are missing. If this is not known statically, check
507    /// with [`Self::missing_shares()`] before calling this method.
508    pub fn complete(self) -> Result<ActiveParticipant<G>, Error> {
509        if let Some(missing_idx) = self.missing_shares().next() {
510            panic!("Missing secret share from participant {missing_idx}");
511        }
512
513        let accumulated_polynomial = self
514            .public_polynomials
515            .into_iter()
516            .reduce(|mut acc, poly| {
517                acc += &poly;
518                acc
519            })
520            .unwrap(); // safe: we have at least ourselves as a participant
521
522        let participant_keys = (0..self.params.shares)
523            .map(|idx| {
524                let idx = (idx as u64 + 1).into();
525                PublicKey::from_element(accumulated_polynomial.value_at(idx))
526            })
527            .collect();
528        let key_set = PublicKeySet::from_participants(self.params, participant_keys)
529            .map_err(Error::InconsistentPublicShares)?;
530
531        let active_participant =
532            ActiveParticipant::new(key_set, self.index, self.accumulated_share)
533                .map_err(Error::InconsistentPublicShares)?;
534        Ok(active_participant)
535    }
536}
537
538#[cfg(test)]
539mod tests {
540    use super::*;
541    use crate::{encryption::DiscreteLogTable, group::Ristretto, sharing::Params};
542
543    #[test]
544    fn dkg_shared_2_of_3_key() {
545        let mut rng = rand::rng();
546        let params = Params::new(3, 2);
547
548        let mut alice = ParticipantCollectingCommitments::<Ristretto>::new(params, 0, &mut rng);
549        assert_eq!(alice.params().shares, params.shares);
550        assert_eq!(alice.params().threshold, params.threshold);
551        assert_eq!(alice.index(), 0);
552        let mut bob = ParticipantCollectingCommitments::<Ristretto>::new(params, 1, &mut rng);
553        assert_eq!(bob.index(), 1);
554        let mut carol = ParticipantCollectingCommitments::<Ristretto>::new(params, 2, &mut rng);
555        assert_eq!(carol.index(), 2);
556
557        assert_eq!(
558            alice.missing_commitments().collect::<Vec<_>>(),
559            [bob.index(), carol.index()]
560        );
561        exchange_commitments(&mut alice, &mut bob, &mut carol);
562
563        let mut alice = alice.finish_commitment_phase();
564        assert_eq!(alice.params().shares, params.shares);
565        assert_eq!(alice.params().threshold, params.threshold);
566        assert_eq!(alice.index(), 0);
567        let mut bob = bob.finish_commitment_phase();
568        assert_eq!(bob.index(), 1);
569        let mut carol = carol.finish_commitment_phase();
570        assert_eq!(carol.index(), 2);
571
572        assert_eq!(
573            alice.missing_public_polynomials().collect::<Vec<_>>(),
574            [bob.index(), carol.index()]
575        );
576        exchange_polynomials(&mut alice, &mut bob, &mut carol).unwrap();
577
578        let mut alice = alice.finish_polynomials_phase();
579        assert_eq!(alice.params().shares, params.shares);
580        assert_eq!(alice.params().threshold, params.threshold);
581        assert_eq!(alice.index(), 0);
582        let mut bob = bob.finish_polynomials_phase();
583        assert_eq!(bob.index(), 1);
584        let mut carol = carol.finish_polynomials_phase();
585        assert_eq!(carol.index(), 2);
586
587        exchange_secret_shares(&mut alice, &mut bob, &mut carol).unwrap();
588
589        let alice = alice.complete().unwrap();
590        let bob = bob.complete().unwrap();
591        carol.complete().unwrap();
592        let key_set = alice.key_set();
593
594        let ciphertext = key_set.shared_key().encrypt(15_u64, &mut rng);
595        let (alice_share, proof) = alice.decrypt_share(ciphertext, &mut rng);
596        key_set
597            .verify_share(alice_share.into(), ciphertext, alice.index(), &proof)
598            .unwrap();
599
600        let (bob_share, proof) = bob.decrypt_share(ciphertext, &mut rng);
601        key_set
602            .verify_share(bob_share.into(), ciphertext, bob.index(), &proof)
603            .unwrap();
604
605        let combined = params
606            .combine_shares([(alice.index(), alice_share), (bob.index(), bob_share)])
607            .unwrap();
608        let lookup_table = DiscreteLogTable::<Ristretto>::new(0..20);
609
610        assert_eq!(combined.decrypt(ciphertext, &lookup_table), Some(15));
611    }
612
613    fn exchange_commitments(
614        alice: &mut ParticipantCollectingCommitments<Ristretto>,
615        bob: &mut ParticipantCollectingCommitments<Ristretto>,
616        carol: &mut ParticipantCollectingCommitments<Ristretto>,
617    ) {
618        let alice_commitment = alice.commitment();
619        let bob_commitment = bob.commitment();
620        let carol_commitment = carol.commitment();
621
622        alice.insert_commitment(bob.index(), bob_commitment);
623        alice.insert_commitment(carol.index(), carol_commitment);
624        bob.insert_commitment(alice.index(), alice_commitment);
625        bob.insert_commitment(carol.index(), carol_commitment);
626        carol.insert_commitment(alice.index(), alice_commitment);
627        carol.insert_commitment(bob.index(), bob_commitment);
628    }
629
630    fn exchange_polynomials(
631        alice: &mut ParticipantCollectingPolynomials<Ristretto>,
632        bob: &mut ParticipantCollectingPolynomials<Ristretto>,
633        carol: &mut ParticipantCollectingPolynomials<Ristretto>,
634    ) -> Result<(), Error> {
635        let alice_info = alice.public_info().into_owned();
636        let bob_info = bob.public_info().into_owned();
637        let carol_info = carol.public_info().into_owned();
638
639        alice.insert_public_polynomial(bob.index(), bob_info.clone())?;
640        alice.insert_public_polynomial(carol.index(), carol_info.clone())?;
641        bob.insert_public_polynomial(alice.index(), alice_info.clone())?;
642        bob.insert_public_polynomial(carol.index(), carol_info)?;
643        carol.insert_public_polynomial(alice.index(), alice_info)?;
644        carol.insert_public_polynomial(bob.index(), bob_info)?;
645        Ok(())
646    }
647
648    fn exchange_secret_shares(
649        alice: &mut ParticipantExchangingSecrets<Ristretto>,
650        bob: &mut ParticipantExchangingSecrets<Ristretto>,
651        carol: &mut ParticipantExchangingSecrets<Ristretto>,
652    ) -> Result<(), Error> {
653        alice.insert_secret_share(bob.index(), bob.secret_share_for_participant(alice.index()))?;
654        alice.insert_secret_share(
655            carol.index(),
656            carol.secret_share_for_participant(alice.index()),
657        )?;
658
659        bob.insert_secret_share(
660            alice.index(),
661            alice.secret_share_for_participant(bob.index()),
662        )?;
663        bob.insert_secret_share(
664            carol.index(),
665            carol.secret_share_for_participant(bob.index()),
666        )?;
667
668        carol.insert_secret_share(
669            alice.index(),
670            alice.secret_share_for_participant(carol.index()),
671        )?;
672        carol.insert_secret_share(bob.index(), bob.secret_share_for_participant(carol.index()))?;
673        Ok(())
674    }
675}