1use super::*;
2use elliptic_curve::group::GroupEncoding;
3use elliptic_curve::subtle::ConditionallySelectable;
4use elliptic_curve::{Group, PrimeField};
5use elliptic_curve_tools::{SumOfProducts, group, prime_field};
6use serde::{Deserialize, Serialize};
7use std::fmt::{self, Display, Formatter};
8use std::sync::Arc;
9use vsss_rs::{IdentifierPrimeField, ShareVerifierGroup};
10
11#[derive(Copy, Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Ord, PartialOrd, Hash)]
13pub enum Round {
14 One,
16 Two,
18 Three,
20 Four,
22}
23
24impl Display for Round {
25 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
26 match self {
27 Self::One => write!(f, "1"),
28 Self::Two => write!(f, "2"),
29 Self::Three => write!(f, "3"),
30 Self::Four => write!(f, "4"),
31 }
32 }
33}
34
35macro_rules! impl_round_to_int {
36 ($($ident:ident),+$(,)*) => {
37 $(
38 impl From<Round> for $ident {
39 fn from(value: Round) -> Self {
40 match value {
41 Round::One => 1,
42 Round::Two => 2,
43 Round::Three => 3,
44 Round::Four => 4,
45 }
46 }
47 }
48
49 impl TryFrom<$ident> for Round {
50 type Error = String;
51
52 fn try_from(value: $ident) -> Result<Self, Self::Error> {
53 match value {
54 1 => Ok(Round::One),
55 2 => Ok(Round::Two),
56 3 => Ok(Round::Three),
57 4 => Ok(Round::Four),
58 _ => Err(format!("Invalid round: {}", value)),
59 }
60 }
61 }
62 )+
63 };
64}
65
66impl_round_to_int!(u8, u16, u32, u128, usize);
67
68#[derive(Debug, Copy, Clone, Default, Deserialize, Serialize)]
70pub enum ParticipantType {
71 #[default]
73 Secret,
74 Refresh,
76}
77
78macro_rules! impl_participant_to_int {
79 ($($ident:ident),+$(,)*) => {
80 $(
81 impl From<ParticipantType> for $ident {
82 fn from(value: ParticipantType) -> Self {
83 match value {
84 ParticipantType::Secret => 1,
85 ParticipantType::Refresh => 2,
86 }
87 }
88 }
89
90 impl TryFrom<$ident> for ParticipantType {
91 type Error = String;
92
93 fn try_from(value: $ident) -> Result<Self, Self::Error> {
94 match value {
95 1 => Ok(ParticipantType::Secret),
96 2 => Ok(ParticipantType::Refresh),
97 _ => Err(format!("Invalid participant type: {}", value)),
98 }
99 }
100 }
101 )+
102 };
103}
104
105impl_participant_to_int!(u8, u16, u32, u128, usize);
106
107#[derive(Debug, Default, Copy, Clone, Deserialize, Serialize)]
109pub struct Signature<G: Group<Scalar: ScalarHash> + GroupEncoding + Default> {
110 #[serde(with = "group")]
111 pub(crate) r: G,
112 #[serde(with = "prime_field")]
113 pub(crate) s: G::Scalar,
114}
115
116#[derive(Debug, Clone, Deserialize, Serialize)]
118pub struct ParticipantRoundOutput<F: ScalarHash> {
119 pub dst_ordinal: usize,
121 #[serde(bound(
123 serialize = "IdentifierPrimeField<F>: Serialize",
124 deserialize = "IdentifierPrimeField<F>: Deserialize<'de>"
125 ))]
126 pub dst_id: IdentifierPrimeField<F>,
127 pub data: WireMessage,
129}
130
131impl<F> ParticipantRoundOutput<F>
132where
133 F: ScalarHash,
134{
135 pub fn new(dst_ordinal: usize, dst_id: IdentifierPrimeField<F>, data: WireMessage) -> Self {
137 Self {
138 dst_ordinal,
139 dst_id,
140 data,
141 }
142 }
143}
144
145pub struct DkgOutput<G>
150where
151 G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
152 G::Scalar: ScalarHash,
153{
154 pub(crate) secret_share: SecretShare<G::Scalar>,
155 pub(crate) public_key: G,
156 pub(crate) feldman_verifiers: Vec<ShareVerifierGroup<G>>,
157 pub(crate) participant_ids: Vec<Option<IdentifierPrimeField<G::Scalar>>>,
158 pub(crate) transcript_hash: [u8; 32],
159}
160
161impl<G> fmt::Debug for DkgOutput<G>
162where
163 G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
164 G::Scalar: ScalarHash,
165{
166 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
167 f.debug_struct("DkgOutput")
168 .field("public_key", &self.public_key)
169 .field("feldman_verifiers", &self.feldman_verifiers)
170 .field("participant_ids", &self.participant_ids)
171 .field("transcript_hash", &self.transcript_hash)
172 .finish_non_exhaustive()
173 }
174}
175
176impl<G> DkgOutput<G>
177where
178 G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
179 G::Scalar: ScalarHash,
180{
181 pub fn secret_share(&self) -> SecretShare<G::Scalar> {
183 self.secret_share
184 }
185
186 pub fn public_key(&self) -> G {
188 self.public_key
189 }
190
191 pub fn feldman_verifiers(&self) -> &[ShareVerifierGroup<G>] {
193 &self.feldman_verifiers
194 }
195
196 pub fn participant_ids(&self) -> &[Option<IdentifierPrimeField<G::Scalar>>] {
198 &self.participant_ids
199 }
200
201 pub fn transcript_hash(&self) -> [u8; 32] {
203 self.transcript_hash
204 }
205}
206
207#[derive(Clone)]
209pub struct WireMessage(Arc<[u8]>);
210
211impl WireMessage {
212 pub fn as_bytes(&self) -> &[u8] {
214 &self.0
215 }
216
217 pub fn as_slice(&self) -> &[u8] {
219 self.as_bytes()
220 }
221}
222
223impl From<Vec<u8>> for WireMessage {
224 fn from(value: Vec<u8>) -> Self {
225 Self(Arc::from(value))
226 }
227}
228
229impl Serialize for WireMessage {
230 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
231 where
232 S: serde::Serializer,
233 {
234 serializer.serialize_bytes(self.as_bytes())
235 }
236}
237
238impl<'de> Deserialize<'de> for WireMessage {
239 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
240 where
241 D: serde::Deserializer<'de>,
242 {
243 Vec::<u8>::deserialize(deserializer).map(Self::from)
244 }
245}
246
247impl AsRef<[u8]> for WireMessage {
248 fn as_ref(&self) -> &[u8] {
249 self.as_bytes()
250 }
251}
252
253impl fmt::Debug for WireMessage {
254 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
255 f.debug_struct("WireMessage")
256 .field("len", &self.0.len())
257 .finish()
258 }
259}
260
261fn serialize_round_message<T>(round: Round, value: &T) -> DkgResult<WireMessage>
262where
263 T: Serialize + ?Sized,
264{
265 Ok(postcard::to_extend(value, vec![u8::from(round)])?.into())
266}
267
268#[derive(Clone, Debug)]
270pub enum MessageDestination<F: ScalarHash> {
271 Broadcast,
273 Direct {
275 ordinal: usize,
277 id: IdentifierPrimeField<F>,
279 },
280}
281
282#[derive(Clone, Debug)]
284pub struct OutboundMessage<F: ScalarHash> {
285 destination: MessageDestination<F>,
286 message: WireMessage,
287}
288
289impl<F: ScalarHash> OutboundMessage<F> {
290 pub fn destination(&self) -> &MessageDestination<F> {
292 &self.destination
293 }
294
295 pub fn message(&self) -> &WireMessage {
297 &self.message
298 }
299}
300
301#[derive(Clone, Debug)]
303pub struct OutboundMessages<F: ScalarHash> {
304 messages: Vec<OutboundMessage<F>>,
305 broadcast_recipients: Vec<Option<IdentifierPrimeField<F>>>,
306}
307
308impl<F: ScalarHash> OutboundMessages<F> {
309 pub fn messages(&self) -> &[OutboundMessage<F>] {
311 &self.messages
312 }
313
314 pub fn len(&self) -> usize {
316 self.messages.len()
317 }
318
319 pub fn is_empty(&self) -> bool {
321 self.messages.is_empty()
322 }
323
324 pub fn iter(&self) -> std::slice::Iter<'_, OutboundMessage<F>> {
326 self.messages.iter()
327 }
328
329 pub fn into_per_recipient(self) -> Vec<ParticipantRoundOutput<F>> {
333 let mut outputs = Vec::new();
334 for message in self.messages {
335 match message.destination {
336 MessageDestination::Broadcast => {
337 outputs.extend(self.broadcast_recipients.iter().enumerate().filter_map(
338 |(ordinal, id)| {
339 id.map(|id| {
340 ParticipantRoundOutput::new(ordinal, id, message.message.clone())
341 })
342 },
343 ));
344 }
345 MessageDestination::Direct { ordinal, id } => {
346 outputs.push(ParticipantRoundOutput::new(ordinal, id, message.message));
347 }
348 }
349 }
350 outputs
351 }
352}
353
354impl<F: ScalarHash> IntoIterator for OutboundMessages<F> {
355 type Item = OutboundMessage<F>;
356 type IntoIter = std::vec::IntoIter<Self::Item>;
357
358 fn into_iter(self) -> Self::IntoIter {
359 self.messages.into_iter()
360 }
361}
362
363impl<'a, F: ScalarHash> IntoIterator for &'a OutboundMessages<F> {
364 type Item = &'a OutboundMessage<F>;
365 type IntoIter = std::slice::Iter<'a, OutboundMessage<F>>;
366
367 fn into_iter(self) -> Self::IntoIter {
368 self.iter()
369 }
370}
371
372#[derive(Clone, Debug)]
374pub enum AdvanceResult<F: ScalarHash> {
375 Messages(OutboundMessages<F>),
377 Complete,
380}
381
382#[derive(Debug, Clone)]
384pub enum RoundOutputGenerator<G>
385where
386 G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
387 G::Scalar: ScalarHash,
388{
389 Round1(Round1OutputGenerator<G>),
391 Round2(Round2OutputGenerator<G>),
393 Round3,
395}
396
397impl<G> RoundOutputGenerator<G>
398where
399 G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
400 G::Scalar: ScalarHash,
401{
402 pub fn into_messages(self) -> DkgResult<OutboundMessages<G::Scalar>> {
404 match self {
405 Self::Round1(data) => {
406 let round1_output_data = Round1Data {
407 sender_ordinal: data.sender_ordinal,
408 sender_id: data.sender_id,
409 sender_type: data.sender_type,
410 feldman_commitments: data.feldman_commitments,
411 verifying_share: data.verifying_share,
412 signature: data.signature,
413 };
414 let mut broadcast_recipients = data
415 .participant_ids
416 .into_iter()
417 .map(Some)
418 .collect::<Vec<_>>();
419 broadcast_recipients[data.sender_ordinal] = None;
420 Ok(OutboundMessages {
421 messages: vec![OutboundMessage {
422 destination: MessageDestination::Broadcast,
423 message: serialize_round_message(Round::One, &round1_output_data)?,
424 }],
425 broadcast_recipients,
426 })
427 }
428 Self::Round2(data) => {
429 let mut messages = Vec::with_capacity(data.participant_ids.len().saturating_sub(1));
430 for (ordinal, id) in data.participant_ids.into_iter().enumerate() {
431 if ordinal == data.sender_ordinal {
432 continue;
433 }
434 let Some(id) = id else {
435 continue;
436 };
437 debug_assert_eq!(data.secret_shares[ordinal].identifier, id);
438 let round2_output_data = Round2Data {
439 sender_ordinal: data.sender_ordinal,
440 sender_id: data.sender_id,
441 sender_type: data.sender_type,
442 secret_share: data.secret_shares[ordinal],
443 transcript_hash: data.transcript_hash,
444 };
445 messages.push(OutboundMessage {
446 destination: MessageDestination::Direct { ordinal, id },
447 message: serialize_round_message(Round::Two, &round2_output_data)?,
448 });
449 }
450 Ok(OutboundMessages {
451 messages,
452 broadcast_recipients: Vec::new(),
453 })
454 }
455 Self::Round3 => Ok(OutboundMessages {
456 messages: Vec::new(),
457 broadcast_recipients: Vec::new(),
458 }),
459 }
460 }
461
462 pub fn iter(&self) -> DkgResult<std::vec::IntoIter<ParticipantRoundOutput<G::Scalar>>> {
466 let outputs = match self {
467 Self::Round1(data) => {
468 let round1_output_data = Round1Data {
469 sender_ordinal: data.sender_ordinal,
470 sender_id: data.sender_id,
471 sender_type: data.sender_type,
472 feldman_commitments: data.feldman_commitments.clone(),
473 verifying_share: data.verifying_share,
474 signature: data.signature,
475 };
476 let output = serialize_round_message(Round::One, &round1_output_data)?;
477 data.participant_ids
478 .iter()
479 .enumerate()
480 .filter_map(|(index, id)| {
481 if index == data.sender_ordinal {
482 None
483 } else {
484 Some(ParticipantRoundOutput::new(index, *id, output.clone()))
485 }
486 })
487 .collect()
488 }
489 Self::Round2(data) => {
490 let mut round2_output_data = Round2Data {
491 sender_ordinal: data.sender_ordinal,
492 sender_id: data.sender_id,
493 sender_type: data.sender_type,
494 secret_share: SecretShare::<G::Scalar>::default(),
495 transcript_hash: data.transcript_hash,
496 };
497 let mut outputs = Vec::with_capacity(data.participant_ids.len().saturating_sub(1));
498 for (index, id) in data.participant_ids.iter().enumerate() {
499 if index == data.sender_ordinal {
500 continue;
501 }
502 let Some(id) = id else {
503 continue;
504 };
505 debug_assert_eq!(data.secret_shares[index].identifier, *id);
506 round2_output_data.secret_share = data.secret_shares[index];
507 let output = serialize_round_message(Round::Two, &round2_output_data)?;
508 outputs.push(ParticipantRoundOutput::new(index, *id, output));
509 }
510 outputs
511 }
512 Self::Round3 => Vec::new(),
513 };
514 Ok(outputs.into_iter())
515 }
516}
517
518#[derive(Debug, Clone)]
520pub struct Round1OutputGenerator<G>
521where
522 G: GroupEncoding + Default + SumOfProducts + ConditionallySelectable,
523 G::Scalar: ScalarHash,
524{
525 pub(crate) participant_ids: Vec<IdentifierPrimeField<G::Scalar>>,
527 pub(crate) sender_type: ParticipantType,
529 pub(crate) sender_ordinal: usize,
531 pub(crate) sender_id: IdentifierPrimeField<G::Scalar>,
533 pub(crate) feldman_commitments: Vec<ShareVerifierGroup<G>>,
535 pub(crate) verifying_share: G,
537 pub(crate) signature: Signature<G>,
539}
540
541#[derive(Clone, Debug, Default, Deserialize, Serialize)]
543pub struct Round1Data<G>
544where
545 G: SumOfProducts + GroupEncoding + Default + ConditionallySelectable,
546 G::Scalar: ScalarHash,
547{
548 pub(crate) sender_ordinal: usize,
550 #[serde(bound(
552 serialize = "IdentifierPrimeField<G::Scalar>: Serialize",
553 deserialize = "IdentifierPrimeField<G::Scalar>: Deserialize<'de>"
554 ))]
555 pub(crate) sender_id: IdentifierPrimeField<G::Scalar>,
556 pub(crate) sender_type: ParticipantType,
558 #[serde(bound(
560 serialize = "ShareVerifierGroup<G>: Serialize",
561 deserialize = "ShareVerifierGroup<G>: Deserialize<'de>"
562 ))]
563 pub(crate) feldman_commitments: Vec<ShareVerifierGroup<G>>,
564 #[serde(with = "group")]
566 pub(crate) verifying_share: G,
567 #[serde(bound(
569 serialize = "Signature<G>: Serialize",
570 deserialize = "Signature<G>: Deserialize<'de>"
571 ))]
572 pub(crate) signature: Signature<G>,
573}
574
575impl<G> Round1Data<G>
576where
577 G: GroupEncoding + Default + SumOfProducts + ConditionallySelectable,
578 G::Scalar: ScalarHash,
579{
580 pub(crate) fn add_to_transcript(&self, transcript: &mut merlin::Transcript) {
581 transcript.append_message(
582 b"sender_ordinal",
583 &(self.sender_ordinal as u16).to_be_bytes(),
584 );
585 transcript.append_message(b"sender_id", self.sender_id.0.to_repr().as_ref());
586 transcript.append_message(b"sender_type", &u16::from(self.sender_type).to_be_bytes());
587 transcript.append_message(b"signature.r", self.signature.r.to_bytes().as_ref());
588 transcript.append_message(b"signature.s", self.signature.s.to_repr().as_ref());
589 transcript.append_message(
590 b"feldman_commitments.len()",
591 &(self.feldman_commitments.len() as u16).to_be_bytes(),
592 );
593 for (i, commitment) in self.feldman_commitments.iter().enumerate() {
594 transcript.append_u64(b"feldman_commitments_index", i as u64);
595 transcript.append_message(b"feldman_commitment", commitment.to_bytes().as_ref());
596 }
597 }
598
599 pub fn sender_ordinal(&self) -> usize {
601 self.sender_ordinal
602 }
603
604 pub fn sender_id(&self) -> IdentifierPrimeField<G::Scalar> {
606 self.sender_id
607 }
608
609 pub fn sender_type(&self) -> ParticipantType {
611 self.sender_type
612 }
613
614 pub fn feldman_commitments(&self) -> &[ShareVerifierGroup<G>] {
616 &self.feldman_commitments
617 }
618
619 pub fn verifying_share(&self) -> G {
621 self.verifying_share
622 }
623
624 pub fn signature(&self) -> Signature<G> {
626 self.signature
627 }
628}
629
630#[derive(Clone)]
632pub struct Round2OutputGenerator<G>
633where
634 G: GroupEncoding + Default + SumOfProducts + ConditionallySelectable,
635 G::Scalar: ScalarHash,
636{
637 pub(crate) participant_ids: Vec<Option<IdentifierPrimeField<G::Scalar>>>,
639 pub(crate) sender_ordinal: usize,
641 pub(crate) sender_id: IdentifierPrimeField<G::Scalar>,
643 pub(crate) sender_type: ParticipantType,
645 pub(crate) secret_shares: Vec<SecretShare<G::Scalar>>,
647 pub(crate) transcript_hash: [u8; 32],
649}
650
651impl<G> fmt::Debug for Round2OutputGenerator<G>
652where
653 G: GroupEncoding + Default + SumOfProducts + ConditionallySelectable,
654 G::Scalar: ScalarHash,
655{
656 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
657 f.debug_struct("Round2OutputGenerator")
658 .field("participant_ids", &self.participant_ids)
659 .field("sender_ordinal", &self.sender_ordinal)
660 .field("sender_id", &self.sender_id)
661 .field("sender_type", &self.sender_type)
662 .field("recipient_count", &self.secret_shares.len())
663 .field("transcript_hash", &self.transcript_hash)
664 .finish()
665 }
666}
667
668#[derive(Clone, Default, Deserialize, Serialize)]
670pub struct Round2Data<F: ScalarHash> {
671 pub(crate) sender_ordinal: usize,
673 #[serde(bound(
675 serialize = "IdentifierPrimeField<F>: Serialize",
676 deserialize = "IdentifierPrimeField<F>: Deserialize<'de>"
677 ))]
678 pub(crate) sender_id: IdentifierPrimeField<F>,
679 pub(crate) sender_type: ParticipantType,
681 #[serde(bound(
683 serialize = "SecretShare<F>: Serialize",
684 deserialize = "SecretShare<F>: Deserialize<'de>"
685 ))]
686 pub(crate) secret_share: SecretShare<F>,
687 pub(crate) transcript_hash: [u8; 32],
689}
690
691impl<F: ScalarHash> fmt::Debug for Round2Data<F> {
692 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
693 f.debug_struct("Round2Data")
694 .field("sender_ordinal", &self.sender_ordinal)
695 .field("sender_id", &self.sender_id)
696 .field("sender_type", &self.sender_type)
697 .field("transcript_hash", &self.transcript_hash)
698 .finish_non_exhaustive()
699 }
700}
701
702impl<F: ScalarHash> Round2Data<F> {
703 pub(crate) fn add_to_transcript(&self, transcript: &mut merlin::Transcript) {
704 transcript.append_message(
705 b"sender_ordinal",
706 &(self.sender_ordinal as u16).to_be_bytes(),
707 );
708 transcript.append_message(b"sender_id", self.sender_id.0.to_repr().as_ref());
709 transcript.append_message(b"sender_type", &u16::from(self.sender_type).to_be_bytes());
710 transcript.append_message(b"transcript_hash", &self.transcript_hash);
711 }
712
713 pub fn sender_ordinal(&self) -> usize {
715 self.sender_ordinal
716 }
717
718 pub fn sender_id(&self) -> IdentifierPrimeField<F> {
720 self.sender_id
721 }
722
723 pub fn sender_type(&self) -> ParticipantType {
725 self.sender_type
726 }
727
728 pub fn secret_share(&self) -> SecretShare<F> {
730 self.secret_share
731 }
732
733 pub fn transcript_hash(&self) -> [u8; 32] {
735 self.transcript_hash
736 }
737}
738
739#[cfg(test)]
740mod tests {
741 use super::*;
742
743 #[test]
744 fn round_two_debug_omits_secret_shares() {
745 let data = Round2Data::<k256::Scalar> {
746 sender_ordinal: 0,
747 sender_id: IdentifierPrimeField::ONE,
748 sender_type: ParticipantType::Secret,
749 secret_share: SecretShare::default(),
750 transcript_hash: [0; 32],
751 };
752 let generator = Round2OutputGenerator::<k256::ProjectivePoint> {
753 participant_ids: vec![Some(IdentifierPrimeField::ONE)],
754 sender_ordinal: 0,
755 sender_id: IdentifierPrimeField::ONE,
756 sender_type: ParticipantType::Secret,
757 secret_shares: vec![SecretShare::default()],
758 transcript_hash: [0; 32],
759 };
760
761 assert!(!format!("{data:?}").contains("secret_share"));
762 assert!(!format!("{generator:?}").contains("secret_share"));
763 }
764
765 #[test]
766 fn serialized_round_message_prefixes_payload_without_changing_it() {
767 let message =
768 serialize_round_message(Round::Two, &42_u16).expect("serialize framed payload");
769
770 assert_eq!(message.as_bytes().first(), Some(&u8::from(Round::Two)));
771 assert_eq!(
772 postcard::from_bytes::<u16>(&message.as_bytes()[1..]).expect("deserialize payload"),
773 42
774 );
775 }
776
777 #[test]
778 fn participant_round_output_postcard_round_trip() {
779 let output = ParticipantRoundOutput::<k256::Scalar>::new(
780 1,
781 IdentifierPrimeField(k256::Scalar::ONE),
782 vec![1, 2, 3].into(),
783 );
784
785 let encoded = postcard::to_stdvec(&output).expect("serialize round output");
786 let decoded = postcard::from_bytes::<ParticipantRoundOutput<k256::Scalar>>(&encoded)
787 .expect("deserialize round output");
788
789 assert_eq!(decoded.dst_ordinal, output.dst_ordinal);
790 assert_eq!(decoded.dst_id, output.dst_id);
791 assert_eq!(decoded.data.as_bytes(), output.data.as_bytes());
792 }
793}