1use 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#[derive(Debug)]
118#[non_exhaustive]
119pub enum Error {
120 InvalidSecret,
123 InvalidCommitment,
125 DuplicateShare,
127 MalformedParticipantProof(sharing::Error),
129 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#[derive(Debug, Clone)]
181pub struct Opening(pub(crate) Zeroizing<[u8; 32]>);
182
183#[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 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 pub fn params(&self) -> &Params {
225 &self.params
226 }
227
228 pub fn index(&self) -> usize {
230 self.index
231 }
232
233 pub fn commitment(&self) -> [u8; 32] {
240 self.commitments[self.index].unwrap()
241 }
242
243 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 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 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 public_polynomials,
288 }
289 }
290}
291
292#[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 #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
300 pub polynomial: Vec<G::Element>,
301 pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
303 pub opening: Opening,
305}
306
307impl<G: Group> PublicInfo<'_, G> {
308 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#[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 pub fn params(&self) -> &Params {
338 &self.params
339 }
340
341 pub fn index(&self) -> usize {
343 self.index
344 }
345
346 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 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 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 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 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#[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 pub fn params(&self) -> &Params {
442 &self.params
443 }
444
445 pub fn index(&self) -> usize {
447 self.index
448 }
449
450 pub fn secret_share_for_participant(&self, participant_index: usize) -> SecretKey<G> {
452 self.dealer.secret_share_for_participant(participant_index)
453 }
454
455 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 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 return Err(Error::InvalidSecret);
491 }
492
493 self.accumulated_share += secret_share;
494 self.shares_received[participant_index] = true;
495 Ok(())
496 }
497
498 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(); 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}