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}