cggmp21_keygen/
lib.rs

1//! Threshold and non-threshold CGGMP21 DKG
2//!
3//! This crate provides an implementation of UC-secure DKG protocol taken from [CGGMP21] paper. Implementation is
4//! fully `#![no_std]` compatible and WASM-friendly.
5//!
6//! [CGGMP21]: https://ia.cr/2021/060
7
8#![allow(non_snake_case, clippy::too_many_arguments)]
9#![forbid(missing_docs)]
10#![no_std]
11
12extern crate alloc;
13#[cfg(feature = "std")]
14extern crate std;
15
16pub mod progress;
17pub mod security_level;
18
19/// Non-threshold DKG specific types
20mod non_threshold;
21/// Threshold DKG specific types
22mod threshold;
23
24mod errors;
25mod execution_id;
26mod utils;
27
28use alloc::vec::Vec;
29
30use digest::Digest;
31use generic_ec::Curve;
32use rand_core::{CryptoRng, RngCore};
33use round_based::{Mpc, MsgId, PartyIndex};
34
35#[doc(inline)]
36pub use key_share;
37
38use crate::progress::Tracer;
39use crate::{
40    errors::IoError,
41    key_share::{CoreKeyShare, InvalidCoreShare},
42    security_level::SecurityLevel,
43};
44
45pub use self::execution_id::ExecutionId;
46#[doc(no_inline)]
47pub use self::msg::{non_threshold::Msg as NonThresholdMsg, threshold::Msg as ThresholdMsg};
48
49/// Defines default choice for digest and security level used across the crate
50mod default_choice {
51    pub type Digest = sha2::Sha256;
52    pub type SecurityLevel = crate::security_level::SecurityLevel128;
53}
54
55#[doc = include_str!("../docs/mpc_message.md")]
56pub mod msg {
57    /// Messages types related to non threshold DKG protocol
58    pub mod non_threshold {
59        pub use crate::non_threshold::{Msg, MsgReliabilityCheck, MsgRound1, MsgRound2, MsgRound3};
60    }
61    /// Messages types related to threshold DKG protocol
62    pub mod threshold {
63        pub use crate::threshold::{
64            Msg, MsgReliabilityCheck, MsgRound1, MsgRound2Broad, MsgRound2Uni, MsgRound3,
65        };
66    }
67}
68
69/// Key generation entry point. You can call [`set_threshold`] to make it into a
70/// threshold DKG
71///
72/// [`set_threshold`]: GenericKeygenBuilder::set_threshold
73pub type KeygenBuilder<
74    'a,
75    E,
76    L = crate::default_choice::SecurityLevel,
77    D = crate::default_choice::Digest,
78> = GenericKeygenBuilder<'a, E, NonThreshold, L, D>;
79
80/// Threshold keygen builder
81pub type ThresholdKeygenBuilder<
82    'a,
83    E,
84    L = crate::default_choice::SecurityLevel,
85    D = crate::default_choice::Digest,
86> = GenericKeygenBuilder<'a, E, WithThreshold, L, D>;
87
88/// Key generation entry point with choice for threshold or non-threshold
89/// variant
90pub struct GenericKeygenBuilder<'a, E: Curve, M, L: SecurityLevel, D: Digest> {
91    i: u16,
92    n: u16,
93    reliable_broadcast_enforced: bool,
94    optional_t: M,
95    execution_id: ExecutionId<'a>,
96    tracer: Option<&'a mut dyn Tracer>,
97    #[cfg(feature = "hd-wallet")]
98    hd_enabled: bool,
99    _params: core::marker::PhantomData<(E, L, D)>,
100}
101
102/// Indicates non-threshold DKG
103pub struct NonThreshold;
104/// Indicates threshold DKG
105pub struct WithThreshold(u16);
106
107impl<'a, E, L, D> GenericKeygenBuilder<'a, E, NonThreshold, L, D>
108where
109    E: Curve,
110    L: SecurityLevel,
111    D: Digest + Clone + 'static,
112{
113    /// Constructs [KeygenBuilder]
114    ///
115    /// Takes local party index $i$ and number of parties $n$
116    pub fn new(eid: ExecutionId<'a>, i: u16, n: u16) -> Self {
117        Self {
118            i,
119            n,
120            optional_t: NonThreshold,
121            reliable_broadcast_enforced: true,
122            execution_id: eid,
123            tracer: None,
124            #[cfg(feature = "hd-wallet")]
125            hd_enabled: true,
126            _params: core::marker::PhantomData,
127        }
128    }
129}
130
131impl<'a, E, L, D, M> GenericKeygenBuilder<'a, E, M, L, D>
132where
133    E: Curve,
134    L: SecurityLevel,
135    D: Digest + Clone + 'static,
136{
137    /// Specifies to generate key shares for a threshold scheme
138    pub fn set_threshold(self, t: u16) -> GenericKeygenBuilder<'a, E, WithThreshold, L, D> {
139        GenericKeygenBuilder {
140            i: self.i,
141            n: self.n,
142            optional_t: WithThreshold(t),
143            reliable_broadcast_enforced: self.reliable_broadcast_enforced,
144            execution_id: self.execution_id,
145            tracer: self.tracer,
146            #[cfg(feature = "hd-wallet")]
147            hd_enabled: self.hd_enabled,
148            _params: core::marker::PhantomData,
149        }
150    }
151    /// Specifies another hash function to use
152    pub fn set_digest<D2>(self) -> GenericKeygenBuilder<'a, E, M, L, D2>
153    where
154        D2: Digest + Clone + 'static,
155    {
156        GenericKeygenBuilder {
157            i: self.i,
158            n: self.n,
159            optional_t: self.optional_t,
160            reliable_broadcast_enforced: self.reliable_broadcast_enforced,
161            execution_id: self.execution_id,
162            tracer: self.tracer,
163            #[cfg(feature = "hd-wallet")]
164            hd_enabled: self.hd_enabled,
165            _params: core::marker::PhantomData,
166        }
167    }
168
169    /// Specifies [security level](crate::security_level)
170    pub fn set_security_level<L2>(self) -> GenericKeygenBuilder<'a, E, M, L2, D>
171    where
172        L2: SecurityLevel,
173    {
174        GenericKeygenBuilder {
175            i: self.i,
176            n: self.n,
177            optional_t: self.optional_t,
178            reliable_broadcast_enforced: self.reliable_broadcast_enforced,
179            execution_id: self.execution_id,
180            tracer: self.tracer,
181            #[cfg(feature = "hd-wallet")]
182            hd_enabled: self.hd_enabled,
183            _params: core::marker::PhantomData,
184        }
185    }
186
187    /// Sets a tracer that tracks progress of protocol execution
188    pub fn set_progress_tracer(mut self, tracer: &'a mut dyn Tracer) -> Self {
189        self.tracer = Some(tracer);
190        self
191    }
192
193    #[doc = include_str!("../docs/enforce_reliable_broadcast.md")]
194    pub fn enforce_reliable_broadcast(self, enforce: bool) -> Self {
195        Self {
196            reliable_broadcast_enforced: enforce,
197            ..self
198        }
199    }
200
201    #[cfg(feature = "hd-wallet")]
202    /// Specifies whether HD derivation is enabled for a key
203    pub fn hd_wallet(mut self, v: bool) -> Self {
204        self.hd_enabled = v;
205        self
206    }
207}
208
209impl<'a, E, L, D> GenericKeygenBuilder<'a, E, NonThreshold, L, D>
210where
211    E: Curve,
212    L: SecurityLevel,
213    D: Digest + Clone + 'static,
214{
215    /// Starts key generation
216    pub async fn start<R, M>(self, rng: &mut R, party: M) -> Result<CoreKeyShare<E>, KeygenError>
217    where
218        R: RngCore + CryptoRng,
219        M: Mpc<ProtocolMessage = non_threshold::Msg<E, L, D>>,
220    {
221        non_threshold::run_keygen(
222            self.tracer,
223            self.i,
224            self.n,
225            self.reliable_broadcast_enforced,
226            self.execution_id,
227            rng,
228            party,
229            #[cfg(feature = "hd-wallet")]
230            self.hd_enabled,
231        )
232        .await
233    }
234
235    /// Returns a state machine that can be used to carry out the key generation protocol
236    ///
237    /// See [`round_based::state_machine`] for details on how that can be done.
238    #[cfg(feature = "state-machine")]
239    pub fn into_state_machine<R>(
240        self,
241        rng: &'a mut R,
242    ) -> impl round_based::state_machine::StateMachine<
243        Output = Result<CoreKeyShare<E>, KeygenError>,
244        Msg = non_threshold::Msg<E, L, D>,
245    > + 'a
246    where
247        R: RngCore + CryptoRng,
248    {
249        round_based::state_machine::wrap_protocol(|party| self.start(rng, party))
250    }
251}
252
253impl<'a, E, L, D> GenericKeygenBuilder<'a, E, WithThreshold, L, D>
254where
255    E: Curve,
256    L: SecurityLevel,
257    D: Digest + Clone + 'static,
258{
259    /// Starts threshold key generation
260    pub async fn start<R, M>(self, rng: &mut R, party: M) -> Result<CoreKeyShare<E>, KeygenError>
261    where
262        R: RngCore + CryptoRng,
263        M: Mpc<ProtocolMessage = threshold::Msg<E, L, D>>,
264    {
265        threshold::run_threshold_keygen(
266            self.tracer,
267            self.i,
268            self.optional_t.0,
269            self.n,
270            self.reliable_broadcast_enforced,
271            self.execution_id,
272            rng,
273            party,
274            #[cfg(feature = "hd-wallet")]
275            self.hd_enabled,
276        )
277        .await
278    }
279
280    /// Returns a state machine that can be used to carry out the key generation protocol
281    ///
282    /// See [`round_based::state_machine`] for details on how that can be done.
283    #[cfg(feature = "state-machine")]
284    pub fn into_state_machine<R>(
285        self,
286        rng: &'a mut R,
287    ) -> impl round_based::state_machine::StateMachine<
288        Output = Result<CoreKeyShare<E>, KeygenError>,
289        Msg = threshold::Msg<E, L, D>,
290    > + 'a
291    where
292        R: RngCore + CryptoRng,
293    {
294        round_based::state_machine::wrap_protocol(|party| self.start(rng, party))
295    }
296}
297
298/// Keygen protocol error
299#[derive(Debug, displaydoc::Display)]
300#[cfg_attr(feature = "std", derive(thiserror::Error))]
301#[displaydoc("keygen protocol is failed to complete")]
302pub struct KeygenError(#[cfg_attr(feature = "std", source)] Reason);
303
304crate::errors::impl_from! {
305    impl From for KeygenError {
306        err: KeygenAborted => KeygenError(Reason::Aborted(err)),
307        err: IoError => KeygenError(Reason::IoError(err)),
308        err: Bug => KeygenError(Reason::Bug(err)),
309    }
310}
311
312#[derive(Debug, displaydoc::Display)]
313#[cfg_attr(feature = "std", derive(thiserror::Error))]
314enum Reason {
315    /// Protocol was maliciously aborted by another party
316    #[displaydoc("protocol was aborted by malicious party")]
317    Aborted(#[cfg_attr(feature = "std", source)] KeygenAborted),
318    #[displaydoc("i/o error")]
319    IoError(#[cfg_attr(feature = "std", source)] IoError),
320    /// Bug occurred
321    #[displaydoc("bug occurred")]
322    Bug(Bug),
323}
324
325impl From<KeygenAborted> for Reason {
326    fn from(err: KeygenAborted) -> Self {
327        Reason::Aborted(err)
328    }
329}
330
331/// Error indicating that protocol was aborted by malicious party
332///
333/// It _can be_ cryptographically proven, but we do not support it yet.
334#[derive(Debug, displaydoc::Display)]
335#[cfg_attr(feature = "std", derive(thiserror::Error))]
336enum KeygenAborted {
337    #[displaydoc("party decommitment doesn't match commitment: {0:?}")]
338    InvalidDecommitment(Vec<utils::AbortBlame>),
339    #[displaydoc("party provided invalid schnorr proof: {0:?}")]
340    InvalidSchnorrProof(Vec<utils::AbortBlame>),
341    #[displaydoc("party secret share is not consistent: {parties:?}")]
342    FeldmanVerificationFailed { parties: Vec<u16> },
343    #[displaydoc("party data size is not suitable for threshold parameters: {parties:?}")]
344    InvalidDataSize { parties: Vec<u16> },
345    #[displaydoc("round1 wasn't reliable")]
346    Round1NotReliable(Vec<(PartyIndex, MsgId)>),
347    #[cfg(feature = "hd-wallet")]
348    #[displaydoc("party did not generate chain code: {0:?}")]
349    MissingChainCode(Vec<utils::AbortBlame>),
350}
351
352#[derive(Debug, displaydoc::Display)]
353#[cfg_attr(feature = "std", derive(thiserror::Error))]
354enum Bug {
355    #[displaydoc("resulting key share is not valid")]
356    InvalidKeyShare(#[cfg_attr(feature = "std", source)] InvalidCoreShare),
357    #[displaydoc("unexpected zero value")]
358    NonZeroScalar,
359    #[cfg(feature = "hd-wallet")]
360    #[displaydoc("chain code is missing although we checked that it should be present")]
361    NoChainCode,
362    #[displaydoc("key share of one of the signers is zero - probability of that is negligible")]
363    ZeroShare,
364    #[displaydoc("shared public key is zero - probability of that is negligible")]
365    ZeroPk,
366}
367
368/// Distributed key generation protocol
369///
370/// Each party of the protocol should have uniquely assigned index $i$ such that $0 \le i < n$
371/// (where $n$ is amount of parties in the protocol).
372pub fn keygen<E: Curve>(eid: ExecutionId, i: u16, n: u16) -> KeygenBuilder<E> {
373    KeygenBuilder::new(eid, i, n)
374}