1use 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#[derive(Debug)]
121#[non_exhaustive]
122pub enum Error {
123 InvalidSecret,
126 InvalidCommitment,
128 DuplicateShare,
130 MalformedParticipantProof(sharing::Error),
132 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#[derive(Debug, Clone)]
184pub struct Opening(pub(crate) Zeroizing<[u8; 32]>);
185
186#[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 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 pub fn params(&self) -> &Params {
228 &self.params
229 }
230
231 pub fn index(&self) -> usize {
233 self.index
234 }
235
236 pub fn commitment(&self) -> [u8; 32] {
243 self.commitments[self.index].unwrap()
244 }
245
246 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 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 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 public_polynomials,
291 }
292 }
293}
294
295#[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 #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
303 pub polynomial: Vec<G::Element>,
304 pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
306 pub opening: Opening,
308}
309
310impl<G: Group> PublicInfo<'_, G> {
311 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#[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 pub fn params(&self) -> &Params {
341 &self.params
342 }
343
344 pub fn index(&self) -> usize {
346 self.index
347 }
348
349 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 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 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 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 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#[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 pub fn params(&self) -> &Params {
445 &self.params
446 }
447
448 pub fn index(&self) -> usize {
450 self.index
451 }
452
453 pub fn secret_share_for_participant(&self, participant_index: usize) -> SecretKey<G> {
455 self.dealer.secret_share_for_participant(participant_index)
456 }
457
458 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 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 return Err(Error::InvalidSecret);
494 }
495
496 self.accumulated_share += secret_share;
497 self.shares_received[participant_index] = true;
498 Ok(())
499 }
500
501 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(); 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}