Skip to main content

cggmp24/
signing.rs

1//! Signing protocol
2
3use digest::Digest;
4use futures::SinkExt;
5use generic_ec::{coords::AlwaysHasAffineX, Curve, NonZero, Point, Scalar, SecretScalar};
6use generic_ec_zkp::polynomial::lagrange_coefficient_at_zero;
7use paillier_zk::{backend::Integer, fast_paillier};
8use paillier_zk::{
9    dlog_with_el_gamal_commitment as pi_elog, paillier_affine_operation_in_range as pi_aff,
10    paillier_encryption_in_range_with_el_gamal as pi_enc_elg, IntegerExt,
11};
12use rand_core::{CryptoRng, RngCore};
13use round_based::{
14    rounds_router::{simple_store::RoundInput, RoundsRouter},
15    runtime::AsyncRuntime,
16    Delivery, Mpc, MpcParty, Outgoing, PartyIndex,
17};
18use serde::{Deserialize, Serialize};
19use thiserror::Error;
20
21use crate::errors::IoError;
22use crate::key_share::{InvalidKeyShare, KeyShare, PedersenParams, VssSetup};
23use crate::progress::Tracer;
24use crate::{security_level::SecurityLevel, utils, ExecutionId};
25
26use self::msg::*;
27
28/// A message digest that is guaranteed to have a known preimage, making it safe for signing.
29///
30/// This struct wraps a scalar value ([`Scalar<E>`](Scalar)) that represents the cryptographic hash
31/// of a message. It is the primary and recommended type for all signing operations in this library.
32///
33/// ## Purpose and Safety
34///
35/// The key security feature of `DataToSign` is its construction method. An instance of this struct
36/// can only be created by providing the original message bytes to the library's API. The library
37/// then performs the hashing internally.
38///
39/// This design acts as a type-safe guard, ensuring that the system will only ever sign a hash for
40/// which the original message (the "preimage") is known and has been processed by the library.
41/// This prevents a critical class of signature forgery attacks where an attacker provides a raw,
42/// maliciously crafted hash (refer to [`PrehashedDataToSign`] docs to learn more about this attack).
43#[derive(Debug, Clone, Copy)]
44pub struct DataToSign<E: Curve>(Scalar<E>);
45
46impl<E: Curve> DataToSign<E> {
47    /// Construct a `DataToSign` by hashing `data` with algorithm `D`
48    pub fn digest<D: Digest>(data: &[u8]) -> Self {
49        DataToSign(Scalar::from_be_bytes_mod_order(D::digest(data)))
50    }
51
52    /// Constructs a `DataToSign` from output of given digest
53    pub fn from_digest<D: Digest>(hash: D) -> Self {
54        DataToSign(Scalar::from_be_bytes_mod_order(hash.finalize()))
55    }
56
57    /// Returns a scalar that represents a data to be signed
58    pub fn to_scalar(self) -> Scalar<E> {
59        self.0
60    }
61}
62
63/// A pre-hashed message digest intended for signing, where the original message (preimage) may
64/// be unknown.
65///
66/// This struct wraps a scalar value representing a message digest. Unlike its safer counterpart,
67/// [`DataToSign`], this struct can be constructed directly from a raw scalar, bypassing the safety
68/// check that ensures the original message is known.
69///
70/// ## Context: `PrehashedDataToSign` vs. `DataToSign`
71/// This library provides two types for data to be signed:
72///
73/// * [`DataToSign`] **(Recommended)**: This is the safe, default option. Its API requires you to
74///   provide the original message bytes. The library then handles the hashing internally. This acts
75///   as a type-safe guard, guaranteeing that the signature corresponds to a known message.
76/// * `PrehashedDataToSign` **(Advanced)**: This is a low-level type for specific, advanced use
77///   cases. It contains a hash that was computed externally.
78///
79/// This `PrehashedDataToSign` struct is only accepted by library functions where it is safe to do
80/// so—specifically, in the full interactive signing protocol where the forgery attacks described
81/// below are not applicable.
82///
83/// ## ⚠️ Assume Preimage Known: Advanced Use Only
84/// This library offers a feature flag named `insecure-assume-preimage-known` for
85/// highly specialized use cases. When enabled, this feature exposes the method
86/// [`PrehashedDataToSign::insecure_assume_preimage_known`] which allows you to convert a
87/// `PrehashedDataToSign` directly into a [`DataToSign`]. This is a powerful but extremely dangerous
88/// operation. It overrides the library's type safety, telling the system to trust that a known
89/// original message exists for the given hash, even if one does not.
90///
91/// This feature **completely bypasses** the security guarantees provided by the [`DataToSign`] type
92/// and should almost never be used.
93///
94/// ### Potential Attack: Signature Forgery via Raw Hash Signing
95///
96/// This attack, detailed in [this paper](https://eprint.iacr.org/2021/1330.pdf) (Section 1.1.2, “An
97/// attack on ECDSA with presignatures”), enables signature forgery when a system signs raw hashes
98/// in combination with presignatures.
99///
100/// The core idea is that an attacker can forge a signature for a message of their choice, `m'`, by
101/// tricking the system into signing a different, maliciously crafted hash, `h`.
102///
103/// Here’s a simplified breakdown of how it works:
104///
105/// * **Message Selection**: The attacker first chooses a target message, `m'`, that they want a
106///   forged signature for
107/// * **Malicious Hash Construction**: Instead of submitting `h' = H(m')`, the attacker uses the
108///   hash of their target message, `H(m')`, and public data from the presignature to compute a new,
109///   malicious hash value, `h`. This `h` has no known preimage and appears random.
110/// * **Signing Request**: The attacker submits this raw hash `h` to the signing protocol.
111/// * **Forgery**: The protocol signs `h` and returns a signature component. The attacker then uses
112///   this component to mathematically compute the final, valid signature for their original target
113///   message, `m'`.
114///
115/// Essentially, the attacker exploits the signing algorithm's structure. By carefully crafting
116/// the hash input, they can predict and manipulate the output to forge a signature for an entirely
117/// different message. This is why **signing a hash without knowing its original message (preimage)
118/// is extremely dangerous** in this context.
119#[derive(Clone, Copy, Debug)]
120pub struct PrehashedDataToSign<E: Curve>(Scalar<E>);
121
122impl<E: Curve> PrehashedDataToSign<E> {
123    /// Constructs a `PrehashedDataToSign` from scalar
124    ///
125    /// `scalar` must be output of cryptographic hash function applied to original message to be signed
126    pub fn from_scalar(scalar: Scalar<E>) -> Self {
127        Self(scalar)
128    }
129
130    /// Returns a scalar that represents a data to be signed
131    pub fn to_scalar(self) -> Scalar<E> {
132        self.0
133    }
134
135    /// Converts a `PrehashedDataToSign` directly into a [`DataToSign`]
136    ///
137    /// **⚠️ Extremely dangerous operation.** It overrides the library's type safety, telling the
138    /// system to trust that a known original message exists for the given hash, even if one does
139    /// not. Read [`PrehashedDataToSign`] docs to learn why it's dangerous.
140    ///
141    /// It's **only** safe to use this method if you have either:
142    ///
143    /// 1. Hashed the original message yourself to generate this scalar (prefer directly using
144    ///    [`DataToSign`] when possible).
145    /// 2. Can otherwise completely verify and trust the source of the prehashed data.
146    #[cfg(feature = "insecure-assume-preimage-known")]
147    pub fn insecure_assume_preimage_known(self) -> DataToSign<E> {
148        DataToSign(self.0)
149    }
150}
151
152mod internal {
153    pub trait Sealed {}
154}
155
156/// Data to be signed, regardless of whether original message to be signed is known or not
157///
158/// This library accepts `&dyn AnyDataToSign` **only if** it doesn't matter for security whether
159/// original message is known or not.
160pub trait AnyDataToSign<E: Curve>: Send + Sync + internal::Sealed {
161    /// Returns a scalar that represents a data to be signed
162    fn to_scalar(&self) -> Scalar<E>;
163}
164impl<E: Curve> internal::Sealed for DataToSign<E> {}
165impl<E: Curve> internal::Sealed for PrehashedDataToSign<E> {}
166
167impl<E: Curve> AnyDataToSign<E> for DataToSign<E> {
168    fn to_scalar(&self) -> Scalar<E> {
169        self.0
170    }
171}
172impl<E: Curve> AnyDataToSign<E> for PrehashedDataToSign<E> {
173    fn to_scalar(&self) -> Scalar<E> {
174        self.0
175    }
176}
177
178/// Presignature, can be used to issue a [partial signature](PartialSignature) without interacting with other signers
179///
180/// [Threshold](crate::key_share::AnyKeyShare::min_signers) amount of partial signatures (from different signers) can be [combined](PartialSignature::combine) into regular signature
181#[derive(Clone, Serialize, Deserialize)]
182#[serde(bound = "")]
183pub struct Presignature<E: Curve> {
184    /// $\Gamma$ component of presignature
185    pub Gamma: NonZero<Point<E>>,
186    /// $\tilde k_i$ component of presignature
187    pub tilde_k: SecretScalar<E>,
188    /// $\tilde \chi_i$ component of presignature
189    pub tilde_chi: SecretScalar<E>,
190}
191
192/// Public part of the presignature that can be used to verify partial signatures from other parties
193///
194/// They are used to validate partial signature produced by the signers from a presignature
195#[derive(Clone, Debug, PartialEq, Eq)]
196pub struct PresignaturePublicData<E: Curve> {
197    /// $\Gamma$ presignature commitment
198    pub Gamma: NonZero<Point<E>>,
199    /// $(\tilde \Delta_j, \tilde S_j)_{j\in\[n\]}$
200    pub commitments: Vec<PresignatureCommitment<E>>,
201}
202
203/// Presignature commitment, used to verify partial signature correctness
204#[derive(Clone, Debug, PartialEq, Eq)]
205pub struct PresignatureCommitment<E: Curve> {
206    /// $\tilde \Delta_j$
207    pub tilde_Delta: Point<E>,
208    /// $\tilde \S_j$
209    pub tilde_S: Point<E>,
210}
211
212/// Partial signature issued by signer for given message
213///
214/// Can be obtained using [`Presignature::issue_partial_signature`]. Partial signature doesn't carry any sensitive inforamtion.
215///
216/// Threshold amount of partial signatures can be combined into a regular signature using [`PartialSignature::combine`]
217#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
218#[serde(bound = "")]
219pub struct PartialSignature<E: Curve> {
220    /// $\sigma$ component of partial signature
221    pub sigma: Scalar<E>,
222}
223
224/// ECDSA signature
225#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Debug)]
226#[serde(bound = "")]
227pub struct Signature<E: Curve> {
228    /// $r$ component of signature
229    pub r: NonZero<Scalar<E>>,
230    /// $s$ component of signature
231    pub s: NonZero<Scalar<E>>,
232}
233
234macro_rules! prefixed {
235    ($name:tt) => {
236        concat!("dfns.cggmp24.signing.", $name)
237    };
238}
239
240#[doc = include_str!("../docs/mpc_message.md")]
241pub mod msg {
242    use digest::Digest;
243    use generic_ec::Curve;
244    use generic_ec::{Point, Scalar};
245
246    use paillier_zk::fast_paillier;
247    use paillier_zk::{
248        dlog_with_el_gamal_commitment as pi_elog, paillier_affine_operation_in_range as pi_aff,
249        paillier_encryption_in_range_with_el_gamal as pi_enc_elg,
250    };
251    use round_based::ProtocolMessage;
252    use serde::{Deserialize, Serialize};
253
254    use crate::utils;
255
256    use super::PartialSignature;
257
258    /// Signing protocol message
259    ///
260    /// Enumerates messages from all rounds
261    #[derive(Clone, ProtocolMessage, Serialize, Deserialize)]
262    #[serde(bound = "")]
263    #[allow(clippy::large_enum_variant)]
264    pub enum Msg<E: Curve, D: Digest> {
265        /// Round 1a message
266        Round1a(MsgRound1a<E>),
267        /// Round 1b message
268        Round1b(MsgRound1b<E>),
269        /// Round 2 message
270        Round2(MsgRound2<E>),
271        /// Round 3 message
272        Round3(MsgRound3<E>),
273        /// Round 4 message
274        Round4(MsgRound4<E>),
275        /// Reliability check message (optional additional round)
276        ReliabilityCheck(MsgReliabilityCheck<D>),
277    }
278
279    /// Message from round 1a
280    #[derive(Clone, Serialize, Deserialize, udigest::Digestable)]
281    #[serde(bound = "")]
282    #[udigest(tag = prefixed!("round1"))]
283    #[udigest(bound = "")]
284    pub struct MsgRound1a<E: Curve> {
285        /// $K_i$
286        #[udigest(as = utils::encoding::Integer)]
287        pub K: fast_paillier::Ciphertext,
288        /// $G_i$
289        #[udigest(as = utils::encoding::Integer)]
290        pub G: fast_paillier::Ciphertext,
291        /// $Y_i$
292        pub Y: Point<E>,
293        /// $A_{i,1}$
294        pub A1: Point<E>,
295        /// $A_{i,2}$
296        pub A2: Point<E>,
297        /// $B_{i,1}$
298        pub B1: Point<E>,
299        /// $B_{i,2}$
300        pub B2: Point<E>,
301    }
302
303    /// Message from round 1b
304    #[derive(Clone, Serialize, Deserialize)]
305    #[serde(bound = "")]
306    pub struct MsgRound1b<E: Curve> {
307        /// $\psi^0_{j,i}$
308        pub psi0: pi_enc_elg::NiProof<E>,
309        /// $\psi^1_{j,i}$
310        pub psi1: pi_enc_elg::NiProof<E>,
311    }
312
313    /// Message from round 2
314    #[derive(Clone, Serialize, Deserialize)]
315    #[serde(bound = "")]
316    pub struct MsgRound2<E: Curve> {
317        /// $\Gamma_i$
318        pub Gamma: Point<E>,
319        /// $D_{j,i}$
320        pub D: fast_paillier::Ciphertext,
321        /// $F_{j,i}$
322        pub F: fast_paillier::Ciphertext,
323        /// $\hat D_{j,i}$
324        pub hat_D: fast_paillier::Ciphertext,
325        /// $\hat F_{j,i}$
326        pub hat_F: fast_paillier::Ciphertext,
327        /// $\psi_i$
328        pub tilde_psi: pi_elog::NiProof<E>,
329        /// $\psi_{j,i}$
330        pub psi_j: pi_aff::NiProof<E>,
331        /// $\hat \psi_{j,i}$
332        pub hat_psi_j: pi_aff::NiProof<E>,
333    }
334
335    /// Message from round 3
336    #[derive(Clone, Serialize, Deserialize)]
337    #[serde(bound = "")]
338    pub struct MsgRound3<E: Curve> {
339        /// $\delta_i$
340        pub delta: Scalar<E>,
341        /// $S_i$
342        pub S: Point<E>,
343        /// $\Delta_i$
344        pub Delta: Point<E>,
345        /// $\psi'_i$
346        pub psi_prime: pi_elog::NiProof<E>,
347    }
348
349    /// Message from round 4
350    #[derive(Clone, Serialize, Deserialize)]
351    #[serde(bound = "")]
352    pub struct MsgRound4<E: Curve> {
353        /// $\sigma_i$
354        pub partial_sig: PartialSignature<E>,
355    }
356
357    /// Message from auxiliary round for reliability check
358    #[derive(Clone, Serialize, Deserialize)]
359    #[serde(bound = "")]
360    pub struct MsgReliabilityCheck<D: Digest>(pub digest::Output<D>);
361}
362
363mod unambiguous {
364    use crate::ExecutionId;
365
366    #[derive(udigest::Digestable)]
367    #[udigest(tag = prefixed!("proof_enc"))]
368    pub struct ProofEnc<'a> {
369        pub sid: ExecutionId<'a>,
370        pub prover: u16,
371        /// Either `0` (corresponds to $\psi^0_{j,i}$) or `1` (corresponds to $\psi^1_{j,i}$)
372        pub num: u8,
373    }
374
375    #[derive(udigest::Digestable)]
376    #[udigest(tag = prefixed!("proof_psi"))]
377    pub struct ProofPsi<'a> {
378        pub sid: ExecutionId<'a>,
379        pub prover: u16,
380        pub hat: bool,
381    }
382
383    #[derive(udigest::Digestable)]
384    #[udigest(tag = prefixed!("proof_elog"))]
385    pub struct ProofElog<'a> {
386        pub sid: ExecutionId<'a>,
387        pub prover: u16,
388        pub prime: bool,
389    }
390
391    #[derive(udigest::Digestable)]
392    #[udigest(tag = prefixed!("echo_round"))]
393    #[udigest(bound = "")]
394    pub struct Echo<'a, E: generic_ec::Curve> {
395        pub sid: ExecutionId<'a>,
396        pub ciphertexts: &'a super::MsgRound1a<E>,
397    }
398}
399
400/// Signing entry point
401pub struct SigningBuilder<
402    'r,
403    E,
404    L = crate::default_choice::SecurityLevel,
405    D = crate::default_choice::Digest,
406> where
407    E: Curve,
408    L: SecurityLevel,
409    D: Digest,
410{
411    i: PartyIndex,
412    parties_indexes_at_keygen: &'r [PartyIndex],
413    key_share: &'r KeyShare<E, L>,
414    execution_id: ExecutionId<'r>,
415    tracer: Option<&'r mut dyn Tracer>,
416    enforce_reliable_broadcast: bool,
417    _digest: std::marker::PhantomData<D>,
418
419    #[cfg(feature = "hd-wallet")]
420    additive_shift: Option<Scalar<E>>,
421}
422
423impl<'r, E, L, D> SigningBuilder<'r, E, L, D>
424where
425    E: Curve,
426    NonZero<Point<E>>: AlwaysHasAffineX<E>,
427    L: SecurityLevel,
428    D: Digest + Clone + 'static,
429{
430    /// Construct a signing builder
431    pub fn new(
432        eid: ExecutionId<'r>,
433        i: PartyIndex,
434        parties_indexes_at_keygen: &'r [PartyIndex],
435        secret_key_share: &'r KeyShare<E, L>,
436    ) -> Self {
437        Self {
438            i,
439            parties_indexes_at_keygen,
440            key_share: secret_key_share,
441            execution_id: eid,
442            tracer: None,
443            enforce_reliable_broadcast: true,
444            _digest: std::marker::PhantomData,
445            #[cfg(feature = "hd-wallet")]
446            additive_shift: None,
447        }
448    }
449
450    /// Specifies another hash function to use
451    pub fn set_digest<D2>(self) -> SigningBuilder<'r, E, L, D2>
452    where
453        D2: Digest,
454    {
455        SigningBuilder {
456            i: self.i,
457            parties_indexes_at_keygen: self.parties_indexes_at_keygen,
458            key_share: self.key_share,
459            tracer: self.tracer,
460            enforce_reliable_broadcast: self.enforce_reliable_broadcast,
461            execution_id: self.execution_id,
462            _digest: std::marker::PhantomData,
463            #[cfg(feature = "hd-wallet")]
464            additive_shift: self.additive_shift,
465        }
466    }
467
468    /// Specifies a tracer that tracks progress of protocol execution
469    pub fn set_progress_tracer(mut self, tracer: &'r mut dyn Tracer) -> Self {
470        self.tracer = Some(tracer);
471        self
472    }
473
474    #[doc = include_str!("../docs/enforce_reliable_broadcast.md")]
475    pub fn enforce_reliable_broadcast(self, v: bool) -> Self {
476        Self {
477            enforce_reliable_broadcast: v,
478            ..self
479        }
480    }
481
482    /// Specifies HD derivation path
483    ///
484    /// ## Example
485    /// Set derivation path to m/1/999
486    ///
487    /// ```rust,no_run
488    /// # let eid = cggmp24::ExecutionId::new(b"protocol nonce");
489    /// # let (i, parties_indexes_at_keygen, key_share): (u16, Vec<u16>, cggmp24::KeyShare<cggmp24::supported_curves::Secp256k1>)
490    /// # = unimplemented!();
491    /// cggmp24::signing(eid, i, &parties_indexes_at_keygen, &key_share)
492    ///     .set_derivation_path([1, 999])?
493    /// # ; Ok::<_, Box<dyn std::error::Error>>(())
494    /// ```
495    ///
496    /// ## Derivation algorithm
497    /// This method uses [`hd_wallet::Slip10`] derivation algorithm, which can only be used with secp256k1
498    /// and secp256r1 curves. If you need to use another one, see
499    /// [`set_derivation_path_with_algo`](Self::set_derivation_path_with_algo)
500    #[cfg(all(feature = "hd-wallet", feature = "hd-slip10"))]
501    pub fn set_derivation_path<Index>(
502        self,
503        path: impl IntoIterator<Item = Index>,
504    ) -> Result<
505        Self,
506        crate::key_share::HdError<<Index as TryInto<hd_wallet::NonHardenedIndex>>::Error>,
507    >
508    where
509        hd_wallet::Slip10: hd_wallet::HdWallet<E>,
510        hd_wallet::NonHardenedIndex: TryFrom<Index>,
511    {
512        self.set_derivation_path_with_algo::<hd_wallet::Slip10, _>(path)
513    }
514
515    /// Specifies HD derivation path, using HD derivation algorithm [`hd_wallet::HdWallet`]
516    #[cfg(feature = "hd-wallet")]
517    pub fn set_derivation_path_with_algo<Hd: hd_wallet::HdWallet<E>, Index>(
518        mut self,
519        path: impl IntoIterator<Item = Index>,
520    ) -> Result<
521        Self,
522        crate::key_share::HdError<<Index as TryInto<hd_wallet::NonHardenedIndex>>::Error>,
523    >
524    where
525        hd_wallet::NonHardenedIndex: TryFrom<Index>,
526    {
527        use crate::key_share::HdError;
528        let public_key = self
529            .key_share
530            .extended_public_key()
531            .ok_or(HdError::DisabledHd)?;
532        self.additive_shift = Some(
533            derive_additive_shift::<E, Hd, _>(public_key, path).map_err(HdError::InvalidPath)?,
534        );
535        Ok(self)
536    }
537
538    /// Starts presignature generation protocol
539    pub async fn generate_presignature<R, M>(
540        self,
541        rng: &mut R,
542        party: M,
543    ) -> Result<(Presignature<E>, PresignaturePublicData<E>), SigningError>
544    where
545        R: RngCore + CryptoRng,
546        M: Mpc<ProtocolMessage = Msg<E, D>>,
547    {
548        match signing_t_out_of_n(
549            self.tracer,
550            rng,
551            party,
552            self.execution_id,
553            self.i,
554            self.key_share,
555            self.parties_indexes_at_keygen,
556            None,
557            self.enforce_reliable_broadcast,
558            #[cfg(feature = "hd-wallet")]
559            self.additive_shift,
560            #[cfg(not(feature = "hd-wallet"))]
561            None,
562        )
563        .await?
564        {
565            ProtocolOutput::Presignature(presig) => Ok(presig),
566            ProtocolOutput::Signature(_) => Err(Bug::UnexpectedProtocolOutput.into()),
567        }
568    }
569
570    /// Returns a state machine that can be used to carry out the presignature generation protocol
571    ///
572    /// See [`round_based::state_machine`] for details on how that can be done.
573    #[cfg(feature = "state-machine")]
574    pub fn generate_presignature_sync<R>(
575        self,
576        rng: &'r mut R,
577    ) -> impl round_based::state_machine::StateMachine<
578        Output = Result<(Presignature<E>, PresignaturePublicData<E>), SigningError>,
579        Msg = Msg<E, D>,
580    > + 'r
581    where
582        R: RngCore + CryptoRng,
583    {
584        round_based::state_machine::wrap_protocol(|party| self.generate_presignature(rng, party))
585    }
586
587    /// Starts signing protocol
588    ///
589    /// `message_to_sign` can be either [`DataToSign`] (original message being signed is known) or
590    /// [`PrehashedDataToSign`] (only hash of the message being signed is known), protocol is secure
591    /// regardless. However, the best practice is to use [`DataToSign`] whenever possible.
592    pub async fn sign<R, M>(
593        self,
594        rng: &mut R,
595        party: M,
596        message_to_sign: &dyn AnyDataToSign<E>,
597    ) -> Result<Signature<E>, SigningError>
598    where
599        R: RngCore + CryptoRng,
600        M: Mpc<ProtocolMessage = Msg<E, D>>,
601    {
602        match signing_t_out_of_n(
603            self.tracer,
604            rng,
605            party,
606            self.execution_id,
607            self.i,
608            self.key_share,
609            self.parties_indexes_at_keygen,
610            Some(message_to_sign.to_scalar()),
611            self.enforce_reliable_broadcast,
612            #[cfg(feature = "hd-wallet")]
613            self.additive_shift,
614            #[cfg(not(feature = "hd-wallet"))]
615            None,
616        )
617        .await?
618        {
619            ProtocolOutput::Signature(sig) => Ok(sig),
620            ProtocolOutput::Presignature(_) => Err(Bug::UnexpectedProtocolOutput.into()),
621        }
622    }
623
624    /// Returns a state machine that can be used to carry out the signing protocol
625    ///
626    /// See [`round_based::state_machine`] for details on how that can be done.
627    ///
628    /// `message_to_sign` can be either [`DataToSign`] (original message being signed is known) or
629    /// [`PrehashedDataToSign`] (only hash of the message being signed is known), protocol is secure
630    /// regardless. However, the best practice is to use [`DataToSign`] whenever possible.
631    #[cfg(feature = "state-machine")]
632    pub fn sign_sync<R>(
633        self,
634        rng: &'r mut R,
635        message_to_sign: &'r dyn AnyDataToSign<E>,
636    ) -> impl round_based::state_machine::StateMachine<
637        Output = Result<Signature<E>, SigningError>,
638        Msg = Msg<E, D>,
639    > + 'r
640    where
641        R: RngCore + CryptoRng,
642    {
643        round_based::state_machine::wrap_protocol(move |party| {
644            self.sign(rng, party, message_to_sign)
645        })
646    }
647}
648
649/// t-out-of-n signing
650///
651/// CGGMP paper doesn't support threshold signing out of the box. However, threshold signing
652/// can be easily implemented on top of CGGMP's [`signing_n_out_of_n`] by converting polynomial
653/// (VSS) key shares into additive (by multiplying at lagrange coefficient) and calling
654/// t-out-of-t protocol. The trick is described in more details in the spec.
655async fn signing_t_out_of_n<M, E, L, D, R>(
656    mut tracer: Option<&mut dyn Tracer>,
657    rng: &mut R,
658    party: M,
659    sid: ExecutionId<'_>,
660    i: PartyIndex,
661    key_share: &KeyShare<E, L>,
662    S: &[PartyIndex],
663    message_to_sign: Option<Scalar<E>>,
664    enforce_reliable_broadcast: bool,
665    additive_shift: Option<Scalar<E>>,
666) -> Result<ProtocolOutput<E>, SigningError>
667where
668    M: Mpc<ProtocolMessage = Msg<E, D>>,
669    E: Curve,
670    L: SecurityLevel,
671    D: Digest + Clone + 'static,
672    R: RngCore + CryptoRng,
673    NonZero<Point<E>>: AlwaysHasAffineX<E>,
674{
675    tracer.protocol_begins();
676    tracer.stage("Map t-out-of-n protocol to t-out-of-t");
677
678    // Validate arguments
679    let n: u16 = key_share
680        .aux
681        .pedersen_params
682        .len()
683        .try_into()
684        .map_err(|_| Bug::PartiesNumberExceedsU16)?;
685    let t = key_share
686        .core
687        .vss_setup
688        .as_ref()
689        .map(|s| s.min_signers)
690        .unwrap_or(n);
691    if S.len() != usize::from(t) {
692        return Err(InvalidArgs::MismatchedAmountOfParties.into());
693    }
694    if !(i < t) {
695        return Err(InvalidArgs::SignerIndexOutOfBounds.into());
696    }
697    if S.iter().any(|&S_j| S_j >= n) {
698        return Err(InvalidArgs::InvalidS.into());
699    }
700
701    // Assemble x_i and \vec X
702    let (mut x_i, mut X) = if let Some(VssSetup { I, .. }) = &key_share.core.vss_setup {
703        // For t-out-of-n keys generated via VSS DKG scheme
704        let I = utils::subset(S, I).ok_or(Bug::Subset)?;
705        let X = utils::subset(S, &key_share.core.public_shares).ok_or(Bug::Subset)?;
706
707        let lambda_i = lagrange_coefficient_at_zero(usize::from(i), &I).ok_or(Bug::LagrangeCoef)?;
708        let x_i = (lambda_i * &key_share.core.x).into_secret();
709
710        let lambda = (0..t).map(|j| lagrange_coefficient_at_zero(usize::from(j), &I));
711        let X = lambda
712            .zip(&X)
713            .map(|(lambda_j, X_j)| Some(lambda_j? * X_j))
714            .collect::<Option<Vec<_>>>()
715            .ok_or(Bug::LagrangeCoef)?;
716
717        (x_i, X)
718    } else {
719        // For n-out-of-n keys generated using original CGGMP DKG
720        let X = utils::subset(S, &key_share.core.public_shares).ok_or(Bug::Subset)?;
721        (key_share.core.x.clone(), X)
722    };
723    debug_assert_eq!(key_share.core.shared_public_key, X.iter().sum::<Point<E>>());
724
725    // Apply additive shift
726    let shift = additive_shift.unwrap_or(Scalar::zero());
727    let Shift = Point::generator() * shift;
728
729    X[0] = NonZero::from_point(X[0] + Shift).ok_or(Bug::DerivedChildKeyZero)?;
730    if i == 0 {
731        x_i = NonZero::from_scalar(x_i + shift)
732            .ok_or(Bug::DerivedChildShareZero)?
733            .into_secret();
734    }
735    debug_assert_eq!(
736        key_share.core.shared_public_key + Shift,
737        X.iter().sum::<Point<E>>()
738    );
739
740    // Assemble rest of the data
741    let (p_i, q_i) = (&key_share.aux.p, &key_share.aux.q);
742    let N = utils::subset(S, &key_share.aux.N)
743        .ok_or(Bug::Subset)?
744        .into_iter()
745        .map(fast_paillier::EncryptionKey::from_n)
746        .collect::<Vec<_>>();
747    let R = utils::subset(S, &key_share.aux.pedersen_params).ok_or(Bug::Subset)?;
748
749    // t-out-of-t signing
750    signing_n_out_of_n::<_, _, L, _, _>(
751        tracer,
752        rng,
753        party,
754        sid,
755        i,
756        t,
757        &x_i,
758        &X,
759        key_share.core.shared_public_key + Shift,
760        p_i,
761        q_i,
762        &N,
763        &R,
764        message_to_sign,
765        enforce_reliable_broadcast,
766    )
767    .await
768}
769
770/// Original CGGMP n-out-of-n signing
771///
772/// Implementation has very little differences compared to original CGGMP protocol: we added broadcast
773/// reliability check, fixed some typos in CGGMP, etc. Differences are covered in the specs.
774async fn signing_n_out_of_n<M, E, L, D, R>(
775    mut tracer: Option<&mut dyn Tracer>,
776    rng: &mut R,
777    party: M,
778    sid: ExecutionId<'_>,
779    i: PartyIndex,
780    n: u16,
781    x_i: &NonZero<SecretScalar<E>>,
782    X: &[NonZero<Point<E>>],
783    pk: Point<E>,
784    p_i: &Integer,
785    q_i: &Integer,
786    N: &[fast_paillier::EncryptionKey],
787    R: &[PedersenParams],
788    message_to_sign: Option<Scalar<E>>,
789    enforce_reliable_broadcast: bool,
790) -> Result<ProtocolOutput<E>, SigningError>
791where
792    M: Mpc<ProtocolMessage = Msg<E, D>>,
793    E: Curve,
794    L: SecurityLevel,
795    D: Digest + Clone + 'static,
796    R: RngCore + CryptoRng,
797    NonZero<Point<E>>: AlwaysHasAffineX<E>,
798{
799    let MpcParty {
800        delivery, runtime, ..
801    } = party.into_party();
802    let (incomings, mut outgoings) = delivery.split();
803
804    tracer.stage("Retrieve auxiliary data");
805    let R_i = &R[usize::from(i)];
806
807    let dec_i: fast_paillier::DecryptionKey =
808        fast_paillier::DecryptionKey::from_primes(p_i.clone(), q_i.clone())
809            .map_err(|_| Bug::InvalidOwnPaillierKey)?;
810
811    tracer.stage("Precompute execution id and security params");
812    let security_params = crate::utils::SecurityParams::new::<L>();
813
814    tracer.stage("Setup networking");
815    let mut rounds = RoundsRouter::<Msg<E, D>>::builder();
816    let round1a = rounds.add_round(RoundInput::<MsgRound1a<E>>::broadcast(i, n));
817    let round1b = rounds.add_round(RoundInput::<MsgRound1b<E>>::p2p(i, n));
818    let round1a_sync = rounds.add_round(RoundInput::<MsgReliabilityCheck<D>>::broadcast(i, n));
819    let round2 = rounds.add_round(RoundInput::<MsgRound2<E>>::p2p(i, n));
820    let round3 = rounds.add_round(RoundInput::<MsgRound3<E>>::broadcast(i, n));
821    let round4 = rounds.add_round(RoundInput::<MsgRound4<E>>::broadcast(i, n));
822    let mut rounds = rounds.listen(incomings);
823
824    // Round 1
825    tracer.round_begins();
826
827    tracer.stage("Generate local ephemeral secrets (k_i, y_i, p_i, v_i)");
828    let gamma_i = SecretScalar::<E>::random(rng);
829    let k_i = SecretScalar::<E>::random(rng);
830
831    let v_i = Integer::sample_in_mult_group_of(rng, dec_i.n());
832    let rho_i = Integer::sample_in_mult_group_of(rng, dec_i.n());
833
834    tracer.stage("Encrypt G_i and K_i");
835    let G_i = dec_i
836        .encrypt_with(&utils::scalar_to_pm_bignumber(&gamma_i), &v_i)
837        .map_err(|_| Bug::PaillierEnc(BugSource::G_i))?;
838    let K_i = dec_i
839        .encrypt_with(&utils::scalar_to_pm_bignumber(&k_i), &rho_i)
840        .map_err(|_| Bug::PaillierEnc(BugSource::K_i))?;
841
842    tracer.stage("Generate a_i, b_i, A_i1, A_i2, B_i1, B_i2");
843    let Y_i = {
844        let y_i = SecretScalar::<E>::random(rng);
845        Point::generator() * y_i
846    };
847    let a_i = SecretScalar::<E>::random(rng);
848    let b_i = SecretScalar::<E>::random(rng);
849
850    let A_i1 = &a_i * Point::generator();
851    let A_i2 = &a_i * Y_i + &k_i * Point::generator();
852
853    let B_i1 = &b_i * Point::generator();
854    let B_i2 = &b_i * Y_i + &gamma_i * Point::generator();
855
856    tracer.send_msg();
857    outgoings
858        .feed(Outgoing::broadcast(Msg::Round1a(MsgRound1a {
859            K: K_i.clone(),
860            G: G_i.clone(),
861            Y: Y_i,
862            A1: A_i1,
863            A2: A_i2,
864            B1: B_i1,
865            B2: B_i2,
866        })))
867        .await
868        .map_err(IoError::send_message)?;
869    tracer.msg_sent();
870
871    for j in utils::iter_peers(i, n) {
872        tracer.stage("Prove psi0_ji, psi1_ji");
873        let R_j = &R[usize::from(j)];
874
875        let psi0_ji = pi_enc_elg::non_interactive::prove::<E, D>(
876            &unambiguous::ProofEnc {
877                sid,
878                prover: i,
879                num: 0,
880            },
881            &R_j.into(),
882            pi_enc_elg::Data {
883                key: &dec_i,
884                ciphertext: &K_i,
885                a: &Y_i,
886                b: &A_i1,
887                x: &A_i2,
888            },
889            pi_enc_elg::PrivateData {
890                plaintext: &utils::scalar_to_pm_bignumber(&k_i),
891                nonce: &rho_i,
892                b: a_i.as_ref(),
893            },
894            &security_params.pi_enc_elg,
895            rng,
896        )
897        .map_err(|e| Bug::PiEncElg(BugSource::psi0, e))?;
898
899        let psi1_ji = pi_enc_elg::non_interactive::prove::<E, D>(
900            &unambiguous::ProofEnc {
901                sid,
902                prover: i,
903                num: 1,
904            },
905            &R_j.into(),
906            pi_enc_elg::Data {
907                key: &dec_i,
908                ciphertext: &G_i,
909                a: &Y_i,
910                b: &B_i1,
911                x: &B_i2,
912            },
913            pi_enc_elg::PrivateData {
914                plaintext: &utils::scalar_to_pm_bignumber(&gamma_i),
915                nonce: &v_i,
916                b: b_i.as_ref(),
917            },
918            &security_params.pi_enc_elg,
919            rng,
920        )
921        .map_err(|e| Bug::PiEncElg(BugSource::psi1, e))?;
922
923        tracer.send_msg();
924        outgoings
925            .feed(Outgoing::p2p(
926                j,
927                Msg::Round1b(MsgRound1b {
928                    psi0: psi0_ji,
929                    psi1: psi1_ji,
930                }),
931            ))
932            .await
933            .map_err(IoError::send_message)?;
934        tracer.msg_sent();
935    }
936    tracer.send_msg();
937    outgoings.flush().await.map_err(IoError::send_message)?;
938    tracer.msg_sent();
939
940    // Round 2
941    tracer.round_begins();
942
943    tracer.receive_msgs();
944    // Contains G_j, K_j sent by other parties
945    let ciphertexts = rounds
946        .complete(round1a)
947        .await
948        .map_err(IoError::receive_message)?;
949    let psi_proofs = rounds
950        .complete(round1b)
951        .await
952        .map_err(IoError::receive_message)?;
953    tracer.msgs_received();
954
955    // Reliability check (if enabled)
956    if enforce_reliable_broadcast {
957        tracer.stage("Hash received msgs (reliability check)");
958        let h_i = udigest::hash_iter::<D>(
959            ciphertexts
960                .iter_including_me(&MsgRound1a {
961                    K: K_i.clone(),
962                    G: G_i.clone(),
963                    Y: Y_i,
964                    A1: A_i1,
965                    A2: A_i2,
966                    B1: B_i1,
967                    B2: B_i2,
968                })
969                .map(|ciphertexts| unambiguous::Echo { sid, ciphertexts }),
970        );
971
972        tracer.send_msg();
973        outgoings
974            .send(Outgoing::broadcast(Msg::ReliabilityCheck(
975                MsgReliabilityCheck(h_i.clone()),
976            )))
977            .await
978            .map_err(IoError::send_message)?;
979        tracer.msg_sent();
980
981        tracer.round_begins();
982
983        tracer.receive_msgs();
984        let round1a_hashes = rounds
985            .complete(round1a_sync)
986            .await
987            .map_err(IoError::receive_message)?;
988        tracer.msgs_received();
989        tracer.stage("Assert other parties hashed messages (reliability check)");
990        let parties_have_different_hashes =
991            utils::collect_simple_blame(&round1a_hashes, |hash| hash.0 != h_i);
992        if !parties_have_different_hashes.is_empty() {
993            return Err(SigningAborted::Round1aNotReliable(parties_have_different_hashes).into());
994        }
995    }
996
997    // Step 1. Verify proofs
998    tracer.stage("Verify psi0, psi1 proofs");
999    let faulty_parties =
1000        utils::collect_blame_with_data(&ciphertexts, &psi_proofs, |j, ciphertexts, proof| {
1001            let N_j = &N[usize::from(j)];
1002            pi_enc_elg::non_interactive::verify::<E, D>(
1003                &unambiguous::ProofEnc {
1004                    sid,
1005                    prover: j,
1006                    num: 0,
1007                },
1008                &R_i.into(),
1009                pi_enc_elg::Data {
1010                    key: N_j,
1011                    ciphertext: &ciphertexts.K,
1012                    a: &ciphertexts.Y,
1013                    b: &ciphertexts.A1,
1014                    x: &ciphertexts.A2,
1015                },
1016                &proof.psi0,
1017                &security_params.pi_enc_elg,
1018            )
1019            .err()?;
1020            pi_enc_elg::non_interactive::verify::<E, D>(
1021                &unambiguous::ProofEnc {
1022                    sid,
1023                    prover: j,
1024                    num: 1,
1025                },
1026                &R_i.into(),
1027                pi_enc_elg::Data {
1028                    key: N_j,
1029                    ciphertext: &ciphertexts.G,
1030                    a: &ciphertexts.Y,
1031                    b: &ciphertexts.B1,
1032                    x: &ciphertexts.B2,
1033                },
1034                &proof.psi1,
1035                &security_params.pi_enc_elg,
1036            )
1037            .err()
1038        });
1039
1040    if !faulty_parties.is_empty() {
1041        return Err(SigningAborted::EncProofOfK(faulty_parties).into());
1042    }
1043    runtime.yield_now().await;
1044
1045    // Step 2
1046    let Gamma_i = Point::generator() * &gamma_i;
1047
1048    tracer.stage("Prove tilde_psi_i");
1049    let tilde_psi_i = pi_elog::non_interactive::prove::<E, D>(
1050        &unambiguous::ProofElog {
1051            sid,
1052            prover: i,
1053            prime: false,
1054        },
1055        pi_elog::Data {
1056            l: &B_i1,
1057            m: &B_i2,
1058            x: &Y_i,
1059            y: &Gamma_i,
1060            h: &Point::generator().to_point(),
1061        },
1062        pi_elog::PrivateData {
1063            y: gamma_i.as_ref(),
1064            lambda: b_i.as_ref(),
1065        },
1066        rng,
1067    )
1068    .map_err(|e| Bug::PiElog(BugSource::psi, e))?;
1069    runtime.yield_now().await;
1070
1071    let J = Integer::one() << L::ELL_PRIME;
1072
1073    let mut beta_sum = Scalar::zero();
1074    let mut hat_beta_sum = Scalar::zero();
1075    for (j, _, ciphertext_j) in ciphertexts.iter_indexed() {
1076        tracer.stage("Sample random r, hat_r, s, hat_s, beta, hat_beta");
1077        let R_j = &R[usize::from(j)];
1078        let enc_j = &N[usize::from(j)];
1079
1080        let r_ij = dec_i.n().random_below_ref(rng);
1081        let hat_r_ij = dec_i.n().random_below_ref(rng);
1082        let s_ij = enc_j.n().random_below_ref(rng);
1083        let hat_s_ij = enc_j.n().random_below_ref(rng);
1084
1085        let beta_ij = Integer::from_rng_half_pm(rng, &J);
1086        let hat_beta_ij = Integer::from_rng_half_pm(rng, &J);
1087
1088        beta_sum += beta_ij.to_scalar();
1089        hat_beta_sum += hat_beta_ij.to_scalar();
1090
1091        tracer.stage("Encrypt D_ji");
1092        // D_ji = (gamma_i * K_j) + enc_j(-beta_ij, s_ij)
1093        let D_ji = {
1094            let gamma_i_times_K_j = enc_j
1095                .omul(&utils::scalar_to_pm_bignumber(&gamma_i), &ciphertext_j.K)
1096                .map_err(|_| Bug::PaillierOp(BugSource::gamma_i_times_K_j))?;
1097            let neg_beta_ij_enc = enc_j
1098                .encrypt_with(&-&beta_ij, &s_ij)
1099                .map_err(|_| Bug::PaillierEnc(BugSource::neg_beta_ij_enc))?;
1100            enc_j
1101                .oadd(&gamma_i_times_K_j, &neg_beta_ij_enc)
1102                .map_err(|_| Bug::PaillierOp(BugSource::D_ji))?
1103        };
1104
1105        tracer.stage("Encrypt F_ji");
1106        let F_ji = dec_i
1107            .encrypt_with(&-&beta_ij, &r_ij)
1108            .map_err(|_| Bug::PaillierEnc(BugSource::F_ji))?;
1109
1110        tracer.stage("Encrypt hat_D_ji");
1111        // Dˆ_ji = (x_i * K_j) + enc_j(-hat_beta_ij, hat_s_ij)
1112        let hat_D_ji = {
1113            let x_i_times_K_j = enc_j
1114                .omul(&utils::scalar_to_pm_bignumber(x_i), &ciphertext_j.K)
1115                .map_err(|_| Bug::PaillierOp(BugSource::x_i_times_K_j))?;
1116            let neg_hat_beta_ij_enc = enc_j
1117                .encrypt_with(&-&hat_beta_ij, &hat_s_ij)
1118                .map_err(|_| Bug::PaillierEnc(BugSource::hat_beta_ij_enc))?;
1119            enc_j
1120                .oadd(&x_i_times_K_j, &neg_hat_beta_ij_enc)
1121                .map_err(|_| Bug::PaillierOp(BugSource::hat_D))?
1122        };
1123        runtime.yield_now().await;
1124
1125        tracer.stage("Encrypt hat_F_ji");
1126        let hat_F_ji = dec_i
1127            .encrypt_with(&-&hat_beta_ij, &hat_r_ij)
1128            .map_err(|_| Bug::PaillierEnc(BugSource::hat_F))?;
1129
1130        tracer.stage("Prove psi_ji");
1131        let psi_ji = pi_aff::non_interactive::prove::<E, D>(
1132            &unambiguous::ProofPsi {
1133                sid,
1134                prover: i,
1135                hat: false,
1136            },
1137            &R_j.into(),
1138            pi_aff::Data {
1139                key_j: enc_j,
1140                key_i: &dec_i,
1141                c: &ciphertext_j.K,
1142                d: &D_ji,
1143                y: &F_ji,
1144                x: &Gamma_i,
1145            },
1146            pi_aff::PrivateData {
1147                x: &utils::scalar_to_pm_bignumber(&gamma_i),
1148                y: &-&beta_ij,
1149                nonce: &s_ij,
1150                nonce_y: &r_ij,
1151            },
1152            &security_params.pi_aff,
1153            &mut *rng,
1154        )
1155        .map_err(|e| Bug::PiAffG(BugSource::psi, e))?;
1156        runtime.yield_now().await;
1157
1158        tracer.stage("Prove psiˆ_ji");
1159        let hat_psi_ji = pi_aff::non_interactive::prove::<E, D>(
1160            &unambiguous::ProofPsi {
1161                sid,
1162                prover: i,
1163                hat: true,
1164            },
1165            &R_j.into(),
1166            pi_aff::Data {
1167                key_j: enc_j,
1168                key_i: &dec_i,
1169                c: &ciphertext_j.K,
1170                d: &hat_D_ji,
1171                y: &hat_F_ji,
1172                x: &(Point::generator() * x_i),
1173            },
1174            pi_aff::PrivateData {
1175                x: &utils::scalar_to_pm_bignumber(x_i),
1176                y: &-&hat_beta_ij,
1177                nonce: &hat_s_ij,
1178                nonce_y: &hat_r_ij,
1179            },
1180            &security_params.pi_aff,
1181            &mut *rng,
1182        )
1183        .map_err(|e| Bug::PiAffG(BugSource::hat_psi, e))?;
1184        runtime.yield_now().await;
1185
1186        tracer.send_msg();
1187        outgoings
1188            .feed(Outgoing::p2p(
1189                j,
1190                Msg::Round2(MsgRound2 {
1191                    Gamma: Gamma_i,
1192                    D: D_ji,
1193                    F: F_ji,
1194                    hat_D: hat_D_ji,
1195                    hat_F: hat_F_ji,
1196                    tilde_psi: tilde_psi_i.clone(),
1197                    psi_j: psi_ji,
1198                    hat_psi_j: hat_psi_ji,
1199                }),
1200            ))
1201            .await
1202            .map_err(IoError::send_message)?;
1203        tracer.msg_sent();
1204    }
1205    tracer.send_msg();
1206    outgoings.flush().await.map_err(IoError::send_message)?;
1207    tracer.msg_sent();
1208
1209    // Round 3
1210    tracer.round_begins();
1211
1212    // Step 1
1213    tracer.receive_msgs();
1214    let round2_msgs = rounds
1215        .complete(round2)
1216        .await
1217        .map_err(IoError::receive_message)?;
1218    tracer.msgs_received();
1219
1220    let faulty_parties =
1221        utils::collect_blame_with_data(&round2_msgs, &ciphertexts, |j, msg, ciphertexts| {
1222            tracer.stage("Retrieve auxiliary data");
1223            let X_j = X[usize::from(j)];
1224            let enc_j = &N[usize::from(j)];
1225
1226            tracer.stage("Validate tilde_psi_j");
1227            let tilde_psi_invalid = pi_elog::non_interactive::verify::<E, D>(
1228                &unambiguous::ProofElog {
1229                    sid,
1230                    prover: j,
1231                    prime: false,
1232                },
1233                pi_elog::Data {
1234                    l: &ciphertexts.B1,
1235                    m: &ciphertexts.B2,
1236                    x: &ciphertexts.Y,
1237                    y: &msg.Gamma,
1238                    h: &Point::generator().to_point(),
1239                },
1240                &msg.tilde_psi,
1241            )
1242            .err();
1243
1244            tracer.stage("Validate psi_i,j");
1245            let psi_j_invalid = pi_aff::non_interactive::verify::<E, D>(
1246                &unambiguous::ProofPsi {
1247                    sid,
1248                    prover: j,
1249                    hat: false,
1250                },
1251                &R_i.into(),
1252                pi_aff::Data {
1253                    key_j: &dec_i,
1254                    key_i: enc_j,
1255                    c: &K_i,
1256                    d: &msg.D,
1257                    y: &msg.F,
1258                    x: &msg.Gamma,
1259                },
1260                &security_params.pi_aff,
1261                &msg.psi_j,
1262            )
1263            .err();
1264
1265            tracer.stage("Validate hat_psi_i,j");
1266            let hat_psi_j_invalid = pi_aff::non_interactive::verify::<E, D>(
1267                &unambiguous::ProofPsi {
1268                    sid,
1269                    prover: j,
1270                    hat: true,
1271                },
1272                &R_i.into(),
1273                pi_aff::Data {
1274                    key_j: &dec_i,
1275                    key_i: enc_j,
1276                    c: &K_i,
1277                    d: &msg.hat_D,
1278                    y: &msg.hat_F,
1279                    x: &X_j,
1280                },
1281                &security_params.pi_aff,
1282                &msg.hat_psi_j,
1283            )
1284            .err();
1285
1286            if tilde_psi_invalid.is_some() || psi_j_invalid.is_some() || hat_psi_j_invalid.is_some()
1287            {
1288                Some((tilde_psi_invalid, psi_j_invalid, hat_psi_j_invalid))
1289            } else {
1290                None
1291            }
1292        });
1293    if !faulty_parties.is_empty() {
1294        return Err(SigningAborted::InvalidPsi(faulty_parties).into());
1295    }
1296    runtime.yield_now().await;
1297
1298    // Step 2
1299    tracer.stage("Compute Gamma, Delta_i, delta_i, chi_i");
1300    let Gamma = Gamma_i + round2_msgs.iter().map(|msg| msg.Gamma).sum::<Point<E>>();
1301    let Delta_i = Gamma * &k_i;
1302
1303    let alpha_sum =
1304        round2_msgs
1305            .iter()
1306            .map(|msg| &msg.D)
1307            .try_fold(Scalar::<E>::zero(), |sum, D_ij| {
1308                let alpha_ij = dec_i
1309                    .decrypt(D_ij)
1310                    .map_err(|_| Bug::PaillierDec(BugSource::alpha))?;
1311                Ok::<_, Bug>(sum + alpha_ij.to_scalar())
1312            })?;
1313    let hat_alpha_sum =
1314        round2_msgs
1315            .iter()
1316            .map(|msg| &msg.hat_D)
1317            .try_fold(Scalar::zero(), |sum, hat_D_ij| {
1318                let hat_alpha_ij = dec_i
1319                    .decrypt(hat_D_ij)
1320                    .map_err(|_| Bug::PaillierDec(BugSource::hat_alpha))?;
1321                Ok::<_, Bug>(sum + hat_alpha_ij.to_scalar())
1322            })?;
1323
1324    let delta_i = gamma_i.as_ref() * k_i.as_ref() + alpha_sum + beta_sum;
1325    let chi_i = x_i * k_i.as_ref() + hat_alpha_sum + hat_beta_sum;
1326    let S_i = chi_i * Gamma;
1327    runtime.yield_now().await;
1328
1329    tracer.stage("Prove psi_prime");
1330    let psi_prime_i = pi_elog::non_interactive::prove::<E, D>(
1331        &unambiguous::ProofElog {
1332            sid,
1333            prover: i,
1334            prime: true,
1335        },
1336        pi_elog::Data {
1337            l: &A_i1,
1338            m: &A_i2,
1339            x: &Y_i,
1340            y: &Delta_i,
1341            h: &Gamma,
1342        },
1343        pi_elog::PrivateData {
1344            y: k_i.as_ref(),
1345            lambda: a_i.as_ref(),
1346        },
1347        rng,
1348    )
1349    .map_err(|e| Bug::PiLog(BugSource::psi_prime, e))?;
1350
1351    tracer.send_msg();
1352    let my_round3_msg = MsgRound3 {
1353        delta: delta_i,
1354        S: S_i,
1355        Delta: Delta_i,
1356        psi_prime: psi_prime_i,
1357    };
1358    outgoings
1359        .send(Outgoing::broadcast(Msg::Round3(my_round3_msg.clone())))
1360        .await
1361        .map_err(IoError::send_message)?;
1362    tracer.msg_sent();
1363
1364    // Output
1365    tracer.named_round_begins("Presig output");
1366
1367    // Step 1
1368    tracer.receive_msgs();
1369    let round3_msgs = rounds
1370        .complete(round3)
1371        .await
1372        .map_err(IoError::receive_message)?;
1373    tracer.msgs_received();
1374
1375    tracer.stage("Validate psi_prime_prime");
1376    let faulty_parties =
1377        utils::collect_blame_with_data(&round3_msgs, &ciphertexts, |j, msg_j, ciphertext_j| {
1378            pi_elog::non_interactive::verify::<E, D>(
1379                &unambiguous::ProofElog {
1380                    sid,
1381                    prover: j,
1382                    prime: true,
1383                },
1384                pi_elog::Data {
1385                    l: &ciphertext_j.A1,
1386                    m: &ciphertext_j.A2,
1387                    x: &ciphertext_j.Y,
1388                    y: &msg_j.Delta,
1389                    h: &Gamma,
1390                },
1391                &msg_j.psi_prime,
1392            )
1393            .err()
1394        });
1395    if !faulty_parties.is_empty() {
1396        return Err(SigningAborted::InvalidPsiPrimePrime(faulty_parties).into());
1397    }
1398    runtime.yield_now().await;
1399
1400    // Step 2
1401    tracer.stage("Calculate presignature");
1402    let delta = delta_i + round3_msgs.iter().map(|m| m.delta).sum::<Scalar<E>>();
1403    let Delta = Delta_i + round3_msgs.iter().map(|m| m.Delta).sum::<Point<E>>();
1404    let S = S_i + round3_msgs.iter().map(|m| m.S).sum::<Point<E>>();
1405
1406    if Point::generator() * delta != Delta || pk * delta != S {
1407        // Following the protocol, party should broadcast additional proofs
1408        // to convince others it didn't cheat. However, since identifiable
1409        // abort is not implemented yet, this part of the protocol is missing
1410        return Err(SigningAborted::MismatchedDelta.into());
1411    }
1412
1413    let delta_inv = delta.invert().ok_or(Bug::Zero(BugSource::delta))?;
1414
1415    let tilde_k_i = SecretScalar::new(&mut (k_i * delta_inv));
1416    let tilde_chi_i = SecretScalar::new(&mut (chi_i * delta_inv));
1417
1418    let Gamma = NonZero::from_point(Gamma).ok_or(Bug::Zero(BugSource::Gamma))?;
1419
1420    let commitments = round3_msgs
1421        .iter_including_me(&my_round3_msg)
1422        .map(|m| PresignatureCommitment {
1423            tilde_Delta: delta_inv * m.Delta,
1424            tilde_S: delta_inv * m.S,
1425        })
1426        .collect::<Vec<_>>();
1427    let commitments = PresignaturePublicData { Gamma, commitments };
1428
1429    let presig = Presignature {
1430        Gamma,
1431        tilde_k: tilde_k_i,
1432        tilde_chi: tilde_chi_i,
1433    };
1434
1435    // If message is not specified, protocol terminates here and outputs partial
1436    // signature
1437    let Some(message_to_sign) = message_to_sign else {
1438        tracer.protocol_ends();
1439        return Ok(ProtocolOutput::Presignature((presig, commitments)));
1440    };
1441
1442    // Signing
1443    tracer.named_round_begins("Partial signing");
1444
1445    // Round 1
1446
1447    // SECURITY: we can safely sign a digest even if its preimage is unknown, because the attack
1448    // described in [`PrehashedDataToSign`] can only be done if message to be signed is chosen
1449    // after presignature is generated. Since we do a full signing, we know that message was
1450    // chosen before presig was generated.
1451    let message_to_sign = DataToSign(message_to_sign);
1452
1453    let partial_sig = presig.issue_partial_signature(message_to_sign);
1454
1455    tracer.send_msg();
1456    outgoings
1457        .send(Outgoing::broadcast(Msg::Round4(MsgRound4 { partial_sig })))
1458        .await
1459        .map_err(IoError::send_message)?;
1460    tracer.msg_sent();
1461
1462    // Output
1463    tracer.named_round_begins("Signature reconstruction");
1464
1465    tracer.receive_msgs();
1466    let partial_sigs = rounds
1467        .complete(round4)
1468        .await
1469        .map_err(IoError::receive_message)?;
1470    tracer.msgs_received();
1471
1472    let partial_sigs = partial_sigs
1473        .into_vec_including_me(MsgRound4 { partial_sig })
1474        .into_iter()
1475        .map(|m| m.partial_sig)
1476        .collect::<Vec<_>>();
1477
1478    // Following the protocol, party should broadcast additional proofs
1479    // to convince others it didn't cheat. However, since identifiable
1480    // abort is not implemented yet, this part of the protocol is missing
1481    let sig = PartialSignature::combine(&partial_sigs, &commitments, message_to_sign)
1482        .ok_or(SigningAborted::SignatureInvalid)?;
1483
1484    tracer.protocol_ends();
1485    Ok(ProtocolOutput::Signature(sig))
1486}
1487
1488impl<E> Presignature<E>
1489where
1490    E: Curve,
1491    NonZero<Point<E>>: AlwaysHasAffineX<E>,
1492{
1493    /// Issues partial signature for given message
1494    ///
1495    /// **Never reuse presignatures!** If you use the same presignatures to sign two different
1496    /// messages, it leaks the private key!
1497    ///
1498    /// When signing using a presignature, it's important that you know a message that's being
1499    /// signed (not just its hash!). For this reason, this function only accepts [`DataToSign`] that
1500    /// can only be constructed when original message is known. Refer to [`PrehashedDataToSign`]
1501    /// docs to learn more about possible attack when signing a hash of the message without knowing
1502    /// the preimage.
1503    pub fn issue_partial_signature(self, message_to_sign: DataToSign<E>) -> PartialSignature<E> {
1504        let r = self.Gamma.x().to_scalar();
1505        let m = message_to_sign.to_scalar();
1506        let sigma_i = self.tilde_k.as_ref() * m + r * self.tilde_chi.as_ref();
1507        PartialSignature { sigma: sigma_i }
1508    }
1509}
1510
1511#[cfg(feature = "hd-wallet")]
1512fn derive_additive_shift<E: Curve, Hd: hd_wallet::HdWallet<E>, Index>(
1513    mut epub: hd_wallet::ExtendedPublicKey<E>,
1514    path: impl IntoIterator<Item = Index>,
1515) -> Result<Scalar<E>, <Index as TryInto<hd_wallet::NonHardenedIndex>>::Error>
1516where
1517    hd_wallet::NonHardenedIndex: TryFrom<Index>,
1518{
1519    let mut additive_shift = Scalar::<E>::zero();
1520
1521    for child_index in path {
1522        let child_index: hd_wallet::NonHardenedIndex = child_index.try_into()?;
1523        let shift = Hd::derive_public_shift(&epub, child_index);
1524
1525        additive_shift += shift.shift;
1526        epub = shift.child_public_key;
1527    }
1528
1529    Ok(additive_shift)
1530}
1531
1532impl<E: Curve> PartialSignature<E>
1533where
1534    NonZero<Point<E>>: AlwaysHasAffineX<E>,
1535{
1536    /// Combines threshold amount of partial signatures into regular signature
1537    ///
1538    /// Returns `None` if input is malformed.
1539    ///
1540    /// `combine` may return a signature that's invalid for public key and message it was issued for.
1541    /// This would mean that some of signers cheated and aborted the protocol. You need to validate
1542    /// resulting signature to be sure that no one aborted the protocol.
1543    pub fn combine(
1544        partial_signatures: &[PartialSignature<E>],
1545        commitments: &PresignaturePublicData<E>,
1546        m: DataToSign<E>,
1547    ) -> Option<Signature<E>> {
1548        if partial_signatures.is_empty()
1549            || partial_signatures.len() != commitments.commitments.len()
1550        {
1551            None
1552        } else {
1553            let r = commitments.Gamma.x().to_scalar();
1554            let r = NonZero::from_scalar(r)?;
1555            let m = m.to_scalar();
1556            for (partial_sig, commitment) in partial_signatures.iter().zip(&commitments.commitments)
1557            {
1558                if partial_sig.sigma * commitments.Gamma
1559                    != m * commitment.tilde_Delta + r * commitment.tilde_S
1560                {
1561                    return None;
1562                }
1563            }
1564            let sigma = partial_signatures.iter().map(|s| &s.sigma).sum();
1565            let sigma = NonZero::from_scalar(sigma)?;
1566            Some(Signature { r, s: sigma }.normalize_s())
1567        }
1568    }
1569}
1570
1571impl<E: Curve> Signature<E>
1572where
1573    NonZero<Point<E>>: AlwaysHasAffineX<E>,
1574{
1575    /// Verifies that signature matches specified public key and message
1576    pub fn verify(
1577        &self,
1578        public_key: &Point<E>,
1579        message: &dyn AnyDataToSign<E>,
1580    ) -> Result<(), InvalidSignature> {
1581        let r = (Point::generator() * message.to_scalar() + public_key * self.r) * self.s.invert();
1582        let r = NonZero::from_point(r).ok_or(InvalidSignature)?;
1583
1584        if *self.r == r.x().to_scalar() {
1585            Ok(())
1586        } else {
1587            Err(InvalidSignature)
1588        }
1589    }
1590}
1591
1592impl<E: Curve> Signature<E> {
1593    /// Create signature struct from `r` and `s` values
1594    pub fn from_raw_parts(r: NonZero<Scalar<E>>, s: NonZero<Scalar<E>>) -> Self {
1595        Self { r, s }
1596    }
1597    /// Normilizes the signature
1598    ///
1599    /// Given that $(r, s)$ is valid signature, $(r, -s)$ is also a valid signature. Some applications (like Bitcoin)
1600    /// remove this ambiguity by restricting $s$ to be in lower half. This method normailizes the signature by picking
1601    /// $s$ that is in lower half.
1602    ///
1603    /// Note that signing protocol implemented within this crate ouputs normalized signature by default.
1604    pub fn normalize_s(self) -> Self {
1605        let neg_s = -self.s;
1606        if neg_s < self.s {
1607            Signature { s: neg_s, ..self }
1608        } else {
1609            self
1610        }
1611    }
1612
1613    /// Writes serialized signature to the bytes buffer
1614    ///
1615    /// Bytes buffer size must be at least [`Signature::serialized_len()`], otherwise content
1616    /// of output buffer is unspecified.
1617    pub fn write_to_slice(&self, out: &mut [u8]) {
1618        if out.len() < Self::serialized_len() {
1619            return;
1620        }
1621        let scalar_size = Scalar::<E>::serialized_len();
1622        out[0..scalar_size].copy_from_slice(&self.r.to_be_bytes());
1623        out[scalar_size..2 * scalar_size].copy_from_slice(&self.s.to_be_bytes());
1624    }
1625
1626    /// Reads serialized signature from the bytes buffer.
1627    ///
1628    /// Bytes buffer size must be equal to [`Signature::serialized_len()`] and
1629    /// none of the signature parts should be 0. If this doesn't hold, returns
1630    /// `None`
1631    pub fn read_from_slice(inp: &[u8]) -> Option<Self> {
1632        if inp.len() != Self::serialized_len() {
1633            return None;
1634        }
1635        let r_bytes = &inp[0..inp.len() / 2];
1636        let s_bytes = &inp[inp.len() / 2..];
1637        let r = generic_ec::Scalar::from_be_bytes(r_bytes)
1638            .ok()?
1639            .try_into()
1640            .ok()?;
1641        let s = generic_ec::Scalar::from_be_bytes(s_bytes)
1642            .ok()?
1643            .try_into()
1644            .ok()?;
1645        Some(Self::from_raw_parts(r, s))
1646    }
1647
1648    /// Returns size of bytes buffer that can fit serialized signature
1649    pub fn serialized_len() -> usize {
1650        2 * Scalar::<E>::serialized_len()
1651    }
1652}
1653
1654enum ProtocolOutput<E: Curve> {
1655    Presignature((Presignature<E>, PresignaturePublicData<E>)),
1656    Signature(Signature<E>),
1657}
1658
1659/// Error indicating that signing protocol failed
1660#[derive(Debug, Error)]
1661#[error("signing protocol failed")]
1662pub struct SigningError(#[source] Reason);
1663
1664crate::errors::impl_from! {
1665    impl From for SigningError {
1666        err: InvalidArgs => SigningError(Reason::InvalidArgs(err)),
1667        err: InvalidKeyShare => SigningError(Reason::InvalidKeyShare(err)),
1668        err: SigningAborted => SigningError(Reason::Aborted(err)),
1669        err: IoError => SigningError(Reason::IoError(err)),
1670        err: Bug => SigningError(Reason::Bug(err)),
1671    }
1672}
1673
1674/// Error indicating that signing failed
1675#[derive(Debug, Error)]
1676enum Reason {
1677    #[error("invalid arguments")]
1678    InvalidArgs(
1679        #[from]
1680        #[source]
1681        InvalidArgs,
1682    ),
1683    #[error("provided key share is not valid")]
1684    InvalidKeyShare(
1685        #[from]
1686        #[source]
1687        InvalidKeyShare,
1688    ),
1689    /// Signing protocol was maliciously aborted by another party
1690    #[error("protocol was maliciously aborted by another party")]
1691    Aborted(
1692        #[source]
1693        #[from]
1694        SigningAborted,
1695    ),
1696    #[error("i/o error")]
1697    IoError(#[source] IoError),
1698    /// Bug occurred
1699    #[error("bug occurred")]
1700    Bug(Bug),
1701}
1702
1703/// Error indicating that protocol was aborted by malicious party
1704///
1705/// It _can be_ cryptographically proven, but we do not support it yet.
1706#[allow(clippy::type_complexity)]
1707#[derive(Debug, Error)]
1708enum SigningAborted {
1709    #[error("pi_enc::verify(K) failed")]
1710    EncProofOfK(Vec<(utils::AbortBlame, paillier_zk::InvalidProof)>),
1711    #[error("ψ_j, ψ_j,i or ψˆ_ji proofs are invalid: {0:?}")]
1712    InvalidPsi(
1713        Vec<(
1714            utils::AbortBlame,
1715            (
1716                Option<paillier_zk::InvalidProof>,
1717                Option<paillier_zk::InvalidProof>,
1718                Option<paillier_zk::InvalidProof>,
1719            ),
1720        )>,
1721    ),
1722    #[error("ψ'' proof is invalid")]
1723    InvalidPsiPrimePrime(Vec<(utils::AbortBlame, paillier_zk::InvalidProof)>),
1724    #[error("Delta != G * delta")]
1725    MismatchedDelta,
1726    #[error("resulting signature is not valid")]
1727    SignatureInvalid,
1728    #[error("other parties received different broadcast messages at round1a")]
1729    Round1aNotReliable(Vec<utils::AbortBlame>),
1730}
1731
1732#[derive(Debug, Error)]
1733enum InvalidArgs {
1734    #[error("exactly `threshold` amount of parties should take part in signing")]
1735    MismatchedAmountOfParties,
1736    #[error("signer index `i` is out of bounds (must be < n)")]
1737    SignerIndexOutOfBounds,
1738    #[error("party index in S is out of bounds (must be < n)")]
1739    InvalidS,
1740}
1741
1742#[derive(Debug, Error)]
1743enum Bug {
1744    #[error("own paillier decryption key is not valid")]
1745    InvalidOwnPaillierKey,
1746    #[error("invalid key share: number of parties exceeds u16")]
1747    PartiesNumberExceedsU16,
1748    #[error("couldn't encrypt a scalar with paillier encryption key: {0:?}")]
1749    PaillierEnc(BugSource),
1750    #[error("paillier addition/multiplication failed: {0:?}")]
1751    PaillierOp(BugSource),
1752    #[error("π enc elg failed to prove statement {0:?}")]
1753    PiEncElg(BugSource, paillier_zk::Error),
1754    #[error("π elog failed to prove statement {0:?}")]
1755    PiElog(BugSource, #[source] paillier_zk::Error),
1756    #[error("π aff-g failed to prove statement {0:?}")]
1757    PiAffG(BugSource, #[source] paillier_zk::Error),
1758    #[error("π log* failed to prove statement: {0:?}")]
1759    PiLog(BugSource, #[source] paillier_zk::Error),
1760    #[error("couldn't decrypt a message: {0:?}")]
1761    PaillierDec(BugSource),
1762    #[error("{0:?} is zero")]
1763    Zero(BugSource),
1764    #[error("unexpected protocol output")]
1765    UnexpectedProtocolOutput,
1766    #[error("derive lagrange coef")]
1767    LagrangeCoef,
1768    #[error("subset function returned error")]
1769    Subset,
1770    #[error("derived child key is zero - probability of that is negligible")]
1771    DerivedChildKeyZero,
1772    #[error("derived child share is zero - probability of that is negligible")]
1773    DerivedChildShareZero,
1774}
1775
1776#[derive(Debug)]
1777#[allow(non_camel_case_types)]
1778enum BugSource {
1779    G_i,
1780    K_i,
1781    gamma_i_times_K_j,
1782    neg_beta_ij_enc,
1783    D_ji,
1784    F_ji,
1785    x_i_times_K_j,
1786    hat_beta_ij_enc,
1787    hat_D,
1788    hat_F,
1789    psi0,
1790    psi1,
1791    psi,
1792    hat_psi,
1793    alpha,
1794    hat_alpha,
1795    psi_prime,
1796
1797    delta,
1798    Gamma,
1799}
1800
1801/// Error indicating that signature is not valid for given public key and message
1802#[derive(Debug, Error)]
1803#[error("signature is not valid")]
1804pub struct InvalidSignature;
1805
1806#[cfg(test)]
1807mod test {
1808    fn read_write_signature<E: generic_ec::Curve>() {
1809        let mut rng = rand_dev::DevRng::new();
1810        for _ in 0..10 {
1811            let r = generic_ec::NonZero::<generic_ec::Scalar<E>>::random(&mut rng);
1812            let s = generic_ec::NonZero::<generic_ec::Scalar<E>>::random(&mut rng);
1813            let signature = super::Signature::from_raw_parts(r, s);
1814            let mut bytes = vec![0; super::Signature::<E>::serialized_len()];
1815            signature.write_to_slice(&mut bytes);
1816            let signature2 = super::Signature::read_from_slice(&bytes).unwrap();
1817            assert!(signature == signature2, "signatures equal");
1818        }
1819    }
1820
1821    #[test]
1822    fn read_write_signature_secp256k1() {
1823        read_write_signature::<crate::supported_curves::Secp256k1>()
1824    }
1825    #[test]
1826    fn read_write_signature_secp256r1() {
1827        read_write_signature::<crate::supported_curves::Secp256r1>()
1828    }
1829    #[test]
1830    fn read_write_signature_stark() {
1831        read_write_signature::<crate::supported_curves::Stark>()
1832    }
1833}