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
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#[derive(Debug, Clone)]
180pub struct Opening(pub(crate) Zeroizing<[u8; 32]>);
181
182#[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 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 pub fn params(&self) -> &Params {
224 &self.params
225 }
226
227 pub fn index(&self) -> usize {
229 self.index
230 }
231
232 pub fn commitment(&self) -> [u8; 32] {
239 self.commitments[self.index].unwrap()
240 }
241
242 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 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 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 public_polynomials,
287 }
288 }
289}
290
291#[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 #[cfg_attr(feature = "serde", serde(with = "VecHelper::<ElementHelper<G>, 1>"))]
299 pub polynomial: Vec<G::Element>,
300 pub proof_of_possession: Cow<'a, ProofOfPossession<G>>,
302 pub opening: Opening,
304}
305
306impl<G: Group> PublicInfo<'_, G> {
307 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#[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 pub fn params(&self) -> &Params {
337 &self.params
338 }
339
340 pub fn index(&self) -> usize {
342 self.index
343 }
344
345 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 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 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 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 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#[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 pub fn params(&self) -> &Params {
441 &self.params
442 }
443
444 pub fn index(&self) -> usize {
446 self.index
447 }
448
449 pub fn secret_share_for_participant(&self, participant_index: usize) -> SecretKey<G> {
451 self.dealer.secret_share_for_participant(participant_index)
452 }
453
454 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 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 return Err(Error::InvalidSecret);
490 }
491
492 self.accumulated_share += secret_share;
493 self.shares_received[participant_index] = true;
494 Ok(())
495 }
496
497 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(); 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}