Skip to main content

dkls23_core/protocols/
signing.rs

1//! `DKLs23` signing protocol.
2//!
3//! This file implements the signing phase of Protocol 3.6 from `DKLs23`
4//! (<https://eprint.iacr.org/2023/765.pdf>). It is the core of this repository.
5//!
6//! # Nomenclature
7//!
8//! For the messages structs, we will use the following nomenclature:
9//!
10//! **Transmit** messages refer to only one counterparty, hence
11//! we must produce a whole vector of them. Each message in this
12//! vector contains the party index to whom we should send it.
13//!
14//! **Broadcast** messages refer to all counterparties at once,
15//! hence we only need to produce a unique instance of it.
16//! This message is broadcasted to all parties.
17//!
18//! ATTENTION: we broadcast the message to ourselves as well!
19//!
20//! **Keep** messages refer to only one counterparty, hence
21//! we must keep a whole vector of them. In this implementation,
22//! we use a `BTreeMap` instead of a vector, where one can put
23//! some party index in the key to retrieve the corresponding data.
24//!
25//! **Unique keep** messages refer to all counterparties at once,
26//! hence we only need to keep a unique instance of it.
27//!
28//! The keep-state types in this module, along with [`Party::sign_phase1`]
29//! through [`Party::sign_phase4`], are public low-level APIs for advanced
30//! resumable/stateless orchestration. [`crate::protocols::sign_session::SignSession`]
31//! remains the preferred high-level API.
32
33use 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/// Data needed to start the signature and is used during the phases.
54#[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    /// Vector containing the indices of the parties participating in the protocol (without us).
59    pub counterparties: Vec<PartyIndex>,
60    /// Hash of message being signed.
61    pub message_hash: HashOutput,
62}
63
64// STRUCTS FOR MESSAGES TO TRANSMIT IN COMMUNICATION ROUNDS.
65
66/// Transmit - Signing.
67///
68/// The message is produced/sent during Phase 1 and used in Phase 2.
69#[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/// Transmit - Signing.
79///
80/// The message is produced/sent during Phase 2 and used in Phase 3.
81#[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/// Broadcast - Signing.
107///
108/// The message is produced/sent during Phase 3 and used in Phase 4.
109#[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// STRUCTS FOR MESSAGES TO KEEP BETWEEN PHASES.
124
125/// Keep - Signing.
126///
127/// The message is produced during Phase 1 and used in Phase 2.
128#[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/// Keep - Signing.
144///
145/// The message is produced during Phase 2 and used in Phase 3.
146#[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/// Unique keep - Signing.
164///
165/// The message is produced during Phase 1 and used in Phase 2.
166#[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/// Unique keep - Signing.
184///
185/// The message is produced during Phase 2 and used in Phase 3.
186#[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// MessageTag implementations.
206#[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
230// SIGNING PROTOCOL
231// We now follow Protocol 3.6 of DKLs23.
232
233/// Implementations related to the `DKLs23` signing protocol ([read more](self)).
234impl<C: DklsCurve> Party<C> {
235    /// Phase 1 for signing: Steps 4, 5 and 6 from
236    /// Protocol 3.6 in <https://eprint.iacr.org/2023/765.pdf>.
237    ///
238    /// The outputs should be kept or transmitted according to the conventions
239    /// [here](self).
240    ///
241    /// # Errors
242    ///
243    /// Will return `Err` if the number of counterparties is wrong, if any
244    /// party index is out of range, or if the counterparty list contains our
245    /// own index.
246    #[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        // Step 4 - Check if we have the correct number of counter parties.
259        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        // Validate party index ranges and uniqueness.
270        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        // Step 5 - Sample secret data.
315        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        // Step 6 - Prepare the messages to keep and to send.
322
323        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            // Commit functionality.
328            let (commitment, salt) = commit_point::<C>(&instance_point);
329
330            // Two-party multiplication functionality.
331            // We start as the receiver.
332
333            // First, let us compute a session id for it.
334            // As in Protocol 3.6 of DKLs23, we include the indexes from the parties.
335            // We also use both the sign id and the DKG id.
336            // The chain code binds the signing session to the derived key path.
337            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            // We run the first phase.
348            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            // We gather the messages.
370            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        // Zero-shares functionality.
389        // We put it here because it doesn't depend on counter parties.
390
391        // We first compute a session id.
392        // Now, different to DKLs23, we won't put the indexes from the parties
393        // because the sign id refers only to this set of parties, hence
394        // it's simpler and almost equivalent to take just the following:
395        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        // "Unique" because it is only one message referring to all counter parties.
408        let unique_keep = UniqueKeep1to2 {
409            instance_key,
410            instance_point,
411            inversion_mask,
412            zeta,
413        };
414
415        // We now return all these values.
416        Ok((unique_keep, keep, transmit))
417    }
418
419    // Communication round 1
420    // Transmit the messages.
421
422    /// Phase 2 for signing: Step 7 from
423    /// Protocol 3.6 in <https://eprint.iacr.org/2023/765.pdf>.
424    ///
425    /// The inputs come from the previous phase. The messages received
426    /// should be gathered in a vector (in any order).
427    ///
428    /// The outputs should be kept or transmitted according to the conventions
429    /// [here](self).
430    ///
431    /// # Errors
432    ///
433    /// Will return `Err` if the multiplication protocol fails.
434    ///
435    /// # Panics
436    ///
437    /// Will panic if the list of keys in the `BTreeMap`'s are incompatible
438    /// with the party indices in the vector `received`.
439    #[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        // Step 7
455
456        // Compute the values that only depend on us.
457
458        // We find the Lagrange coefficient associated to us.
459        // It is the same as the one calculated during DKG.
460        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        // These are sk_i and pk_i from the paper.
479        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        // This is the input for the multiplication protocol.
484        let input = vec![unique_kept.instance_key, key_share];
485
486        // Now, we compute the variables related to each counter party.
487        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            // Validate sender identity before processing (defense-in-depth against misrouting).
502            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            // We continue the multiplication protocol to get the values
536            // c^u and c^v from the paper. We are now the sender.
537
538            // Let us retrieve the session id for multiplication.
539            // Note that the roles are now reversed.
540            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            // We compute the remaining values.
580            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                // Check-adjust
601                gamma_u,
602                gamma_v,
603                psi,
604                public_share,
605                // Decommit
606                instance_point: unique_kept.instance_point,
607                salt: current_kept.salt.clone(),
608                // Multiply
609                mul_transmit,
610            });
611        }
612
613        // Common values to keep for the next phase.
614        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    // Communication round 2
626    // Transmit the messages.
627
628    /// Phase 3 for signing: Steps 8 and 9 from
629    /// Protocol 3.6 in <https://eprint.iacr.org/2023/765.pdf>.
630    ///
631    /// The inputs come from the previous phase. The messages received
632    /// should be gathered in a vector (in any order).
633    ///
634    /// The first output is already the value `r` from the ECDSA signature.
635    /// The second output should be broadcasted according to the conventions
636    /// [here](self).
637    ///
638    /// # Errors
639    ///
640    /// Will return `Err` if some commitment doesn't verify, if the multiplication
641    /// protocol fails or if one of the consistency checks is false. The error
642    /// will also happen if the total instance point is trivial (very unlikely).
643    ///
644    /// # Panics
645    ///
646    /// Will panic if the list of keys in the `BTreeMap`'s are incompatible
647    /// with the party indices in the vector `received`.
648    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        // Steps 8 and 9
656
657        // The following values will represent the sums calculated in this step.
658        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            // Validate sender identity before processing (defense-in-depth against misrouting).
678            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            // Checking the committed value.
719            let verification = verify_commitment_point::<C>(
720                &message.instance_point,
721                &current_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            // Finishing the multiplication protocol.
732            // We are now the receiver.
733
734            // Let us retrieve the session id for multiplication.
735            // Note that we reverse the roles again.
736            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, &current_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            // First consistency checks.
775            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            // In the paper, they write "Lagrange(P, j, 0) · P(j)". For the math
787            // to be consistent, we believe it should be "pk_j" instead.
788            // This agrees with the alternative computation of gamma_v at the
789            // end of page 21 in the paper.
790            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            // We add the current summand to our sums.
799            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        // Second consistency check.
812        if expected_public_key != self.pk {
813            return Err(Abort::recoverable(
814                self.party_index,
815                AbortReason::PolynomialInconsistency,
816            ));
817        }
818
819        // We introduce another consistency check: the total instance point
820        // should not be the point at infinity (this is not specified on
821        // DKLs23 but actually on ECDSA itself). In any case, the probability
822        // of this happening is very low.
823        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        // Compute u_i, v_i and w_i from the paper.
831        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        // There is no salt because the hash function here is always the same.
836        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        // We also return the x-coordinate of the instance point.
843        // This is half of the final signature.
844
845        Ok((x_coord, broadcast))
846    }
847
848    // Communication round 3
849    // Broadcast the messages (including to ourselves).
850
851    /// Phase 4 for signing: Step 10 from
852    /// Protocol 3.6 in <https://eprint.iacr.org/2023/765.pdf>.
853    ///
854    /// The inputs come from the previous phase. The messages received
855    /// should be gathered in a vector (in any order). Note that our
856    /// broadcasted message from the previous round should also appear
857    /// here.
858    ///
859    /// The first output is the value `s` from the ECDSA signature.
860    /// The second output is the recovery id from the ECDSA signature.
861    /// Note that the parameter 'v' isn't this value, but it is used to compute it.
862    /// To know how to compute it, check the EIP which standardizes the transaction format
863    /// that you're using. For example: EIP-155, EIP-2930, EIP-1559.
864    ///
865    /// # Errors
866    ///
867    /// Will return `Err` if the final ECDSA signature is invalid
868    /// or if the denominator in signature assembly is zero.
869    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        // Step 10
877
878        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        // Normalize signature into "low S" form as described in
897        // BIP-0062 Dealing with Malleability: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
898        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        // First calculate R (signature point) in order to retrieve its y coordinate.
915        // This is necessary because we need to check if y is even or odd to calculate the
916        // recovery id. We compute R in the same way that we did in verify_ecdsa_signature:
917        // R = (G * msg_hash + pk * r_x) / s
918        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        // Compute recovery id from the signature point R.
927        //
928        // y-parity: determined from the compressed SEC1 prefix byte (0x02=even, 0x03=odd).
929        let compressed = rustcrypto_group::GroupEncoding::to_bytes(&signature_point);
930        let is_y_odd = compressed.as_ref()[0] == 0x03;
931
932        // is_x_reduced: true when R.x (as a field element) >= the curve order n,
933        // meaning Scalar::reduce(&R.x) lost information. This is negligibly rare
934        // but must be correct for downstream key-recovery flows.
935        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        // Recovery ID: bit 0 = y_is_odd, bit 1 = x_is_reduced
945        let rec_id = (is_y_odd as u8) | ((is_x_reduced as u8) << 1);
946
947        Ok((signature, rec_id))
948    }
949}
950
951/// Parses a hex string as a canonical scalar for curve `C` (value must be < curve order n).
952///
953/// Returns `None` if the hex string is invalid or represents a value >= n.
954/// This enforces strict ECDSA range requirements for signature verification.
955fn 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
964/// Reduces a 32-byte hash output into a scalar for curve `C`.
965fn 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
973/// Reduces a hex string into a scalar for curve `C`.
974fn 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/// Usual verifying function from ECDSA.
985///
986/// It receives a message already in bytes.
987#[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    // Verify if the numbers are non-zero (valid ECDSA range check).
1004    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    /// Tests if the signing protocol generates a valid ECDSA signature.
1053    ///
1054    /// In this case, parties are sampled via the [`re_key`] function.
1055    #[test]
1056    fn test_signing() {
1057        // Disclaimer: this implementation is not the most efficient,
1058        // we are only testing if everything works! Note as well that
1059        // parties are being simulated one after the other, but they
1060        // should actually execute the protocol simultaneously.
1061
1062        let threshold = rng::get_rng().random_range(2..=5); // You can change the ranges here.
1063        let offset = rng::get_rng().random_range(0..=5);
1064
1065        let parameters = Parameters {
1066            threshold,
1067            share_count: threshold + offset,
1068        }; // You can fix the parameters if you prefer.
1069
1070        // We use the re_key function to quickly sample the parties.
1071        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>(&parameters, &session_id, &secret_key, None, no_address);
1075
1076        // SIGNING
1077
1078        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        // For simplicity, we are testing only the first parties.
1082        let executing_parties: Vec<PartyIndex> = (1..=parameters.threshold)
1083            .map(|i| PartyIndex::new(i).unwrap())
1084            .collect();
1085
1086        // Each party prepares their data for this signing session.
1087        let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1088        for party_index in executing_parties.clone() {
1089            //Gather the counterparties
1090            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        // Phase 1
1104        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        // Communication round 1
1119        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        // Phase 2
1133        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        // Communication round 2
1158        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        // Phase 3
1173        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        // We verify all parties got the same x coordinate.
1195        let x_coord = x_coords[0].clone(); // We take the first one as reference.
1196        for i in 1..parameters.threshold {
1197            assert_eq!(x_coord, x_coords[i as usize]);
1198        }
1199
1200        // Communication round 3
1201        // This is a broadcast to all parties. The desired result is already broadcast_3to4.
1202
1203        // Phase 4
1204        // It is essentially independent of the party, so we compute just once.
1205        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        // We could call verify_ecdsa_signature here, but it is already called during Phase 4.
1216    }
1217
1218    /// Tests if the signing protocol generates a valid ECDSA signature
1219    /// and that it is the same one as we would get if we knew the
1220    /// secret key shared by the parties.
1221    ///
1222    /// In this case, parties are sampled via the [`re_key`] function.
1223    #[test]
1224    fn test_signing_against_ecdsa() {
1225        let threshold = rng::get_rng().random_range(2..=5); // You can change the ranges here.
1226        let offset = rng::get_rng().random_range(0..=5);
1227
1228        let parameters = Parameters {
1229            threshold,
1230            share_count: threshold + offset,
1231        }; // You can fix the parameters if you prefer.
1232
1233        // We use the re_key function to quickly sample the parties.
1234        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>(&parameters, &session_id, &secret_key, None, no_address);
1238
1239        // SIGNING (as in test_signing)
1240
1241        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        // For simplicity, we are testing only the first parties.
1245        let executing_parties: Vec<PartyIndex> = (1..=parameters.threshold)
1246            .map(|i| PartyIndex::new(i).unwrap())
1247            .collect();
1248
1249        // Each party prepares their data for this signing session.
1250        let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1251        for party_index in executing_parties.clone() {
1252            //Gather the counterparties
1253            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        // Phase 1
1267        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        // Communication round 1
1282        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        // Phase 2
1296        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        // Communication round 2
1321        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        // Phase 3
1336        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        // We verify all parties got the same x coordinate.
1358        let x_coord = x_coords[0].clone(); // We take the first one as reference.
1359        for i in 1..parameters.threshold {
1360            assert_eq!(x_coord, x_coords[i as usize]);
1361        }
1362
1363        // Communication round 3
1364        // This is a broadcast to all parties. The desired result is already broadcast_3to4.
1365
1366        // Phase 4
1367        // It is essentially independent of the party, so we compute just once.
1368        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        // We could call verify_ecdsa_signature here, but it is already called during Phase 4.
1382
1383        // ECDSA (computations that would be done if there were only one person)
1384
1385        // Let us retrieve the total instance/ephemeral key.
1386        let mut total_instance_key = Scalar::ZERO;
1387        for (_, kept) in unique_kept_1to2 {
1388            total_instance_key += kept.instance_key;
1389        }
1390
1391        // Compare the total "instance point" with the parties' calculations.
1392        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        // The hash of the message:
1397        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        // Now we can find the signature in the usual way.
1406        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        // Calculate the expected recovery id
1412        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        // We compare the results.
1421        assert_eq!(signature.0, expected_signature);
1422        assert_eq!(signature.1, expected_rec_id);
1423    }
1424
1425    /// Tests DKG and signing together. The main purpose is to
1426    /// verify whether the initialization protocols from DKG are working.
1427    ///
1428    /// It is a combination of `test_dkg_initialization` and [`test_signing`].
1429    #[test]
1430    fn test_dkg_and_signing() {
1431        // DKG (as in test_dkg_initialization)
1432
1433        let threshold = rng::get_rng().random_range(2..=5); // You can change the ranges here.
1434        let offset = rng::get_rng().random_range(0..=5);
1435
1436        let parameters = Parameters {
1437            threshold,
1438            share_count: threshold + offset,
1439        }; // You can fix the parameters if you prefer.
1440        let session_id = rng::get_rng().random::<[u8; crate::utilities::ID_LEN]>();
1441
1442        // Each party prepares their data for this DKG.
1443        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        // Phase 1
1453        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        // Communication round 1 - Each party receives a fragment from each counterparty.
1461        // They also produce a fragment for themselves.
1462        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        // Phase 2
1473        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); // This variable should be grouped into a BTreeMap.
1494        }
1495
1496        // Communication round 2
1497        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            // We don't need to transmit the commitments because proofs_commitments is already what we need.
1501            // In practice, this should be done here.
1502
1503            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                    // Check if this message should be sent to us.
1509                    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        // bip_transmit_2to4 is already in the format we need.
1518        // In practice, the messages received should be grouped into a BTreeMap.
1519
1520        // Phase 3
1521        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); // This variable should be grouped into a BTreeMap.
1543        }
1544
1545        // Communication round 3
1546        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            // We don't need to transmit the proofs because proofs_commitments is already what we need.
1552            // In practice, this should be done here.
1553
1554            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                    // Check if this message should be sent to us.
1560                    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                    // Check if this message should be sent to us.
1572                    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        // bip_transmit_3to4 is already in the format we need.
1581        // In practice, the messages received should be grouped into a BTreeMap.
1582
1583        // Phase 4
1584        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        // We check if the public keys and chain codes are the same.
1611        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        // SIGNING (as in test_signing)
1619
1620        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        // For simplicity, we are testing only the first parties.
1624        let executing_parties: Vec<PartyIndex> = (1..=parameters.threshold)
1625            .map(|i| PartyIndex::new(i).unwrap())
1626            .collect();
1627
1628        // Each party prepares their data for this signing session.
1629        let mut all_data: BTreeMap<PartyIndex, SignData> = BTreeMap::new();
1630        for party_index in executing_parties.clone() {
1631            //Gather the counterparties
1632            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        // Phase 1
1646        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        // Communication round 1
1661        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        // Phase 2
1675        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        // Communication round 2
1700        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        // Phase 3
1715        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        // We verify all parties got the same x coordinate.
1737        let x_coord = x_coords[0].clone(); // We take the first one as reference.
1738        for i in 1..parameters.threshold {
1739            assert_eq!(x_coord, x_coords[i as usize]);
1740        }
1741
1742        // Communication round 3
1743        // This is a broadcast to all parties. The desired result is already broadcast_3to4.
1744
1745        // Phase 4
1746        // It is essentially independent of the party, so we compute just once.
1747        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        // We could call verify_ecdsa_signature here, but it is already called during Phase 4.
1758    }
1759
1760    /// Tests if sign_phase4 handles zero denominator without panicking.
1761    #[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>(&parameters, &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    /// Tests that malformed hex inputs are rejected without panicking.
1791    #[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    /// Tests if phase 1 rejects duplicate counterparties.
1810    #[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>(&parameters, &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    /// Tests if phase 1 rejects missing multiplication state.
1840    #[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>(&parameters, &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    /// Tests if phase 2 rejects messages from unknown senders.
2017    #[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    /// Tests if phase 2 rejects messages addressed to a different receiver.
2040    #[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    /// Tests if phase 2 rejects message vectors with unexpected size.
2063    #[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    /// Tests if phase 3 rejects invalid decommitment data.
2082    #[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    /// Tests if phase 3 rejects message vectors with unexpected size.
2119    #[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    /// Tests if phase 3 emits a ban abort on gamma_u inconsistency.
2146    #[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    /// Tests if phase 4 rejects tampered broadcast values that invalidate signature assembly.
2183    #[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}