1use elliptic_curve::ops::Reduce;
34use elliptic_curve::point::AffineCoordinates;
35use elliptic_curve::scalar::IsHigh;
36use rustcrypto_ff::{Field, PrimeField};
37use rustcrypto_group::prime::PrimeCurveAffine;
38use rustcrypto_group::Curve;
39use std::collections::{BTreeMap, BTreeSet};
40use zeroize::{Zeroize, ZeroizeOnDrop};
41
42use hex;
43
44use crate::curve::DklsCurve;
45use crate::protocols::{Abort, AbortReason, PartiesMessage, Party, PartyIndex};
46
47use crate::utilities::commits::{commit_point, verify_commitment_point};
48use crate::utilities::hashes::HashOutput;
49use crate::utilities::multiplication::{MulDataToKeepReceiver, MulDataToReceiver};
50use crate::utilities::ot::extension::OTEDataToSender;
51use crate::utilities::rng;
52
53#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct SignData {
57 pub sign_id: Vec<u8>,
58 pub counterparties: Vec<PartyIndex>,
60 pub message_hash: HashOutput,
62}
63
64#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
70#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
71pub struct TransmitPhase1to2 {
72 pub parties: PartiesMessage,
73 pub commitment: HashOutput,
74 #[zeroize(skip)]
75 pub mul_transmit: OTEDataToSender,
76}
77
78#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
83#[cfg_attr(
84 feature = "serde",
85 serde(bound(
86 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
87 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
88 ))
89)]
90pub struct TransmitPhase2to3<C: DklsCurve> {
91 pub parties: PartiesMessage,
92 #[zeroize(skip)]
93 pub gamma_u: C::AffinePoint,
94 #[zeroize(skip)]
95 pub gamma_v: C::AffinePoint,
96 pub psi: C::Scalar,
97 #[zeroize(skip)]
98 pub public_share: C::AffinePoint,
99 #[zeroize(skip)]
100 pub instance_point: C::AffinePoint,
101 pub salt: Vec<u8>,
102 #[zeroize(skip)]
103 pub mul_transmit: MulDataToReceiver<C>,
104}
105
106#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111#[cfg_attr(
112 feature = "serde",
113 serde(bound(
114 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
115 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
116 ))
117)]
118pub struct Broadcast3to4<C: DklsCurve> {
119 pub u: C::Scalar,
120 pub w: C::Scalar,
121}
122
123#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
130#[cfg_attr(
131 feature = "serde",
132 serde(bound(
133 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
134 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
135 ))
136)]
137pub struct KeepPhase1to2<C: DklsCurve> {
138 pub salt: Vec<u8>,
139 pub chi: C::Scalar,
140 pub mul_keep: MulDataToKeepReceiver<C>,
141}
142
143#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
147#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
148#[cfg_attr(
149 feature = "serde",
150 serde(bound(
151 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
152 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
153 ))
154)]
155pub struct KeepPhase2to3<C: DklsCurve> {
156 pub c_u: C::Scalar,
157 pub c_v: C::Scalar,
158 pub commitment: HashOutput,
159 pub mul_keep: MulDataToKeepReceiver<C>,
160 pub chi: C::Scalar,
161}
162
163#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
168#[cfg_attr(
169 feature = "serde",
170 serde(bound(
171 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
172 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
173 ))
174)]
175pub struct UniqueKeep1to2<C: DklsCurve> {
176 pub instance_key: C::Scalar,
177 #[zeroize(skip)]
178 pub instance_point: C::AffinePoint,
179 pub inversion_mask: C::Scalar,
180 pub zeta: C::Scalar,
181}
182
183#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
188#[cfg_attr(
189 feature = "serde",
190 serde(bound(
191 serialize = "C::AffinePoint: serde::Serialize, C::Scalar: serde::Serialize",
192 deserialize = "C::AffinePoint: serde::Deserialize<'de>, C::Scalar: serde::Deserialize<'de>"
193 ))
194)]
195pub struct UniqueKeep2to3<C: DklsCurve> {
196 pub instance_key: C::Scalar,
197 #[zeroize(skip)]
198 pub instance_point: C::AffinePoint,
199 pub inversion_mask: C::Scalar,
200 pub key_share: C::Scalar,
201 #[zeroize(skip)]
202 pub public_share: C::AffinePoint,
203}
204
205#[cfg(feature = "serde")]
207mod message_tags {
208 use super::*;
209 use crate::protocols::messages::MessageTag;
210
211 impl MessageTag for TransmitPhase1to2 {
212 const TAG: u8 = 0x10;
213 }
214 impl<C: DklsCurve> MessageTag for TransmitPhase2to3<C>
215 where
216 C::AffinePoint: serde::Serialize + serde::de::DeserializeOwned,
217 C::Scalar: serde::Serialize + serde::de::DeserializeOwned,
218 {
219 const TAG: u8 = 0x11;
220 }
221 impl<C: DklsCurve> MessageTag for Broadcast3to4<C>
222 where
223 C::AffinePoint: serde::Serialize + serde::de::DeserializeOwned,
224 C::Scalar: serde::Serialize + serde::de::DeserializeOwned,
225 {
226 const TAG: u8 = 0x12;
227 }
228}
229
230impl<C: DklsCurve> Party<C> {
235 #[allow(clippy::type_complexity)]
247 pub fn sign_phase1(
248 &self,
249 data: &SignData,
250 ) -> Result<
251 (
252 UniqueKeep1to2<C>,
253 BTreeMap<PartyIndex, KeepPhase1to2<C>>,
254 Vec<TransmitPhase1to2>,
255 ),
256 Abort,
257 > {
258 if data.counterparties.len() != (self.parameters.threshold - 1) as usize {
260 return Err(Abort::recoverable(
261 self.party_index,
262 AbortReason::WrongCounterpartyCount {
263 expected: (self.parameters.threshold - 1) as usize,
264 got: data.counterparties.len(),
265 },
266 ));
267 }
268
269 if self.party_index.as_u8() > self.parameters.share_count {
271 return Err(Abort::recoverable(
272 self.party_index,
273 AbortReason::InvalidPartyIndex {
274 index: self.party_index,
275 },
276 ));
277 }
278 let mut seen_counterparties: BTreeSet<PartyIndex> = BTreeSet::new();
279 for counterparty in &data.counterparties {
280 if counterparty.as_u8() > self.parameters.share_count {
281 return Err(Abort::recoverable(
282 self.party_index,
283 AbortReason::InvalidPartyIndex {
284 index: *counterparty,
285 },
286 ));
287 }
288 if !seen_counterparties.insert(*counterparty) {
289 return Err(Abort::recoverable(
290 self.party_index,
291 AbortReason::DuplicateCounterparty {
292 index: *counterparty,
293 },
294 ));
295 }
296 if *counterparty == self.party_index {
297 return Err(Abort::recoverable(
298 self.party_index,
299 AbortReason::SelfInCounterparties,
300 ));
301 }
302 if !self.mul_senders.contains_key(counterparty)
303 || !self.mul_receivers.contains_key(counterparty)
304 {
305 return Err(Abort::recoverable(
306 self.party_index,
307 AbortReason::MissingMulState {
308 counterparty: *counterparty,
309 },
310 ));
311 }
312 }
313
314 let instance_key = C::Scalar::random(&mut rng::get_rng());
316 let inversion_mask = C::Scalar::random(&mut rng::get_rng());
317
318 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
319 let instance_point = (generator * instance_key).to_affine();
320
321 let mut keep: BTreeMap<PartyIndex, KeepPhase1to2<C>> = BTreeMap::new();
324 let mut transmit: Vec<TransmitPhase1to2> =
325 Vec::with_capacity((self.parameters.threshold - 1) as usize);
326 for counterparty in &data.counterparties {
327 let (commitment, salt) = commit_point::<C>(&instance_point);
329
330 let mul_sid = [
338 "Multiplication protocol".as_bytes(),
339 &self.party_index.as_u8().to_be_bytes(),
340 &counterparty.as_u8().to_be_bytes(),
341 &self.session_id,
342 &data.sign_id,
343 &self.derivation_data.chain_code,
344 ]
345 .concat();
346
347 let mul_receiver = self.mul_receivers.get(counterparty).ok_or_else(|| {
349 Abort::recoverable(
350 self.party_index,
351 AbortReason::MissingMulState {
352 counterparty: *counterparty,
353 },
354 )
355 })?;
356 let (chi, mul_keep, mul_transmit) = match mul_receiver.run_phase1(&mul_sid) {
357 Ok(values) => values,
358 Err(error) => {
359 return Err(Abort::recoverable(
360 self.party_index,
361 AbortReason::MultiplicationVerificationFailed {
362 counterparty: *counterparty,
363 detail: error.description.clone(),
364 },
365 ));
366 }
367 };
368
369 keep.insert(
371 *counterparty,
372 KeepPhase1to2 {
373 salt,
374 chi,
375 mul_keep,
376 },
377 );
378 transmit.push(TransmitPhase1to2 {
379 parties: PartiesMessage {
380 sender: self.party_index,
381 receiver: *counterparty,
382 },
383 commitment,
384 mul_transmit,
385 });
386 }
387
388 let zero_sid = [
396 "Zero shares protocol".as_bytes(),
397 &self.session_id,
398 &data.sign_id,
399 &self.derivation_data.chain_code,
400 ]
401 .concat();
402
403 let zeta = self
404 .zero_share
405 .compute::<C>(&data.counterparties, &zero_sid);
406
407 let unique_keep = UniqueKeep1to2 {
409 instance_key,
410 instance_point,
411 inversion_mask,
412 zeta,
413 };
414
415 Ok((unique_keep, keep, transmit))
417 }
418
419 #[allow(clippy::type_complexity)]
440 pub fn sign_phase2(
441 &self,
442 data: &SignData,
443 unique_kept: &UniqueKeep1to2<C>,
444 kept: &BTreeMap<PartyIndex, KeepPhase1to2<C>>,
445 received: &[TransmitPhase1to2],
446 ) -> Result<
447 (
448 UniqueKeep2to3<C>,
449 BTreeMap<PartyIndex, KeepPhase2to3<C>>,
450 Vec<TransmitPhase2to3<C>>,
451 ),
452 Abort,
453 > {
454 let mut l_numerator = <C::Scalar as Field>::ONE;
461 let mut l_denominator = <C::Scalar as Field>::ONE;
462 for counterparty in &data.counterparties {
463 l_numerator *= C::Scalar::from(u64::from(counterparty.as_u8()));
464 l_denominator *= C::Scalar::from(u64::from(counterparty.as_u8()))
465 - C::Scalar::from(u64::from(self.party_index.as_u8()));
466 }
467 let l_denominator_inverse = match Option::<C::Scalar>::from(l_denominator.invert()) {
468 Some(inv) => inv,
469 None => {
470 return Err(Abort::recoverable(
471 self.party_index,
472 AbortReason::LagrangeCoefficientFailed,
473 ));
474 }
475 };
476 let l = l_numerator * l_denominator_inverse;
477
478 let key_share = (self.poly_point * l) + unique_kept.zeta;
480 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
481 let public_share = (generator * key_share).to_affine();
482
483 let input = vec![unique_kept.instance_key, key_share];
485
486 let mut keep: BTreeMap<PartyIndex, KeepPhase2to3<C>> = BTreeMap::new();
488 let mut transmit: Vec<TransmitPhase2to3<C>> =
489 Vec::with_capacity((self.parameters.threshold - 1) as usize);
490 if received.len() != data.counterparties.len() {
491 return Err(Abort::recoverable(
492 self.party_index,
493 AbortReason::WrongMessageCount {
494 expected: data.counterparties.len(),
495 got: received.len(),
496 },
497 ));
498 }
499 let mut seen_senders: BTreeSet<PartyIndex> = BTreeSet::new();
500 for message in received {
501 let counterparty = message.parties.sender;
503 if !data.counterparties.contains(&counterparty) {
504 return Err(Abort::recoverable(
505 self.party_index,
506 AbortReason::UnexpectedSender {
507 sender: counterparty,
508 },
509 ));
510 }
511 if message.parties.receiver != self.party_index {
512 return Err(Abort::recoverable(
513 self.party_index,
514 AbortReason::MisroutedMessage {
515 expected_receiver: self.party_index,
516 actual_receiver: message.parties.receiver,
517 },
518 ));
519 }
520 if !seen_senders.insert(counterparty) {
521 return Err(Abort::recoverable(
522 self.party_index,
523 AbortReason::DuplicateSender {
524 sender: counterparty,
525 },
526 ));
527 }
528 let current_kept = kept.get(&counterparty).ok_or_else(|| {
529 Abort::recoverable(
530 self.party_index,
531 AbortReason::MissingMulState { counterparty },
532 )
533 })?;
534
535 let mul_sid = [
541 "Multiplication protocol".as_bytes(),
542 &counterparty.as_u8().to_be_bytes(),
543 &self.party_index.as_u8().to_be_bytes(),
544 &self.session_id,
545 &data.sign_id,
546 &self.derivation_data.chain_code,
547 ]
548 .concat();
549
550 let mul_sender = self.mul_senders.get(&counterparty).ok_or_else(|| {
551 Abort::recoverable(
552 self.party_index,
553 AbortReason::MissingMulState { counterparty },
554 )
555 })?;
556 let mul_result = mul_sender.run(&mul_sid, &input, &message.mul_transmit);
557
558 let c_u: C::Scalar;
559 let c_v: C::Scalar;
560 let mul_transmit: MulDataToReceiver<C>;
561 match mul_result {
562 Err(error) => {
563 return Err(Abort::ban(
564 self.party_index,
565 counterparty,
566 AbortReason::MultiplicationVerificationFailed {
567 counterparty,
568 detail: error.description.clone(),
569 },
570 ));
571 }
572 Ok((c_values, data_to_receiver)) => {
573 c_u = c_values[0];
574 c_v = c_values[1];
575 mul_transmit = data_to_receiver;
576 }
577 }
578
579 let gamma_u = (generator * c_u).to_affine();
581 let gamma_v = (generator * c_v).to_affine();
582
583 let psi = unique_kept.inversion_mask - current_kept.chi;
584
585 keep.insert(
586 counterparty,
587 KeepPhase2to3 {
588 c_u,
589 c_v,
590 commitment: message.commitment,
591 mul_keep: current_kept.mul_keep.clone(),
592 chi: current_kept.chi,
593 },
594 );
595 transmit.push(TransmitPhase2to3 {
596 parties: PartiesMessage {
597 sender: self.party_index,
598 receiver: counterparty,
599 },
600 gamma_u,
602 gamma_v,
603 psi,
604 public_share,
605 instance_point: unique_kept.instance_point,
607 salt: current_kept.salt.clone(),
608 mul_transmit,
610 });
611 }
612
613 let unique_keep = UniqueKeep2to3 {
615 instance_key: unique_kept.instance_key,
616 instance_point: unique_kept.instance_point,
617 inversion_mask: unique_kept.inversion_mask,
618 key_share,
619 public_share,
620 };
621
622 Ok((unique_keep, keep, transmit))
623 }
624
625 pub fn sign_phase3(
649 &self,
650 data: &SignData,
651 unique_kept: &UniqueKeep2to3<C>,
652 kept: &BTreeMap<PartyIndex, KeepPhase2to3<C>>,
653 received: &[TransmitPhase2to3<C>],
654 ) -> Result<(String, Broadcast3to4<C>), Abort> {
655 let mut expected_public_key = unique_kept.public_share;
659 let mut total_instance_point = unique_kept.instance_point;
660
661 let mut first_sum_u_v = unique_kept.inversion_mask;
662
663 let mut second_sum_u = <C::Scalar as Field>::ZERO;
664 let mut second_sum_v = <C::Scalar as Field>::ZERO;
665
666 if received.len() != data.counterparties.len() {
667 return Err(Abort::recoverable(
668 self.party_index,
669 AbortReason::WrongMessageCount {
670 expected: data.counterparties.len(),
671 got: received.len(),
672 },
673 ));
674 }
675 let mut seen_senders: BTreeSet<PartyIndex> = BTreeSet::new();
676 for message in received {
677 let counterparty = message.parties.sender;
679 if !data.counterparties.contains(&counterparty) {
680 return Err(Abort::recoverable(
681 self.party_index,
682 AbortReason::UnexpectedSender {
683 sender: counterparty,
684 },
685 ));
686 }
687 if message.parties.receiver != self.party_index {
688 return Err(Abort::recoverable(
689 self.party_index,
690 AbortReason::MisroutedMessage {
691 expected_receiver: self.party_index,
692 actual_receiver: message.parties.receiver,
693 },
694 ));
695 }
696 let identity = <C::AffinePoint as PrimeCurveAffine>::identity();
697 if message.instance_point == identity {
698 return Err(Abort::recoverable(
699 self.party_index,
700 AbortReason::TrivialInstancePoint { counterparty },
701 ));
702 }
703 if !seen_senders.insert(counterparty) {
704 return Err(Abort::recoverable(
705 self.party_index,
706 AbortReason::DuplicateSender {
707 sender: counterparty,
708 },
709 ));
710 }
711 let current_kept = kept.get(&counterparty).ok_or_else(|| {
712 Abort::recoverable(
713 self.party_index,
714 AbortReason::MissingMulState { counterparty },
715 )
716 })?;
717
718 let verification = verify_commitment_point::<C>(
720 &message.instance_point,
721 ¤t_kept.commitment,
722 &message.salt,
723 );
724 if !verification {
725 return Err(Abort::recoverable(
726 self.party_index,
727 AbortReason::CommitmentMismatch { counterparty },
728 ));
729 }
730
731 let mul_sid = [
737 "Multiplication protocol".as_bytes(),
738 &self.party_index.as_u8().to_be_bytes(),
739 &counterparty.as_u8().to_be_bytes(),
740 &self.session_id,
741 &data.sign_id,
742 &self.derivation_data.chain_code,
743 ]
744 .concat();
745
746 let mul_receiver = self.mul_receivers.get(&counterparty).ok_or_else(|| {
747 Abort::recoverable(
748 self.party_index,
749 AbortReason::MissingMulState { counterparty },
750 )
751 })?;
752 let mul_result =
753 mul_receiver.run_phase2(&mul_sid, ¤t_kept.mul_keep, &message.mul_transmit);
754
755 let d_u: C::Scalar;
756 let d_v: C::Scalar;
757 match mul_result {
758 Err(error) => {
759 return Err(Abort::ban(
760 self.party_index,
761 counterparty,
762 AbortReason::MultiplicationVerificationFailed {
763 counterparty,
764 detail: error.description.clone(),
765 },
766 ));
767 }
768 Ok(d_values) => {
769 d_u = d_values[0];
770 d_v = d_values[1];
771 }
772 }
773
774 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
776
777 if (message.instance_point * current_kept.chi) != ((generator * d_u) + message.gamma_u)
778 {
779 return Err(Abort::ban(
780 self.party_index,
781 counterparty,
782 AbortReason::GammaUInconsistency { counterparty },
783 ));
784 }
785
786 if (message.public_share * current_kept.chi) != ((generator * d_v) + message.gamma_v) {
791 return Err(Abort::ban(
792 self.party_index,
793 counterparty,
794 AbortReason::OtConsistencyCheckFailed { counterparty },
795 ));
796 }
797
798 expected_public_key =
800 (C::ProjectivePoint::from(expected_public_key) + message.public_share).to_affine();
801 total_instance_point = (C::ProjectivePoint::from(total_instance_point)
802 + message.instance_point)
803 .to_affine();
804
805 first_sum_u_v += &message.psi;
806
807 second_sum_u = second_sum_u + current_kept.c_u + d_u;
808 second_sum_v = second_sum_v + current_kept.c_v + d_v;
809 }
810
811 if expected_public_key != self.pk {
813 return Err(Abort::recoverable(
814 self.party_index,
815 AbortReason::PolynomialInconsistency,
816 ));
817 }
818
819 if total_instance_point == <C::AffinePoint as PrimeCurveAffine>::identity() {
824 return Err(Abort::recoverable(
825 self.party_index,
826 AbortReason::PolynomialInconsistency,
827 ));
828 }
829
830 let u = (unique_kept.instance_key * first_sum_u_v) + second_sum_u;
832 let v = (unique_kept.key_share * first_sum_u_v) + second_sum_v;
833
834 let x_coord = hex::encode(total_instance_point.x());
835 let msg_scalar = reduce_hash_bytes::<C>(&data.message_hash);
837 let rx_scalar = reduce_hex_bytes::<C>(&x_coord);
838 let w = (msg_scalar * unique_kept.inversion_mask) + (v * rx_scalar);
839
840 let broadcast = Broadcast3to4 { u, w };
841
842 Ok((x_coord, broadcast))
846 }
847
848 pub fn sign_phase4(
870 &self,
871 data: &SignData,
872 x_coord: &str,
873 received: &[Broadcast3to4<C>],
874 normalize: bool,
875 ) -> Result<(String, u8), Abort> {
876 let mut numerator = <C::Scalar as Field>::ZERO;
879 let mut denominator = <C::Scalar as Field>::ZERO;
880 for message in received {
881 numerator += &message.w;
882 denominator += &message.u;
883 }
884
885 let denominator_inverse = match Option::<C::Scalar>::from(denominator.invert()) {
886 Some(inv) => inv,
887 None => {
888 return Err(Abort::recoverable(
889 self.party_index,
890 AbortReason::ZeroDenominator,
891 ));
892 }
893 };
894 let mut s = numerator * denominator_inverse;
895
896 if normalize && s.is_high().into() {
899 s = -s;
900 }
901
902 let s_bytes: elliptic_curve::FieldBytes<C> = s.into();
903 let signature = hex::encode(&s_bytes);
904
905 let verification =
906 verify_ecdsa_signature::<C>(&data.message_hash, &self.pk, x_coord, &signature);
907 if !verification {
908 return Err(Abort::recoverable(
909 self.party_index,
910 AbortReason::SignatureVerificationFailed,
911 ));
912 }
913
914 let rx_scalar = reduce_hex_bytes::<C>(x_coord);
919 let msg_scalar = reduce_hash_bytes::<C>(&data.message_hash);
920 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
921 let first = generator * msg_scalar;
922 let second = self.pk * rx_scalar;
923 let s_inverse = s.invert().unwrap();
924 let signature_point = ((first + second) * s_inverse).to_affine();
925
926 let compressed = rustcrypto_group::GroupEncoding::to_bytes(&signature_point);
930 let is_y_odd = compressed.as_ref()[0] == 0x03;
931
932 let x_bytes_original = {
936 let mut bytes = vec![0u8; elliptic_curve::FieldBytes::<C>::default().len()];
937 hex::decode_to_slice(x_coord, &mut bytes).expect("valid hex");
938 bytes
939 };
940 let rx_repr = rx_scalar.to_repr();
941 let rx_repr_slice: &[u8] = rx_repr.as_ref();
942 let is_x_reduced = x_bytes_original.as_slice() != rx_repr_slice;
943
944 let rec_id = (is_y_odd as u8) | ((is_x_reduced as u8) << 1);
946
947 Ok((signature, rec_id))
948 }
949}
950
951fn parse_hex_to_scalar<C: DklsCurve>(hex_value: &str) -> Option<C::Scalar> {
956 let mut bytes = vec![0u8; elliptic_curve::FieldBytes::<C>::default().len()];
957 if hex::decode_to_slice(hex_value, &mut bytes).is_err() {
958 return None;
959 }
960 let field_bytes: &elliptic_curve::FieldBytes<C> = bytes.as_slice().try_into().ok()?;
961 Option::from(<C::Scalar as PrimeField>::from_repr(field_bytes.clone()))
962}
963
964fn reduce_hash_bytes<C: DklsCurve>(hash: &HashOutput) -> C::Scalar {
966 let field_bytes: &elliptic_curve::FieldBytes<C> = hash
967 .as_slice()
968 .try_into()
969 .expect("hash output length matches field size");
970 <C::Scalar as Reduce<elliptic_curve::FieldBytes<C>>>::reduce(field_bytes)
971}
972
973fn reduce_hex_bytes<C: DklsCurve>(hex_value: &str) -> C::Scalar {
975 let mut bytes = vec![0u8; elliptic_curve::FieldBytes::<C>::default().len()];
976 hex::decode_to_slice(hex_value, &mut bytes).expect("valid hex");
977 let field_bytes: &elliptic_curve::FieldBytes<C> = bytes
978 .as_slice()
979 .try_into()
980 .expect("decoded hex length matches field size");
981 <C::Scalar as Reduce<elliptic_curve::FieldBytes<C>>>::reduce(field_bytes)
982}
983
984#[must_use]
988pub fn verify_ecdsa_signature<C: DklsCurve>(
989 msg: &HashOutput,
990 pk: &C::AffinePoint,
991 x_coord: &str,
992 signature: &str,
993) -> bool {
994 let rx_as_scalar = match parse_hex_to_scalar::<C>(x_coord) {
995 Some(value) => value,
996 None => return false,
997 };
998 let s_as_scalar = match parse_hex_to_scalar::<C>(signature) {
999 Some(value) => value,
1000 None => return false,
1001 };
1002
1003 if rx_as_scalar == <C::Scalar as Field>::ZERO || s_as_scalar == <C::Scalar as Field>::ZERO {
1005 return false;
1006 }
1007
1008 let inverse_s = match Option::<C::Scalar>::from(s_as_scalar.invert()) {
1009 Some(inv) => inv,
1010 None => return false,
1011 };
1012
1013 let msg_scalar = reduce_hash_bytes::<C>(msg);
1014 let first = msg_scalar * inverse_s;
1015 let second = rx_as_scalar * inverse_s;
1016
1017 let generator = <C::AffinePoint as PrimeCurveAffine>::generator();
1018 let point_to_check = ((generator * first) + (*pk * second)).to_affine();
1019 let identity = <C::AffinePoint as PrimeCurveAffine>::identity();
1020 if point_to_check == identity {
1021 return false;
1022 }
1023
1024 let x_bytes = point_to_check.x();
1025 let x_field_bytes: &elliptic_curve::FieldBytes<C> = (&x_bytes[..])
1026 .try_into()
1027 .expect("x coordinate length matches field size");
1028 let x_check = <C::Scalar as Reduce<elliptic_curve::FieldBytes<C>>>::reduce(x_field_bytes);
1029
1030 x_check == rx_as_scalar
1031}
1032
1033#[cfg(test)]
1034mod tests {
1035 use super::*;
1036 use crate::protocols::dkg::*;
1037 use crate::protocols::re_key::re_key;
1038 use crate::protocols::*;
1039 use crate::utilities::hashes::tagged_hash;
1040 use elliptic_curve::sec1::ToSec1Point;
1041 use elliptic_curve::Curve as _;
1042 use elliptic_curve::CurveArithmetic;
1043 use k256::{AffinePoint, ProjectivePoint, Scalar, Secp256k1, U256};
1044 use rand::RngExt;
1045
1046 type TestCurve = k256::Secp256k1;
1047
1048 fn no_address(_pk: &<TestCurve as CurveArithmetic>::AffinePoint) -> String {
1049 String::new()
1050 }
1051
1052 #[test]
1056 fn test_signing() {
1057 let threshold = rng::get_rng().random_range(2..=5); let offset = rng::get_rng().random_range(0..=5);
1064
1065 let parameters = Parameters {
1066 threshold,
1067 share_count: threshold + offset,
1068 }; let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1072 let secret_key = Scalar::random(&mut rng::get_rng());
1073 let (parties, _) =
1074 re_key::<TestCurve>(¶meters, &session_id, &secret_key, None, no_address);
1075
1076 let sign_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1079 let message_to_sign = tagged_hash(b"test-sign", &[b"Message to sign!"]);
1080
1081 let executing_parties: Vec<PartyIndex> = (1..=parameters.threshold)
1083 .map(|i| PartyIndex::new(i).unwrap())
1084 .collect();
1085
1086 let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1088 for party_index in executing_parties.clone() {
1089 let mut counterparties = executing_parties.clone();
1091 counterparties.retain(|index| *index != party_index);
1092
1093 all_data.insert(
1094 party_index,
1095 SignData {
1096 sign_id: sign_id.to_vec(),
1097 counterparties,
1098 message_hash: message_to_sign,
1099 },
1100 );
1101 }
1102
1103 let mut unique_kept_1to2: BTreeMap<PartyIndex, UniqueKeep1to2<TestCurve>> = BTreeMap::new();
1105 let mut kept_1to2: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase1to2<TestCurve>>> =
1106 BTreeMap::new();
1107 let mut transmit_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1108 for party_index in executing_parties.clone() {
1109 let (unique_keep, keep, transmit) = parties[(party_index.as_u8() - 1) as usize]
1110 .sign_phase1(all_data.get(&party_index).unwrap())
1111 .unwrap();
1112
1113 unique_kept_1to2.insert(party_index, unique_keep);
1114 kept_1to2.insert(party_index, keep);
1115 transmit_1to2.insert(party_index, transmit);
1116 }
1117
1118 let mut received_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1120
1121 for &party_index in &executing_parties {
1122 let messages_for_party: Vec<TransmitPhase1to2> = transmit_1to2
1123 .values()
1124 .flatten()
1125 .filter(|message| message.parties.receiver == party_index)
1126 .cloned()
1127 .collect();
1128
1129 received_1to2.insert(party_index, messages_for_party);
1130 }
1131
1132 let mut unique_kept_2to3: BTreeMap<PartyIndex, UniqueKeep2to3<TestCurve>> = BTreeMap::new();
1134 let mut kept_2to3: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase2to3<TestCurve>>> =
1135 BTreeMap::new();
1136 let mut transmit_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1137 BTreeMap::new();
1138 for party_index in executing_parties.clone() {
1139 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase2(
1140 all_data.get(&party_index).unwrap(),
1141 unique_kept_1to2.get(&party_index).unwrap(),
1142 kept_1to2.get(&party_index).unwrap(),
1143 received_1to2.get(&party_index).unwrap(),
1144 );
1145 match result {
1146 Err(abort) => {
1147 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1148 }
1149 Ok((unique_keep, keep, transmit)) => {
1150 unique_kept_2to3.insert(party_index, unique_keep);
1151 kept_2to3.insert(party_index, keep);
1152 transmit_2to3.insert(party_index, transmit);
1153 }
1154 }
1155 }
1156
1157 let mut received_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1159 BTreeMap::new();
1160
1161 for &party_index in &executing_parties {
1162 let messages_for_party: Vec<TransmitPhase2to3<TestCurve>> = transmit_2to3
1163 .values()
1164 .flatten()
1165 .filter(|message| message.parties.receiver == party_index)
1166 .cloned()
1167 .collect();
1168
1169 received_2to3.insert(party_index, messages_for_party);
1170 }
1171
1172 let mut x_coords: Vec<String> = Vec::with_capacity(parameters.threshold as usize);
1174 let mut broadcast_3to4: Vec<Broadcast3to4<TestCurve>> =
1175 Vec::with_capacity(parameters.threshold as usize);
1176 for party_index in executing_parties.clone() {
1177 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase3(
1178 all_data.get(&party_index).unwrap(),
1179 unique_kept_2to3.get(&party_index).unwrap(),
1180 kept_2to3.get(&party_index).unwrap(),
1181 received_2to3.get(&party_index).unwrap(),
1182 );
1183 match result {
1184 Err(abort) => {
1185 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1186 }
1187 Ok((x_coord, broadcast)) => {
1188 x_coords.push(x_coord);
1189 broadcast_3to4.push(broadcast);
1190 }
1191 }
1192 }
1193
1194 let x_coord = x_coords[0].clone(); for i in 1..parameters.threshold {
1197 assert_eq!(x_coord, x_coords[i as usize]);
1198 }
1199
1200 let some_index = executing_parties[0];
1206 let result = parties[(some_index.as_u8() - 1) as usize].sign_phase4(
1207 all_data.get(&some_index).unwrap(),
1208 &x_coord,
1209 &broadcast_3to4,
1210 true,
1211 );
1212 if let Err(abort) = result {
1213 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1214 }
1215 }
1217
1218 #[test]
1224 fn test_signing_against_ecdsa() {
1225 let threshold = rng::get_rng().random_range(2..=5); let offset = rng::get_rng().random_range(0..=5);
1227
1228 let parameters = Parameters {
1229 threshold,
1230 share_count: threshold + offset,
1231 }; let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1235 let secret_key = Scalar::random(&mut rng::get_rng());
1236 let (parties, _) =
1237 re_key::<TestCurve>(¶meters, &session_id, &secret_key, None, no_address);
1238
1239 let sign_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1242 let message_to_sign = tagged_hash(b"test-sign", &[b"Message to sign!"]);
1243
1244 let executing_parties: Vec<PartyIndex> = (1..=parameters.threshold)
1246 .map(|i| PartyIndex::new(i).unwrap())
1247 .collect();
1248
1249 let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1251 for party_index in executing_parties.clone() {
1252 let mut counterparties = executing_parties.clone();
1254 counterparties.retain(|index| *index != party_index);
1255
1256 all_data.insert(
1257 party_index,
1258 SignData {
1259 sign_id: sign_id.to_vec(),
1260 counterparties,
1261 message_hash: message_to_sign,
1262 },
1263 );
1264 }
1265
1266 let mut unique_kept_1to2: BTreeMap<PartyIndex, UniqueKeep1to2<TestCurve>> = BTreeMap::new();
1268 let mut kept_1to2: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase1to2<TestCurve>>> =
1269 BTreeMap::new();
1270 let mut transmit_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1271 for party_index in executing_parties.clone() {
1272 let (unique_keep, keep, transmit) = parties[(party_index.as_u8() - 1) as usize]
1273 .sign_phase1(all_data.get(&party_index).unwrap())
1274 .unwrap();
1275
1276 unique_kept_1to2.insert(party_index, unique_keep);
1277 kept_1to2.insert(party_index, keep);
1278 transmit_1to2.insert(party_index, transmit);
1279 }
1280
1281 let mut received_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1283
1284 for &party_index in &executing_parties {
1285 let messages_for_party: Vec<TransmitPhase1to2> = transmit_1to2
1286 .values()
1287 .flatten()
1288 .filter(|message| message.parties.receiver == party_index)
1289 .cloned()
1290 .collect();
1291
1292 received_1to2.insert(party_index, messages_for_party);
1293 }
1294
1295 let mut unique_kept_2to3: BTreeMap<PartyIndex, UniqueKeep2to3<TestCurve>> = BTreeMap::new();
1297 let mut kept_2to3: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase2to3<TestCurve>>> =
1298 BTreeMap::new();
1299 let mut transmit_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1300 BTreeMap::new();
1301 for party_index in executing_parties.clone() {
1302 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase2(
1303 all_data.get(&party_index).unwrap(),
1304 unique_kept_1to2.get(&party_index).unwrap(),
1305 kept_1to2.get(&party_index).unwrap(),
1306 received_1to2.get(&party_index).unwrap(),
1307 );
1308 match result {
1309 Err(abort) => {
1310 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1311 }
1312 Ok((unique_keep, keep, transmit)) => {
1313 unique_kept_2to3.insert(party_index, unique_keep);
1314 kept_2to3.insert(party_index, keep);
1315 transmit_2to3.insert(party_index, transmit);
1316 }
1317 }
1318 }
1319
1320 let mut received_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1322 BTreeMap::new();
1323
1324 for &party_index in &executing_parties {
1325 let messages_for_party: Vec<TransmitPhase2to3<TestCurve>> = transmit_2to3
1326 .values()
1327 .flatten()
1328 .filter(|message| message.parties.receiver == party_index)
1329 .cloned()
1330 .collect();
1331
1332 received_2to3.insert(party_index, messages_for_party);
1333 }
1334
1335 let mut x_coords: Vec<String> = Vec::with_capacity(parameters.threshold as usize);
1337 let mut broadcast_3to4: Vec<Broadcast3to4<TestCurve>> =
1338 Vec::with_capacity(parameters.threshold as usize);
1339 for party_index in executing_parties.clone() {
1340 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase3(
1341 all_data.get(&party_index).unwrap(),
1342 unique_kept_2to3.get(&party_index).unwrap(),
1343 kept_2to3.get(&party_index).unwrap(),
1344 received_2to3.get(&party_index).unwrap(),
1345 );
1346 match result {
1347 Err(abort) => {
1348 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1349 }
1350 Ok((x_coord, broadcast)) => {
1351 x_coords.push(x_coord);
1352 broadcast_3to4.push(broadcast);
1353 }
1354 }
1355 }
1356
1357 let x_coord = x_coords[0].clone(); for i in 1..parameters.threshold {
1360 assert_eq!(x_coord, x_coords[i as usize]);
1361 }
1362
1363 let some_index = executing_parties[0];
1369 let result = parties[(some_index.as_u8() - 1) as usize].sign_phase4(
1370 all_data.get(&some_index).unwrap(),
1371 &x_coord,
1372 &broadcast_3to4,
1373 false,
1374 );
1375 let signature = match result {
1376 Err(abort) => {
1377 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1378 }
1379 Ok(s) => s,
1380 };
1381 let mut total_instance_key = Scalar::ZERO;
1387 for (_, kept) in unique_kept_1to2 {
1388 total_instance_key += kept.instance_key;
1389 }
1390
1391 let total_instance_point = (AffinePoint::GENERATOR * total_instance_key).to_affine();
1393 let expected_x_coord = hex::encode(total_instance_point.x());
1394 assert_eq!(x_coord, expected_x_coord);
1395
1396 let hashed_message = Scalar::reduce(&U256::from_be_slice(&message_to_sign));
1398 assert_eq!(
1399 hashed_message,
1400 Scalar::reduce(&U256::from_be_hex(
1401 "c73f9dea26b12228c23b66686b090b61bd6a61a80c665e058320eb7c2433c9ac"
1402 ))
1403 );
1404
1405 let expected_signature_as_scalar = total_instance_key.invert().unwrap()
1407 * (hashed_message
1408 + (secret_key * Scalar::reduce(&U256::from_be_hex(&expected_x_coord))));
1409 let expected_signature = hex::encode(expected_signature_as_scalar.to_bytes());
1410
1411 let x_as_int = U256::from_be_hex(&expected_x_coord);
1413 let is_x_reduced = x_as_int >= Secp256k1::ORDER;
1414 let is_y_odd = total_instance_point.to_sec1_point(false).y().unwrap()
1415 [crate::utilities::ID_LEN - 1]
1416 & 1
1417 == 1;
1418 let expected_rec_id = (is_y_odd as u8) | ((is_x_reduced as u8) << 1);
1419
1420 assert_eq!(signature.0, expected_signature);
1422 assert_eq!(signature.1, expected_rec_id);
1423 }
1424
1425 #[test]
1430 fn test_dkg_and_signing() {
1431 let threshold = rng::get_rng().random_range(2..=5); let offset = rng::get_rng().random_range(0..=5);
1435
1436 let parameters = Parameters {
1437 threshold,
1438 share_count: threshold + offset,
1439 }; let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1441
1442 let mut all_data: Vec<SessionData> = Vec::with_capacity(parameters.share_count as usize);
1444 for i in 0..parameters.share_count {
1445 all_data.push(SessionData {
1446 parameters: parameters.clone(),
1447 party_index: PartyIndex::new(i + 1).unwrap(),
1448 session_id: session_id.to_vec(),
1449 });
1450 }
1451
1452 let mut dkg_1: Vec<Vec<Scalar>> = Vec::with_capacity(parameters.share_count as usize);
1454 for i in 0..parameters.share_count {
1455 let out1 = phase1::<TestCurve>(&all_data[i as usize]);
1456
1457 dkg_1.push(out1);
1458 }
1459
1460 let mut poly_fragments = vec![
1463 Vec::<Scalar>::with_capacity(parameters.share_count as usize);
1464 parameters.share_count as usize
1465 ];
1466 for row_i in dkg_1 {
1467 for j in 0..parameters.share_count {
1468 poly_fragments[j as usize].push(row_i[j as usize]);
1469 }
1470 }
1471
1472 let mut poly_points: Vec<Scalar> = Vec::with_capacity(parameters.share_count as usize);
1474 let mut proofs_commitments: Vec<ProofCommitment<TestCurve>> =
1475 Vec::with_capacity(parameters.share_count as usize);
1476 let mut zero_kept_2to3: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase2to3>> =
1477 Vec::with_capacity(parameters.share_count as usize);
1478 let mut zero_transmit_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>> =
1479 Vec::with_capacity(parameters.share_count as usize);
1480 let mut bip_kept_2to3: Vec<UniqueKeepDerivationPhase2to3> =
1481 Vec::with_capacity(parameters.share_count as usize);
1482 let mut bip_broadcast_2to4: BTreeMap<PartyIndex, BroadcastDerivationPhase2to4> =
1483 BTreeMap::new();
1484 for i in 0..parameters.share_count {
1485 let (out1, out2, out3, out4, out5, out6) =
1486 phase2(&all_data[i as usize], &poly_fragments[i as usize]);
1487
1488 poly_points.push(out1);
1489 proofs_commitments.push(out2);
1490 zero_kept_2to3.push(out3);
1491 zero_transmit_2to4.push(out4);
1492 bip_kept_2to3.push(out5);
1493 bip_broadcast_2to4.insert(PartyIndex::new(i + 1).unwrap(), out6); }
1495
1496 let mut zero_received_2to4: Vec<Vec<TransmitInitZeroSharePhase2to4>> =
1498 Vec::with_capacity(parameters.share_count as usize);
1499 for i in 1..=parameters.share_count {
1500 let i_idx = PartyIndex::new(i).unwrap();
1504 let mut new_row: Vec<TransmitInitZeroSharePhase2to4> =
1505 Vec::with_capacity((parameters.share_count - 1) as usize);
1506 for party in &zero_transmit_2to4 {
1507 for message in party {
1508 if message.parties.receiver == i_idx {
1510 new_row.push(message.clone());
1511 }
1512 }
1513 }
1514 zero_received_2to4.push(new_row);
1515 }
1516
1517 let mut zero_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitZeroSharePhase3to4>> =
1522 Vec::with_capacity(parameters.share_count as usize);
1523 let mut zero_transmit_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>> =
1524 Vec::with_capacity(parameters.share_count as usize);
1525 let mut mul_kept_3to4: Vec<BTreeMap<PartyIndex, KeepInitMulPhase3to4<TestCurve>>> =
1526 Vec::with_capacity(parameters.share_count as usize);
1527 let mut mul_transmit_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>> =
1528 Vec::with_capacity(parameters.share_count as usize);
1529 let mut bip_broadcast_3to4: BTreeMap<PartyIndex, BroadcastDerivationPhase3to4> =
1530 BTreeMap::new();
1531 for i in 0..parameters.share_count {
1532 let (out1, out2, out3, out4, out5) = phase3(
1533 &all_data[i as usize],
1534 &zero_kept_2to3[i as usize],
1535 &bip_kept_2to3[i as usize],
1536 );
1537
1538 zero_kept_3to4.push(out1);
1539 zero_transmit_3to4.push(out2);
1540 mul_kept_3to4.push(out3);
1541 mul_transmit_3to4.push(out4);
1542 bip_broadcast_3to4.insert(PartyIndex::new(i + 1).unwrap(), out5); }
1544
1545 let mut zero_received_3to4: Vec<Vec<TransmitInitZeroSharePhase3to4>> =
1547 Vec::with_capacity(parameters.share_count as usize);
1548 let mut mul_received_3to4: Vec<Vec<TransmitInitMulPhase3to4<TestCurve>>> =
1549 Vec::with_capacity(parameters.share_count as usize);
1550 for i in 1..=parameters.share_count {
1551 let i_idx = PartyIndex::new(i).unwrap();
1555 let mut new_row: Vec<TransmitInitZeroSharePhase3to4> =
1556 Vec::with_capacity((parameters.share_count - 1) as usize);
1557 for party in &zero_transmit_3to4 {
1558 for message in party {
1559 if message.parties.receiver == i_idx {
1561 new_row.push(message.clone());
1562 }
1563 }
1564 }
1565 zero_received_3to4.push(new_row);
1566
1567 let mut new_row: Vec<TransmitInitMulPhase3to4<TestCurve>> =
1568 Vec::with_capacity((parameters.share_count - 1) as usize);
1569 for party in &mul_transmit_3to4 {
1570 for message in party {
1571 if message.parties.receiver == i_idx {
1573 new_row.push(message.clone());
1574 }
1575 }
1576 }
1577 mul_received_3to4.push(new_row);
1578 }
1579
1580 let mut parties: Vec<Party<TestCurve>> =
1585 Vec::with_capacity(parameters.share_count as usize);
1586 for i in 0..parameters.share_count {
1587 let result = phase4(
1588 &all_data[i as usize],
1589 &poly_points[i as usize],
1590 &proofs_commitments,
1591 &zero_kept_3to4[i as usize],
1592 &zero_received_2to4[i as usize],
1593 &zero_received_3to4[i as usize],
1594 &mul_kept_3to4[i as usize],
1595 &mul_received_3to4[i as usize],
1596 &bip_broadcast_2to4,
1597 &bip_broadcast_3to4,
1598 no_address,
1599 );
1600 match result {
1601 Err(abort) => {
1602 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1603 }
1604 Ok((party, _)) => {
1605 parties.push(party);
1606 }
1607 }
1608 }
1609
1610 let expected_pk = parties[0].pk;
1612 let expected_chain_code = parties[0].derivation_data.chain_code;
1613 for party in &parties {
1614 assert_eq!(expected_pk, party.pk);
1615 assert_eq!(expected_chain_code, party.derivation_data.chain_code);
1616 }
1617
1618 let sign_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1621 let message_to_sign = tagged_hash(b"test-sign", &[b"Message to sign!"]);
1622
1623 let executing_parties: Vec<PartyIndex> = (1..=parameters.threshold)
1625 .map(|i| PartyIndex::new(i).unwrap())
1626 .collect();
1627
1628 let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1630 for party_index in executing_parties.clone() {
1631 let mut counterparties = executing_parties.clone();
1633 counterparties.retain(|index| *index != party_index);
1634
1635 all_data.insert(
1636 party_index,
1637 SignData {
1638 sign_id: sign_id.to_vec(),
1639 counterparties,
1640 message_hash: message_to_sign,
1641 },
1642 );
1643 }
1644
1645 let mut unique_kept_1to2: BTreeMap<PartyIndex, UniqueKeep1to2<TestCurve>> = BTreeMap::new();
1647 let mut kept_1to2: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase1to2<TestCurve>>> =
1648 BTreeMap::new();
1649 let mut transmit_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1650 for party_index in executing_parties.clone() {
1651 let (unique_keep, keep, transmit) = parties[(party_index.as_u8() - 1) as usize]
1652 .sign_phase1(all_data.get(&party_index).unwrap())
1653 .unwrap();
1654
1655 unique_kept_1to2.insert(party_index, unique_keep);
1656 kept_1to2.insert(party_index, keep);
1657 transmit_1to2.insert(party_index, transmit);
1658 }
1659
1660 let mut received_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1662
1663 for &party_index in &executing_parties {
1664 let messages_for_party: Vec<TransmitPhase1to2> = transmit_1to2
1665 .values()
1666 .flatten()
1667 .filter(|message| message.parties.receiver == party_index)
1668 .cloned()
1669 .collect();
1670
1671 received_1to2.insert(party_index, messages_for_party);
1672 }
1673
1674 let mut unique_kept_2to3: BTreeMap<PartyIndex, UniqueKeep2to3<TestCurve>> = BTreeMap::new();
1676 let mut kept_2to3: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase2to3<TestCurve>>> =
1677 BTreeMap::new();
1678 let mut transmit_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1679 BTreeMap::new();
1680 for party_index in executing_parties.clone() {
1681 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase2(
1682 all_data.get(&party_index).unwrap(),
1683 unique_kept_1to2.get(&party_index).unwrap(),
1684 kept_1to2.get(&party_index).unwrap(),
1685 received_1to2.get(&party_index).unwrap(),
1686 );
1687 match result {
1688 Err(abort) => {
1689 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1690 }
1691 Ok((unique_keep, keep, transmit)) => {
1692 unique_kept_2to3.insert(party_index, unique_keep);
1693 kept_2to3.insert(party_index, keep);
1694 transmit_2to3.insert(party_index, transmit);
1695 }
1696 }
1697 }
1698
1699 let mut received_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1701 BTreeMap::new();
1702
1703 for &party_index in &executing_parties {
1704 let messages_for_party: Vec<TransmitPhase2to3<TestCurve>> = transmit_2to3
1705 .values()
1706 .flatten()
1707 .filter(|message| message.parties.receiver == party_index)
1708 .cloned()
1709 .collect();
1710
1711 received_2to3.insert(party_index, messages_for_party);
1712 }
1713
1714 let mut x_coords: Vec<String> = Vec::with_capacity(parameters.threshold as usize);
1716 let mut broadcast_3to4: Vec<Broadcast3to4<TestCurve>> =
1717 Vec::with_capacity(parameters.threshold as usize);
1718 for party_index in executing_parties.clone() {
1719 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase3(
1720 all_data.get(&party_index).unwrap(),
1721 unique_kept_2to3.get(&party_index).unwrap(),
1722 kept_2to3.get(&party_index).unwrap(),
1723 received_2to3.get(&party_index).unwrap(),
1724 );
1725 match result {
1726 Err(abort) => {
1727 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1728 }
1729 Ok((x_coord, broadcast)) => {
1730 x_coords.push(x_coord);
1731 broadcast_3to4.push(broadcast);
1732 }
1733 }
1734 }
1735
1736 let x_coord = x_coords[0].clone(); for i in 1..parameters.threshold {
1739 assert_eq!(x_coord, x_coords[i as usize]);
1740 }
1741
1742 let some_index = executing_parties[0];
1748 let result = parties[(some_index.as_u8() - 1) as usize].sign_phase4(
1749 all_data.get(&some_index).unwrap(),
1750 &x_coord,
1751 &broadcast_3to4,
1752 true,
1753 );
1754 if let Err(abort) = result {
1755 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1756 }
1757 }
1759
1760 #[test]
1762 fn test_sign_phase4_zero_denominator_returns_abort() {
1763 let parameters = Parameters {
1764 threshold: 2,
1765 share_count: 2,
1766 };
1767 let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1768 let secret_key = Scalar::random(&mut rng::get_rng());
1769 let (parties, _) =
1770 re_key::<TestCurve>(¶meters, &session_id, &secret_key, None, no_address);
1771
1772 let data = SignData {
1773 sign_id: rng::get_rng()
1774 .random::<[u8; crate::utilities::ID_LEN]>()
1775 .to_vec(),
1776 counterparties: vec![PartyIndex::new(2).unwrap()],
1777 message_hash: tagged_hash(b"test-sign", &[b"Message to sign!"]),
1778 };
1779
1780 let received = vec![Broadcast3to4 {
1781 u: Scalar::ZERO,
1782 w: Scalar::ONE,
1783 }];
1784 let result = parties[0].sign_phase4(&data, "01", &received, true);
1785 let abort = result.expect_err("zero denominator should return abort");
1786 assert_eq!(abort.kind, AbortKind::Recoverable);
1787 assert!(matches!(abort.reason, AbortReason::ZeroDenominator));
1788 }
1789
1790 #[test]
1792 fn test_verify_ecdsa_signature_rejects_malformed_hex() {
1793 use k256::AffinePoint;
1794 let message = tagged_hash(b"test-sign", &[b"Message to sign!"]);
1795 let pk = AffinePoint::GENERATOR;
1796
1797 assert!(!verify_ecdsa_signature::<TestCurve>(
1798 &message, &pk, "zz", "11"
1799 ));
1800 assert!(!verify_ecdsa_signature::<TestCurve>(&message, &pk, "", ""));
1801 assert!(!verify_ecdsa_signature::<TestCurve>(
1802 &message,
1803 &pk,
1804 "010203",
1805 "not-a-hex-string"
1806 ));
1807 }
1808
1809 #[test]
1811 fn test_sign_phase1_rejects_duplicate_counterparty() {
1812 let parameters = Parameters {
1813 threshold: 3,
1814 share_count: 3,
1815 };
1816 let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1817 let secret_key = Scalar::random(&mut rng::get_rng());
1818 let (parties, _) =
1819 re_key::<TestCurve>(¶meters, &session_id, &secret_key, None, no_address);
1820
1821 let data = SignData {
1822 sign_id: rng::get_rng()
1823 .random::<[u8; crate::utilities::ID_LEN]>()
1824 .to_vec(),
1825 counterparties: vec![PartyIndex::new(2).unwrap(), PartyIndex::new(2).unwrap()],
1826 message_hash: tagged_hash(b"test-sign", &[b"Message to sign!"]),
1827 };
1828
1829 let abort = parties[0]
1830 .sign_phase1(&data)
1831 .expect_err("duplicate counterparty should be rejected");
1832 assert_eq!(abort.kind, AbortKind::Recoverable);
1833 assert!(matches!(
1834 abort.reason,
1835 AbortReason::DuplicateCounterparty { .. }
1836 ));
1837 }
1838
1839 #[test]
1841 fn test_sign_phase1_rejects_missing_mul_state() {
1842 let (parties, all_data, _, _, _) = setup_two_party_signing_phase1();
1843 let mut party = parties[0].clone();
1844 party.mul_senders.remove(&PartyIndex::new(2).unwrap());
1845
1846 let abort = party
1847 .sign_phase1(
1848 all_data
1849 .get(&PartyIndex::new(1).unwrap())
1850 .expect("party data should exist"),
1851 )
1852 .expect_err("missing multiplication state should be rejected");
1853 assert_eq!(abort.kind, AbortKind::Recoverable);
1854 assert!(matches!(abort.reason, AbortReason::MissingMulState { .. }));
1855 }
1856
1857 #[allow(clippy::type_complexity)]
1858 fn setup_two_party_signing_phase1() -> (
1859 Vec<Party<TestCurve>>,
1860 BTreeMap<PartyIndex, SignData>,
1861 BTreeMap<PartyIndex, UniqueKeep1to2<TestCurve>>,
1862 BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase1to2<TestCurve>>>,
1863 BTreeMap<PartyIndex, Vec<TransmitPhase1to2>>,
1864 ) {
1865 let parameters = Parameters {
1866 threshold: 2,
1867 share_count: 2,
1868 };
1869 let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1870 let secret_key = Scalar::random(&mut rng::get_rng());
1871 let (parties, _) =
1872 re_key::<TestCurve>(¶meters, &session_id, &secret_key, None, no_address);
1873
1874 let sign_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1875 let message_to_sign = tagged_hash(b"test-sign", &[b"Message to sign!"]);
1876
1877 let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1878 all_data.insert(
1879 PartyIndex::new(1).unwrap(),
1880 SignData {
1881 sign_id: sign_id.to_vec(),
1882 counterparties: vec![PartyIndex::new(2).unwrap()],
1883 message_hash: message_to_sign,
1884 },
1885 );
1886 all_data.insert(
1887 PartyIndex::new(2).unwrap(),
1888 SignData {
1889 sign_id: sign_id.to_vec(),
1890 counterparties: vec![PartyIndex::new(1).unwrap()],
1891 message_hash: message_to_sign,
1892 },
1893 );
1894
1895 let mut unique_kept_1to2: BTreeMap<PartyIndex, UniqueKeep1to2<TestCurve>> = BTreeMap::new();
1896 let mut kept_1to2: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase1to2<TestCurve>>> =
1897 BTreeMap::new();
1898 let mut transmit_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1899 for party_index in [PartyIndex::new(1).unwrap(), PartyIndex::new(2).unwrap()] {
1900 let (unique_keep, keep, transmit) = match parties[(party_index.as_u8() - 1) as usize]
1901 .sign_phase1(all_data.get(&party_index).unwrap())
1902 {
1903 Ok(result) => result,
1904 Err(abort) => {
1905 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1906 }
1907 };
1908
1909 unique_kept_1to2.insert(party_index, unique_keep);
1910 kept_1to2.insert(party_index, keep);
1911 transmit_1to2.insert(party_index, transmit);
1912 }
1913
1914 let mut received_1to2: BTreeMap<PartyIndex, Vec<TransmitPhase1to2>> = BTreeMap::new();
1915 for party_index in [PartyIndex::new(1).unwrap(), PartyIndex::new(2).unwrap()] {
1916 let messages_for_party: Vec<TransmitPhase1to2> = transmit_1to2
1917 .values()
1918 .flatten()
1919 .filter(|message| message.parties.receiver == party_index)
1920 .cloned()
1921 .collect();
1922 received_1to2.insert(party_index, messages_for_party);
1923 }
1924
1925 (
1926 parties,
1927 all_data,
1928 unique_kept_1to2,
1929 kept_1to2,
1930 received_1to2,
1931 )
1932 }
1933
1934 #[allow(clippy::type_complexity)]
1935 fn run_two_party_phase2(
1936 parties: &[Party<TestCurve>],
1937 all_data: &BTreeMap<PartyIndex, SignData>,
1938 unique_kept_1to2: &BTreeMap<PartyIndex, UniqueKeep1to2<TestCurve>>,
1939 kept_1to2: &BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase1to2<TestCurve>>>,
1940 received_1to2: &BTreeMap<PartyIndex, Vec<TransmitPhase1to2>>,
1941 ) -> (
1942 BTreeMap<PartyIndex, UniqueKeep2to3<TestCurve>>,
1943 BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase2to3<TestCurve>>>,
1944 BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>>,
1945 ) {
1946 let mut unique_kept_2to3: BTreeMap<PartyIndex, UniqueKeep2to3<TestCurve>> = BTreeMap::new();
1947 let mut kept_2to3: BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase2to3<TestCurve>>> =
1948 BTreeMap::new();
1949 let mut transmit_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1950 BTreeMap::new();
1951 for party_index in [PartyIndex::new(1).unwrap(), PartyIndex::new(2).unwrap()] {
1952 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase2(
1953 all_data.get(&party_index).unwrap(),
1954 unique_kept_1to2.get(&party_index).unwrap(),
1955 kept_1to2.get(&party_index).unwrap(),
1956 received_1to2.get(&party_index).unwrap(),
1957 );
1958 match result {
1959 Err(abort) => {
1960 panic!("Party {} aborted: {:?}", abort.index, abort.description());
1961 }
1962 Ok((unique_keep, keep, transmit)) => {
1963 unique_kept_2to3.insert(party_index, unique_keep);
1964 kept_2to3.insert(party_index, keep);
1965 transmit_2to3.insert(party_index, transmit);
1966 }
1967 }
1968 }
1969
1970 let mut received_2to3: BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>> =
1971 BTreeMap::new();
1972 for party_index in [PartyIndex::new(1).unwrap(), PartyIndex::new(2).unwrap()] {
1973 let messages_for_party: Vec<TransmitPhase2to3<TestCurve>> = transmit_2to3
1974 .values()
1975 .flatten()
1976 .filter(|message| message.parties.receiver == party_index)
1977 .cloned()
1978 .collect();
1979 received_2to3.insert(party_index, messages_for_party);
1980 }
1981
1982 (unique_kept_2to3, kept_2to3, received_2to3)
1983 }
1984
1985 fn run_two_party_phase3(
1986 parties: &[Party<TestCurve>],
1987 all_data: &BTreeMap<PartyIndex, SignData>,
1988 unique_kept_2to3: &BTreeMap<PartyIndex, UniqueKeep2to3<TestCurve>>,
1989 kept_2to3: &BTreeMap<PartyIndex, BTreeMap<PartyIndex, KeepPhase2to3<TestCurve>>>,
1990 received_2to3: &BTreeMap<PartyIndex, Vec<TransmitPhase2to3<TestCurve>>>,
1991 ) -> (String, Vec<Broadcast3to4<TestCurve>>) {
1992 let mut x_coords: Vec<String> = Vec::with_capacity(2);
1993 let mut broadcasts: Vec<Broadcast3to4<TestCurve>> = Vec::with_capacity(2);
1994 for party_index in [PartyIndex::new(1).unwrap(), PartyIndex::new(2).unwrap()] {
1995 let result = parties[(party_index.as_u8() - 1) as usize].sign_phase3(
1996 all_data.get(&party_index).unwrap(),
1997 unique_kept_2to3.get(&party_index).unwrap(),
1998 kept_2to3.get(&party_index).unwrap(),
1999 received_2to3.get(&party_index).unwrap(),
2000 );
2001 match result {
2002 Err(abort) => {
2003 panic!("Party {} aborted: {:?}", abort.index, abort.description());
2004 }
2005 Ok((x_coord, broadcast)) => {
2006 x_coords.push(x_coord);
2007 broadcasts.push(broadcast);
2008 }
2009 }
2010 }
2011
2012 assert_eq!(x_coords[0], x_coords[1]);
2013 (x_coords[0].clone(), broadcasts)
2014 }
2015
2016 #[test]
2018 fn test_sign_phase2_rejects_unknown_sender() {
2019 let (parties, all_data, unique_kept_1to2, kept_1to2, received_1to2) =
2020 setup_two_party_signing_phase1();
2021
2022 let mut tampered = received_1to2
2023 .get(&PartyIndex::new(1).unwrap())
2024 .unwrap()
2025 .clone();
2026 tampered[0].parties.sender = PartyIndex::new(3).unwrap();
2027
2028 let result = parties[0].sign_phase2(
2029 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2030 unique_kept_1to2.get(&PartyIndex::new(1).unwrap()).unwrap(),
2031 kept_1to2.get(&PartyIndex::new(1).unwrap()).unwrap(),
2032 &tampered,
2033 );
2034 let abort = result.expect_err("unknown sender should be rejected");
2035 assert_eq!(abort.kind, AbortKind::Recoverable);
2036 assert!(matches!(abort.reason, AbortReason::UnexpectedSender { .. }));
2037 }
2038
2039 #[test]
2041 fn test_sign_phase2_rejects_wrong_receiver() {
2042 let (parties, all_data, unique_kept_1to2, kept_1to2, received_1to2) =
2043 setup_two_party_signing_phase1();
2044
2045 let mut tampered = received_1to2
2046 .get(&PartyIndex::new(1).unwrap())
2047 .unwrap()
2048 .clone();
2049 tampered[0].parties.receiver = PartyIndex::new(2).unwrap();
2050
2051 let result = parties[0].sign_phase2(
2052 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2053 unique_kept_1to2.get(&PartyIndex::new(1).unwrap()).unwrap(),
2054 kept_1to2.get(&PartyIndex::new(1).unwrap()).unwrap(),
2055 &tampered,
2056 );
2057 let abort = result.expect_err("wrong receiver should be rejected");
2058 assert_eq!(abort.kind, AbortKind::Recoverable);
2059 assert!(matches!(abort.reason, AbortReason::MisroutedMessage { .. }));
2060 }
2061
2062 #[test]
2064 fn test_sign_phase2_rejects_wrong_message_count() {
2065 let (parties, all_data, unique_kept_1to2, kept_1to2, _) = setup_two_party_signing_phase1();
2066
2067 let result = parties[0].sign_phase2(
2068 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2069 unique_kept_1to2.get(&PartyIndex::new(1).unwrap()).unwrap(),
2070 kept_1to2.get(&PartyIndex::new(1).unwrap()).unwrap(),
2071 &[],
2072 );
2073 let abort = result.expect_err("wrong message count should be rejected");
2074 assert_eq!(abort.kind, AbortKind::Recoverable);
2075 assert!(matches!(
2076 abort.reason,
2077 AbortReason::WrongMessageCount { .. }
2078 ));
2079 }
2080
2081 #[test]
2083 fn test_sign_phase3_rejects_invalid_commitment_decommit() {
2084 let (parties, all_data, unique_kept_1to2, kept_1to2, received_1to2) =
2085 setup_two_party_signing_phase1();
2086 let (unique_kept_2to3, kept_2to3, received_2to3) = run_two_party_phase2(
2087 &parties,
2088 &all_data,
2089 &unique_kept_1to2,
2090 &kept_1to2,
2091 &received_1to2,
2092 );
2093
2094 let mut tampered = received_2to3
2095 .get(&PartyIndex::new(1).unwrap())
2096 .unwrap()
2097 .clone();
2098 assert!(
2099 !tampered[0].salt.is_empty(),
2100 "phase-3 decommit salt should be non-empty"
2101 );
2102 tampered[0].salt[0] ^= 1;
2103
2104 let result = parties[0].sign_phase3(
2105 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2106 unique_kept_2to3.get(&PartyIndex::new(1).unwrap()).unwrap(),
2107 kept_2to3.get(&PartyIndex::new(1).unwrap()).unwrap(),
2108 &tampered,
2109 );
2110 let abort = result.expect_err("invalid decommit should be rejected");
2111 assert_eq!(abort.kind, AbortKind::Recoverable);
2112 assert!(matches!(
2113 abort.reason,
2114 AbortReason::CommitmentMismatch { .. }
2115 ));
2116 }
2117
2118 #[test]
2120 fn test_sign_phase3_rejects_wrong_message_count() {
2121 let (parties, all_data, unique_kept_1to2, kept_1to2, received_1to2) =
2122 setup_two_party_signing_phase1();
2123 let (unique_kept_2to3, kept_2to3, _) = run_two_party_phase2(
2124 &parties,
2125 &all_data,
2126 &unique_kept_1to2,
2127 &kept_1to2,
2128 &received_1to2,
2129 );
2130
2131 let result = parties[0].sign_phase3(
2132 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2133 unique_kept_2to3.get(&PartyIndex::new(1).unwrap()).unwrap(),
2134 kept_2to3.get(&PartyIndex::new(1).unwrap()).unwrap(),
2135 &[],
2136 );
2137 let abort = result.expect_err("wrong message count should be rejected");
2138 assert_eq!(abort.kind, AbortKind::Recoverable);
2139 assert!(matches!(
2140 abort.reason,
2141 AbortReason::WrongMessageCount { .. }
2142 ));
2143 }
2144
2145 #[test]
2147 fn test_sign_phase3_bans_on_inconsistent_gamma_u() {
2148 let (parties, all_data, unique_kept_1to2, kept_1to2, received_1to2) =
2149 setup_two_party_signing_phase1();
2150 let (unique_kept_2to3, kept_2to3, received_2to3) = run_two_party_phase2(
2151 &parties,
2152 &all_data,
2153 &unique_kept_1to2,
2154 &kept_1to2,
2155 &received_1to2,
2156 );
2157
2158 let mut tampered = received_2to3
2159 .get(&PartyIndex::new(1).unwrap())
2160 .unwrap()
2161 .clone();
2162 tampered[0].gamma_u =
2163 (ProjectivePoint::from(tampered[0].gamma_u) + ProjectivePoint::GENERATOR).to_affine();
2164
2165 let result = parties[0].sign_phase3(
2166 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2167 unique_kept_2to3.get(&PartyIndex::new(1).unwrap()).unwrap(),
2168 kept_2to3.get(&PartyIndex::new(1).unwrap()).unwrap(),
2169 &tampered,
2170 );
2171 let abort = result.expect_err("inconsistent gamma_u should be rejected");
2172 assert_eq!(
2173 abort.kind,
2174 AbortKind::BanCounterparty(PartyIndex::new(2).unwrap())
2175 );
2176 assert!(matches!(
2177 abort.reason,
2178 AbortReason::GammaUInconsistency { .. }
2179 ));
2180 }
2181
2182 #[test]
2184 fn test_sign_phase4_rejects_tampered_broadcast() {
2185 let (parties, all_data, unique_kept_1to2, kept_1to2, received_1to2) =
2186 setup_two_party_signing_phase1();
2187 let (unique_kept_2to3, kept_2to3, received_2to3) = run_two_party_phase2(
2188 &parties,
2189 &all_data,
2190 &unique_kept_1to2,
2191 &kept_1to2,
2192 &received_1to2,
2193 );
2194 let (x_coord, mut broadcasts) = run_two_party_phase3(
2195 &parties,
2196 &all_data,
2197 &unique_kept_2to3,
2198 &kept_2to3,
2199 &received_2to3,
2200 );
2201
2202 broadcasts[0].w += Scalar::ONE;
2203
2204 let result = parties[0].sign_phase4(
2205 all_data.get(&PartyIndex::new(1).unwrap()).unwrap(),
2206 &x_coord,
2207 &broadcasts,
2208 true,
2209 );
2210 let abort = result.expect_err("tampered broadcast should fail signature validation");
2211 assert_eq!(abort.kind, AbortKind::Recoverable);
2212 assert!(matches!(
2213 abort.reason,
2214 AbortReason::SignatureVerificationFailed
2215 ));
2216 }
2217}