dkls23/sign/
mod.rs

1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4//! Distributed Signature Generation (DSG) Protocol Implementation
5//!
6//! This module implements a distributed signature generation protocol that allows
7//! multiple parties to collaboratively generate digital signatures without any
8//! single party knowing the complete private key.
9//!
10//! # Protocol Overview
11//!
12//! The DSG protocol consists of two main phases:
13//! 1. Pre-signing phase: Generates a pre-signature that can be completed later
14//! 2. Finish phase: Completes the signature using the pre-signature
15//!
16//! # Security Properties
17//!
18//! The protocol provides the following security guarantees:
19//! - Threshold security: Signatures can only be generated with a sufficient number of parties
20//! - Privacy: No information about the private key is leaked
21//! - Verifiability: Signatures can be verified using standard ECDSA verification
22
23mod constants;
24mod dsg;
25mod messages;
26mod types;
27
28pub use dsg::*;
29pub use types::*;
30
31pub use messages::PreSign;
32
33pub use k256::ecdsa::{RecoveryId, Signature, VerifyingKey};
34
35use crate::setup::{ProtocolParticipant, ABORT_MESSAGE_TAG};
36use sl_mpc_mate::message::MsgId;
37
38/// Variants of the Distributed Signature Generation protocol
39///
40/// This enum represents the different modes in which the DSG protocol can be run:
41/// - Full protocol execution
42/// - Pre-signing only
43/// - Finish phase only
44pub enum DsgVariant {
45    /// Execute both PreSign and Finish phases
46    Full,
47    /// Execute only the PreSign phase
48    PreSign,
49    /// Execute only the Finish phase
50    Finish,
51}
52
53/// Generates a map of message receivers for the DSG protocol
54///
55/// This function helps set up the message routing for the DSG protocol by
56/// determining which messages should be sent to which participants.
57///
58/// # Type Parameters
59///
60/// * `S` - A type implementing the `ProtocolParticipant` trait
61/// * `F` - A closure type for handling message receivers
62///
63/// # Arguments
64///
65/// * `setup` - The protocol setup configuration
66/// * `variant` - The variant of the DSG protocol to run
67/// * `msg_receiver` - A closure that will be called for each (message_id, verifier) pair
68pub fn message_receivers<S, F>(
69    setup: &S,
70    variant: DsgVariant,
71    mut msg_receiver: F,
72) where
73    S: ProtocolParticipant,
74    F: FnMut(MsgId, &S::MessageVerifier),
75{
76    setup.all_other_parties().for_each(|p| {
77        let vk = setup.verifier(p);
78
79        msg_receiver(setup.msg_id(None, ABORT_MESSAGE_TAG), vk);
80
81        if matches!(variant, DsgVariant::Full | DsgVariant::PreSign) {
82            msg_receiver(setup.msg_id(None, constants::DSG_MSG_R1), vk);
83            msg_receiver(setup.msg_id(Some(p), constants::DSG_MSG_R2), vk);
84            msg_receiver(setup.msg_id(Some(p), constants::DSG_MSG_R3), vk);
85        }
86
87        if matches!(variant, DsgVariant::Finish | DsgVariant::Full) {
88            msg_receiver(setup.msg_id(None, constants::DSG_MSG_R4), vk);
89        }
90    })
91}
92
93#[cfg(any(test, feature = "test-support"))]
94pub use support::*;
95
96#[cfg(any(test, feature = "test-support"))]
97mod support {
98    use std::{str::FromStr, sync::Arc, time::Duration};
99
100    use derivation_path::DerivationPath;
101    use rand::prelude::*;
102    use sha2::{Digest, Sha256};
103
104    use sl_mpc_mate::message::*;
105
106    use crate::{
107        keygen::Keyshare,
108        setup::ProtocolParticipant,
109        setup::{
110            finish::SetupMessage as FinishSetupMsg, sign::SetupMessage,
111            NoSigningKey, NoVerifyingKey,
112        },
113        sign::PreSign,
114        Seed,
115    };
116
117    /// Sets up the DSG protocol for testing
118    ///
119    /// This function creates the necessary setup messages and seeds for testing
120    /// the DSG protocol with a given set of key shares.
121    ///
122    /// # Arguments
123    ///
124    /// * `instance` - Optional instance identifier
125    /// * `shares` - Vector of key shares for the participants
126    /// * `chain_path` - The derivation path for the key
127    ///
128    /// # Returns
129    ///
130    /// A vector of tuples containing:
131    /// * The setup message for each participant
132    /// * The random seed for each participant
133    ///
134    /// # Panics
135    ///
136    /// This function will panic if:
137    /// * The number of shares is less than the threshold
138    /// * The first share does not have rank 0
139    pub fn setup_dsg(
140        instance: Option<[u8; 32]>,
141        shares: &[Arc<Keyshare>],
142        chain_path: &str,
143    ) -> Vec<(SetupMessage, Seed)> {
144        let instance = instance.unwrap_or_else(rand::random);
145
146        let chain_path = DerivationPath::from_str(chain_path).unwrap();
147
148        let t = shares[0].threshold as usize;
149        assert!(shares.len() >= t);
150
151        // make sure that first share has rank 0
152        assert_eq!(shares[0].get_rank(0), 0);
153
154        let party_vk: Vec<NoVerifyingKey> = shares
155            .iter()
156            .map(|share| NoVerifyingKey::new(share.party_id as _))
157            .collect();
158
159        shares
160            .iter()
161            .enumerate()
162            .map(|(party_idx, share)| {
163                SetupMessage::new(
164                    InstanceId::new(instance),
165                    NoSigningKey,
166                    party_idx,
167                    party_vk.clone(),
168                    share.clone(),
169                )
170                .with_chain_path(chain_path.clone())
171                .with_hash([1; 32])
172                .with_ttl(Duration::from_secs(1000))
173            })
174            .map(|setup| {
175                let mixin = [setup.participant_index() as u8 + 1];
176
177                (
178                    setup,
179                    Sha256::new()
180                        .chain_update(instance)
181                        .chain_update(b"dsg-party-seed")
182                        .chain_update(mixin)
183                        .finalize()
184                        .into(),
185                )
186            })
187            .collect::<Vec<_>>()
188    }
189
190    /// Sets up the finish phase of the DSG protocol
191    ///
192    /// This function creates the necessary setup messages for completing
193    /// the signature generation using pre-signatures.
194    ///
195    /// # Arguments
196    ///
197    /// * `pre_signs` - Vector of pre-signatures from the participants
198    ///
199    /// # Returns
200    ///
201    /// A vector of setup messages for the finish phase
202    pub fn setup_finish_sign(pre_signs: Vec<PreSign>) -> Vec<FinishSetupMsg> {
203        let mut rng = rand::thread_rng();
204
205        let instance = InstanceId::from(rng.gen::<[u8; 32]>());
206
207        let party_vk: Vec<NoVerifyingKey> = pre_signs
208            .iter()
209            .map(|pre| NoVerifyingKey::new(pre.party_id as _))
210            .collect();
211
212        pre_signs
213            .into_iter()
214            .enumerate()
215            .map(|(party_idx, pre)| {
216                crate::setup::finish::SetupMessage::new(
217                    instance,
218                    party_idx,
219                    NoSigningKey,
220                    party_vk.clone(),
221                    pre,
222                )
223            })
224            .collect()
225    }
226}