Skip to main content

cggmp24/
lib.rs

1//! ![License](https://img.shields.io/crates/l/cggmp24.svg)
2//! [![Docs](https://docs.rs/cggmp24/badge.svg)](https://docs.rs/cggmp24)
3//! [![Crates io](https://img.shields.io/crates/v/cggmp24.svg)](https://crates.io/crates/cggmp24)
4//! [![Discord](https://img.shields.io/discord/905194001349627914?logo=discord&logoColor=ffffff&label=Discord)][in Discord]
5//!
6//! # Threshold ECDSA based on [CGGMP24] paper
7//!
8//! <!-- TOC -->
9#![doc = include_str!("../docs/toc-cggmp24.md")]
10//!
11//! [CGGMP24] is a state-of-art ECDSA TSS protocol that supports 1-round signing (requires 3 preprocessing rounds),
12//! identifiable abort, and a key refresh protocol.
13//!
14//! This crate implements:
15//! * Threshold (i.e., t-out-of-n) and non-threshold (i.e., n-out-of-n) key generation
16//! * (3+1)-round general threshold and non-threshold signing
17//! * Auxiliary info generation protocol
18//! * HD-wallets support based on [slip10] standard (compatible with [bip32]) \
19//!   Requires `hd-wallets` feature
20//!
21//! A self-contained description of the protocols we implemented is available [here][the spec].
22//!
23//! We also provide auxiliary tools like:
24//! * [Secret key reconstruction](crate::key_share::reconstruct_secret_key) (exporting key from TSS)
25//! * [Trusted dealer](crate::trusted_dealer) (importing key into TSS)
26//!
27//! This crate **does not** (currently) support:
28//! * Key refresh for both threshold (i.e., t-out-of-n) and non-threshold (i.e., n-out-of-n) keys
29//! * Identifiable abort
30//!
31//! Our implementation has been audited by Kudelski. Report can be found [here][report].
32//!
33//! > About notion of threshold and non-threshold keys: originally, the CGGMP24 paper does not specify
34//! protocols for threshold t-out-of-n keys with arbitrary `t`, and only works with non-threshold
35//! n-out-of-n keys. We have added support for arbitrary threshold $2 \le t \le n$, however, we made
36//! it possible to opt out thresholdness so original CGGMP24 protocol can be carried out if needed.
37//!
38//! ## Running the protocol
39//!
40//! ### Networking
41//! The most essential part of running an interactive protocol is to define how parties can communicate with
42//! each other. Our `cggmp24` library is agnostic to the network layer and only requires you to provide two
43//! things: a stream of incoming messages and a sink for outgoing messages, i.e.:
44//!
45//! ```rust,ignore
46//! let incoming: impl Stream<Item = Result<Incoming<Msg>>>;
47//! let outgoing: impl Sink<Outgoing<Msg>>;
48//! ```
49//!
50//! where:
51//! * `Msg` is a protocol message (e.g., [`signing::msg::Msg`])
52//! * [`round_based::Incoming`] and [`round_based::Outgoing`] wrap `Msg` and provide additional data (e.g., sender/recipient)
53//! * [`futures::Stream`] and [`futures::Sink`] are well-known async primitives.
54//!
55//! Once you have that, you can construct an [`MpcParty`](round_based::MpcParty):
56//!
57//! [`MpcParty`]: round_based::MpcParty
58//!
59//! ```rust
60//! # type Msg = cggmp24::signing::msg::Msg<cggmp24::supported_curves::Secp256k1, sha2::Sha256>;
61//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
62//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
63//! let delivery = (incoming, outgoing);
64//! let party = round_based::MpcParty::connected(delivery);
65//! ```
66//!
67//! The concrete networking implementation to use will depend heavily on the specific application.
68//! Some applications may use libp2p; others may prefer having a central delivery server or a database
69//! (like Redis or Postgres); some specific applications may want to communicate over a public
70//! blockchain, and so on.
71//!
72//! Whatever networking implementation you use, keep in mind that:
73//!
74//! * All messages must be authenticated \
75//!   Whenever one party receives a message from another, the receiver should cryptographically
76//!   verify that the message comes from the claimed sender.
77//! * All p2p messages must be encrypted \
78//!   Only the designated recipient should be able to read the message
79//!
80//! #### Signer indices
81//! Our library uses indices to uniquely refer to particular signers sharing a key. Each index `i`
82//! is an unsigned integer `u16` with $0 \le i < n$ where `n` is the total number of parties.
83//!
84//! All signers should have the same view about each others' indices. For instance, if Signer A
85//! holds index 2, then all other signers must agree that i=2 corresponds to Signer A.
86//!
87//! Assuming some sort of PKI (which would anyway likely be used to ensure secure communication,
88//! as described above), each signer has a public key that uniquely identifies that signer.
89//! It is then possible to assign unique indices to the signers by lexicographically sorting the
90//! signers' public keys, and letting the index of a signer be the position of that signer's public
91//! key in the sorted list.
92//!
93//! ### Execution ID
94//! Execution of our protocols requires all participants to agree on unique execution ID (aka
95//! session identifier) that is assumed never to repeat. This string provides context separation
96//! between different executions of the protocol to ensure that an adversary cannot replay messages
97//! from one execution to another.
98//!
99//! Once signers can talk to each other and share an execution ID, they're ready to do MPC!
100//!
101//! ### Auxiliary info generation
102//! In the usual flow, signers run a protocol for auxiliary-data generation before running distributed
103//! key generation. This protocol sets up certain parameters (in particular, Paillier moduli
104//! for each of the signers) that will be used during the signing protocols. This protocol can be
105//! run as follows:
106//!
107//! ```rust,no_run
108//! # async fn doc() -> Result<(), cggmp24::KeyRefreshError> {
109//! # type Msg = cggmp24::key_refresh::msg::Msg<sha2::Sha256, cggmp24::security_level::SecurityLevel128>;
110//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
111//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
112//! # let delivery = (incoming, outgoing);
113//! # let party = round_based::MpcParty::connected(delivery);
114//! #
115//! # use rand_core::OsRng;
116//! // Prime generation can take a while
117//! let pregenerated_primes = cggmp24::PregeneratedPrimes::generate(&mut OsRng);
118//!
119//! let eid = cggmp24::ExecutionId::new(b"execution id, unique per protocol execution");
120//! let i = /* signer index, same as at keygen */
121//! # 0;
122//! let n = /* number of signers */
123//! # 3;
124//!
125//! let aux_info = cggmp24::aux_info_gen(eid, i, n, pregenerated_primes)
126//!     .start(&mut OsRng, party)
127//!     .await?;
128//! # Ok(()) }
129//! ```
130//!
131//! The auxiliary-data generation protocol is computationally heavy as it requires the generation
132//! of safe primes and involves several zero-knowledge (ZK) proofs.
133//!
134//! #### On reusability of the auxiliary data
135//! The CGGMP24 paper assumes that new auxiliary data is generated for each secret key that is shared.
136//! However, examination of the proof shows that this is not necessary, and a fixed group of signers
137//! can use the same auxiliary data for the secure sharing/usage of multiple keys.
138//!
139//! ### Distributed Key Generation (DKG)
140//! The DKG protocol involves all signers who will co-share a key. All signers need to agree on
141//! some basic parameters including the participants' indices, the execution ID, and the
142//! threshold value (i.e., t). The protocol can be executed as
143//!
144//! ```rust,no_run
145//! # async fn doc() -> Result<(), cggmp24::KeygenError> {
146//! # type Msg = cggmp24::keygen::msg::threshold::Msg<cggmp24::supported_curves::Secp256k1, cggmp24::security_level::SecurityLevel128, sha2::Sha256>;
147//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
148//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
149//! # let delivery = (incoming, outgoing);
150//! # let party = round_based::MpcParty::connected(delivery);
151//! #
152//! use cggmp24::supported_curves::Secp256k1;
153//! # use rand_core::OsRng;
154//!
155//! let eid = cggmp24::ExecutionId::new(b"execution id, unique per protocol execution");
156//! let i = /* signer index (0 <= i < n) */
157//! # 0;
158//! let n = /* number of signers taking part in key generation */
159//! # 3;
160//! let t = /* threshold */
161//! # 2;
162//!
163//! let incomplete_key_share = cggmp24::keygen::<Secp256k1>(eid, i, n)
164//!     .set_threshold(t)
165//!     .start(&mut OsRng, party)
166//!     .await?;
167//! # Ok(()) }
168//! ```
169//!
170//! The above produces an [`IncompleteKeyShare`]. An incomplete key share can be saved on disk by serializing using
171//! [`serde` crate][serde]. Treat this material appropriately as it contains sensitive information.
172//!
173//! Assuming auxiliary-data generation has already been done (see above), you can "complete" the
174//! key share using:
175//!
176//! ```rust,no_run
177//! # let (incomplete_key_share, aux_info): (cggmp24::IncompleteKeyShare<cggmp24::supported_curves::Secp256k1>, cggmp24::key_share::AuxInfo) = unimplemented!();
178//! let key_share = cggmp24::KeyShare::from_parts((incomplete_key_share, aux_info))?;
179//! # Ok::<_, cggmp24::key_share::InvalidKeyShare>(())
180//! ```
181//!
182//! ### Signing
183//! Once signers have a set of "completed" key shares, they can sign or generate presignatures.
184//! In either case, exactly the threshold number (i.e., t) of signers must take part in the protocol.
185//! As in the DKG protocol, each signer needs to be assigned a unique index, now in the range from 0
186//! to t-1. But the signers also need to know which index each signer occupied at the time of keygen.
187//!
188//! In the example below, we do a full signing:
189//! ```rust,no_run
190//! # async fn doc() -> Result<(), cggmp24::SigningError> {
191//! # type Msg = cggmp24::signing::msg::Msg<cggmp24::supported_curves::Secp256k1, sha2::Sha256>;
192//! # let incoming = futures::stream::pending::<Result<round_based::Incoming<Msg>, std::convert::Infallible>>();
193//! # let outgoing = futures::sink::drain::<round_based::Outgoing<Msg>>();
194//! # let delivery = (incoming, outgoing);
195//! # let party = round_based::MpcParty::connected(delivery);
196//! #
197//! # use rand_core::OsRng; use sha2::Sha256;
198//! # const MIN_SIGNERS: usize = 3;
199//! #
200//! let eid = cggmp24::ExecutionId::new(b"execution id, unique per protocol execution");
201//!
202//! let i = /* signer index (0 <= i < min_signers) */
203//! # 0;
204//! let parties_indexes_at_keygen: [u16; MIN_SIGNERS] =
205//!     /* parties_indexes_at_keygen[i] is the index the i-th party had at keygen */
206//! # [0, 1, 2];
207//! let key_share = /* completed key share */
208//! # {let s: cggmp24::KeyShare<cggmp24::supported_curves::Secp256k1> = unimplemented!(); s};
209//!
210//! let data_to_sign = cggmp24::DataToSign::digest::<Sha256>(b"data to be signed");
211//!
212//! let signature = cggmp24::signing(eid, i, &parties_indexes_at_keygen, &key_share)
213//!     .sign(&mut OsRng, party, &data_to_sign)
214//!     .await?;
215//! # Ok(()) }
216//! ```
217//!
218//! Alternatively, you can generate a presignature and later use it to sign:
219//! 1. Use [`SigningBuilder::generate_presignature`] to run the presignature generation protocol
220//! 2. Later, when a signing request is received, each signer issues a partial signature using
221//!    [`Presignature::issue_partial_signature`]
222//! 3. A threshold number of partial signatures can be combined using [`PartialSignature::combine`] to
223//!    obtain a full signature
224//!
225//! **Never reuse presignatures!** If you use the same presignature to sign two different messages,
226//! the private key may be leaked.
227//!
228//! ## Sync API
229//! Every protocol is defined as async function. If you need to run a protocol in non-async environment,
230//! library provides a wrapper that allows you to execute protocol using sync API only.
231//!
232//! To use it, you need to enable `state-machine` feature. Then, for every protocol definition, you can
233//! find a companion function that returns [`StateMachine`](round_based::state_machine::StateMachine)
234//! which can be used to carry out the protocol. For instance, if you do presignature generation, use
235//! [`signing::SigningBuilder::generate_presignature_sync`].
236//!
237//! ## HD wallets support
238//! Library supports non-hardened deterministic key derivation based on [slip10] standard (compatible
239//! with [bip32]). It allows signers to generate a master key once, and then use it to instantaneously
240//! derive as many child keys as needed. Child key derivation takes place within signing protocol
241//! practically at no cost.
242//!
243//! In order to use HD wallets, `hd-wallets` feature must be enabled. Then, a master key needs to be
244//! generated by running a regular key generation protocol with [`hd_wallet`](keygen::GenericKeygenBuilder::hd_wallet)
245//! set to `true`.
246//!
247//! When master key is generated, you can issue a signature for child key by setting
248//! [derivation path](signing::SigningBuilder::set_derivation_path) in the signing.
249//!
250//! ## SPOF code: Key Import and Export
251//! CGGMP24 protocol is designed to avoid Single Point of Failure by guaranteeing that attacker would
252//! need to compromise threshold amount of nodes to obtain a secret key. However, some use-cases may
253//! require you to create a SPOF, for instance, importing an existing key into TSS and exporting key
254//! from TSS.
255//!
256//! Such use-cases contradict to nature of MPC so we don't include those primitives by default.
257//! However, you may opt for them by enabling `spof` feature, then you can use [`trusted_dealer`]
258//! for key import and [`key_share::reconstruct_secret_key`] for key export.
259//!
260//! ## Big integer implementation
261//!
262//! This protocol relies heavily on big integer arithmetic, and provides a
263//! choice between two backends: rug and num-bigint.
264//!
265//! - To use rug, select the feature `backend-rug`. This backend is faster, in
266//! some applications by several times, but requires an LGPL dependency
267//! - To use num-bigint, select the feature `backend-num-bigint` (selected as
268//! **default** feature)
269//!
270//! ## no\_std compatability
271//!
272//! Every crate in this project is compatible with no\_std, provided that
273//! `num-bigint` backend is chosen. The presence of `alloc` is still required. This
274//! means that `cggmp24` and `cggmp24-keygen` can be run on a
275//! `wasm32-unknown-unknown` platform.
276//!
277//! To compile the crates for `no_std`, you need to disable the default feature
278//! "std" and instead enable `no_std` for some crates. The dependencies in your
279//! cargo file should look like this:
280//!
281//! ```toml
282//! cggmp24 = { version = "0.7", default-features = false, features = ["no_std", "backend-num-bigint"] }
283//! cggmp24-keygen = { version = "0.7", default-features = false }
284//! key-share = { version = "0.6", default-features = false }
285//! ```
286//!
287//! ## Differences between the implementation and CGGMP24
288//! [CGGMP24] only defines a non-threshold protocol. To support general thresholds,
289//! we defined our own CGGMP24-like key generation and threshold signing
290//! protocols. However, we keep both
291//! threshold and non-threshold versions of the protocols in the crate, so if you opt for the non-threshold
292//! protocol, you will be running the original protocol defined in the paper.
293//!
294//! There are other (small) differences in the implementation compared to the original paper (mostly typo fixes);
295//! they are all documented in [the spec].
296//!
297//! [CGGMP24]: https://ia.cr/2021/060
298//! [the spec]: https://lfdt-lockness.github.io/cggmp21/cggmp24-spec.pdf
299//! [security guidelines]: #security-guidelines
300//! [slip10]: https://github.com/satoshilabs/slips/blob/master/slip-0010.md
301//! [bip32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
302//! [report]: https://github.com/LFDT-Lockness/cggmp21/blob/m/docs/audit_report.pdf
303//! [serde]: https://serde.rs/
304//!
305//! ## Timing attacks
306//! Timing attacks are type of side-channel attacks that leak sensitive information through duration of
307//! execution. We consider timing attacks out of scope as they are nearly impossible to perform for such
308//! complicated protocol as CGGMP24 and impossible to do in our specific deployment. Thus, we intentionally
309//! don't do constant-time operations which gives us a significant performance boost.
310//!
311//! ## Join us in Discord!
312//! Feel free to reach out to us [in Discord]!
313//!
314//! [in Discord]: https://discordapp.com/channels/905194001349627914/1285268686147424388
315
316#![allow(
317    non_snake_case,
318    mixed_script_confusables,
319    uncommon_codepoints,
320    clippy::too_many_arguments,
321    clippy::nonminimal_bool
322)]
323#![forbid(clippy::disallowed_methods, missing_docs, unsafe_code)]
324#![cfg_attr(not(test), forbid(unused_crate_dependencies))]
325#![cfg_attr(docsrs, feature(doc_cfg))]
326
327#[cfg(feature = "hd-wallet")]
328pub use hd_wallet;
329pub use {
330    generic_ec, paillier_zk,
331    paillier_zk::{backend, fast_paillier},
332    round_based,
333};
334
335#[doc(inline)]
336pub use cggmp24_keygen::{keygen, progress, ExecutionId};
337
338use generic_ec::{coords::HasAffineX, Curve, Point};
339use round_based::PartyIndex;
340use security_level::SecurityLevel;
341use signing::SigningBuilder;
342
343mod errors;
344pub mod key_refresh;
345pub mod key_share;
346pub mod security_level;
347pub mod signing;
348pub mod supported_curves;
349mod utils;
350mod zk;
351
352mod _unused_deps {
353    // we don't use it, but we need to enable certain features
354    use key_share as _;
355}
356
357#[cfg(feature = "spof")]
358pub mod trusted_dealer;
359
360/// Defines default choice for digest and security level used across the crate
361mod default_choice {
362    pub type Digest = sha2::Sha256;
363    pub type SecurityLevel = crate::security_level::SecurityLevel128;
364}
365
366/// Threshold and non-threshold CGGMP24 DKG
367pub mod keygen {
368    #[doc(inline)]
369    pub use cggmp24_keygen::{
370        msg, GenericKeygenBuilder, KeygenBuilder, KeygenError, NonThreshold,
371        ThresholdKeygenBuilder, WithThreshold,
372    };
373
374    pub use msg::non_threshold::Msg as NonThresholdMsg;
375    pub use msg::threshold::Msg as ThresholdMsg;
376}
377
378pub use self::{
379    key_refresh::{KeyRefreshError, PregeneratedPrimes},
380    key_share::{IncompleteKeyShare, KeyShare},
381    keygen::KeygenError,
382    signing::{
383        DataToSign, PartialSignature, PrehashedDataToSign, Presignature, Signature, SigningError,
384    },
385};
386
387/// Protocol for finalizing the keygen by generating aux info.
388///
389/// PregeneratedPrimes can be obtained with [`key_refresh::PregeneratedPrimes::generate`]
390///
391/// Index `i` of party should be the same as index [inside the key share] you are
392/// going to use this aux info with. Number of parties `n` should be the same [as number
393/// of signers] sharing the key.
394///
395/// Outputs [`AuxInfo`](key_share::AuxInfo) that can be used to "complete" [`IncompleteKeyShare`]
396/// using [`KeyShare::from_parts`].
397///
398/// [inside the key share]: key_share::DirtyIncompleteKeyShare::i
399/// [as number of signers]: IncompleteKeyShare::n
400pub fn aux_info_gen<L>(
401    eid: ExecutionId,
402    i: u16,
403    n: u16,
404    pregenerated: key_refresh::PregeneratedPrimes<L>,
405) -> key_refresh::AuxInfoBuilder<L>
406where
407    L: SecurityLevel,
408{
409    key_refresh::AuxInfoBuilder::new_aux_gen(eid, i, n, pregenerated)
410}
411
412/// Protocol for generating a signature or presignature
413pub fn signing<'r, E, L>(
414    eid: ExecutionId<'r>,
415    i: PartyIndex,
416    parties_indexes_at_keygen: &'r [PartyIndex],
417    key_share: &'r KeyShare<E, L>,
418) -> SigningBuilder<'r, E, L>
419where
420    E: Curve,
421    Point<E>: HasAffineX<E>,
422    L: SecurityLevel,
423{
424    SigningBuilder::new(eid, i, parties_indexes_at_keygen, key_share)
425}
426
427#[cfg(test)]
428mod tests {
429    use digest::Digest;
430    use generic_ec::Curve;
431    use serde::{de::DeserializeOwned, Serialize};
432
433    use crate::security_level::SecurityLevel;
434
435    macro_rules! ensure_certain_types_impl_serde {
436        ($($type:ty),+,) => {
437            fn impls_serde<T: Serialize + DeserializeOwned>() {}
438
439            #[allow(dead_code)]
440            fn ensure_types_impl_serde<E: Curve, L: SecurityLevel, D: Digest>() {$(
441                impls_serde::<$type>();
442            )+}
443        }
444    }
445
446    ensure_certain_types_impl_serde! {
447        crate::key_share::KeyShare<E, L>,
448        crate::key_share::IncompleteKeyShare<E>,
449        crate::key_share::AuxInfo<L>,
450
451        crate::key_share::DirtyKeyShare<E, L>,
452        crate::key_share::DirtyIncompleteKeyShare<E>,
453        crate::key_share::DirtyAuxInfo<L>,
454
455        crate::keygen::msg::non_threshold::Msg<E, L, D>,
456        crate::keygen::msg::threshold::Msg<E, L, D>,
457
458        crate::key_refresh::msg::Msg<D, L>,
459
460        crate::signing::msg::Msg<E, D>,
461        crate::signing::Presignature<E>,
462        crate::signing::PartialSignature<E>,
463        crate::signing::Signature<E>,
464    }
465}