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