dkls23/lib.rs
1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4//! A rust threshold ECDSA signatures library implementing DKLs23 protocol.
5//!
6//! ## Functionality
7//! - Distributed Key Generation (DKG)
8//! - Distributed Signature Generation (DSG)
9//! - Key refresh protocol that refreshes the secret key shares without changing the common public key.
10//! - Import a singleton key and distribute it among parties
11//! - Export a threshold key to a singleton one
12//! - Quorum Change: change dynamically the set of participants by adding or removing nodes
13//! - Migration: Migrate from compatible curve protocols like: GG** or CMP to DKLs23
14//!
15//! ## Examples
16//! The mod common module can be replicated from the dkls23 github [repo](https://github.com/silence-laboratories/dkls23/examples/common.rs) under examples folder
17//! ### KeyGen
18//! ```
19//! use dkls23::keygen;
20//! use k256::elliptic_curve::group::GroupEncoding;
21//! use rand::Rng;
22//! use rand_chacha::ChaCha20Rng;
23//! use rand_core::SeedableRng;
24//! use std::sync::Arc;
25//!
26//! mod common;
27//!
28//! #[tokio::main]
29//! pub async fn main() {
30//! let t: u8 = 2;
31//! let n: u8 = 3;
32//! let coord = sl_mpc_mate::coord::SimpleMessageRelay::new();
33//!
34//! let mut parties = tokio::task::JoinSet::new();
35//!
36//! for setup in common::shared::setup_keygen(t, n, None) {
37//! parties.spawn({
38//! let relay = coord.connect();
39//! let mut rng = ChaCha20Rng::from_entropy();
40//! keygen::run(setup, rng.gen(), relay)
41//! });
42//! }
43//! let mut shares = vec![];
44//! while let Some(fini) = parties.join_next().await {
45//! if let Err(ref err) = fini {
46//! println!("error {err:?}");
47//! } else {
48//! match fini.unwrap() {
49//! Err(err) => panic!("err {:?}", err),
50//! Ok(share) => shares.push(Arc::new(share)),
51//! }
52//! }
53//! }
54//!
55//! for keyshare in shares.iter() {
56//! println!("PK{}", hex::encode(keyshare.public_key().to_bytes()));
57//! }
58//! }
59//! ```
60//! ### Key Refresh
61//! ```
62//! use dkls23::keygen::key_refresh::KeyshareForRefresh;
63//! use k256::elliptic_curve::group::GroupEncoding;
64//! use rand::Rng;
65//! use rand_chacha::ChaCha20Rng;
66//! use rand_core::SeedableRng;
67//! use sl_mpc_mate::coord::SimpleMessageRelay;
68//! use std::sync::Arc;
69//! use tokio::task::JoinSet;
70//!
71//! mod common;
72//!
73//! #[tokio::main]
74//! pub async fn main() {
75//! let old_shares = common::shared::gen_keyshares(2, 3).await;
76//! let coord = SimpleMessageRelay::new();
77//! let mut parties = JoinSet::new();
78//!
79//! let key_shares_for_refresh: Vec<KeyshareForRefresh> = old_shares
80//! .iter()
81//! .map(|share| KeyshareForRefresh::from_keyshare(share, None))
82//! .collect();
83//!
84//! let mut rng = ChaCha20Rng::from_entropy();
85//! for (setup, share) in common::shared::setup_keygen(2, 3, None)
86//! .into_iter()
87//! .zip(key_shares_for_refresh)
88//! .collect::<Vec<_>>()
89//! {
90//! // run the keyrefresh protocol for each node
91//! parties.spawn(dkls23::keygen::key_refresh::run(
92//! setup,
93//! rng.gen(),
94//! coord.connect(),
95//! share,
96//! ));
97//! }
98//!
99//! let mut new_shares = vec![];
100//! while let Some(fini) = parties.join_next().await {
101//! let fini = fini.unwrap();
102//!
103//! if let Err(ref err) = fini {
104//! println!("error {}", err);
105//! }
106//!
107//! assert!(fini.is_ok());
108//!
109//! // Print all the new PK of the refreshed share
110//! let new_share = fini.unwrap();
111//! let pk = hex::encode(new_share.public_key().to_bytes());
112//!
113//! new_shares.push(Arc::new(new_share));
114//!
115//! println!("PK {}", pk);
116//! }
117//!
118//! //check that this is equal the old key share public key
119//! println!(
120//! "Old PK{}",
121//! hex::encode(old_shares[0].public_key().to_bytes())
122//! );
123//!
124//! }
125//! ```
126//!
127//! ### Sign
128//! ```
129//! use tokio::task::JoinSet;
130//!
131//! use rand::Rng;
132//! use rand_chacha::ChaCha20Rng;
133//! use rand_core::SeedableRng;
134//!
135//! use k256::ecdsa::{RecoveryId, VerifyingKey};
136//!
137//! use dkls23::sign;
138//! use sl_mpc_mate::coord::SimpleMessageRelay;
139//!
140//! mod common;
141//!
142//! #[tokio::main]
143//! async fn main() {
144//! let coord = SimpleMessageRelay::new();
145//!
146//! // We locally generate some key shares in order to test the signing procedure.
147//! let shares = common::shared::gen_keyshares(2, 3).await;
148//!
149//! //fetch the public verification key from one of the keyshares
150//! let vk = VerifyingKey::from_affine(shares[0].public_key().to_affine()).unwrap();
151//!
152//! //define a chain path for the signature: m is the default one
153//! let chain_path = "m";
154//!
155//! //Here the parties are simulated as in a real world example but locally as a set of rust async tasks:
156//! let mut parties = JoinSet::new();
157//!
158//! for setup in common::shared::setup_dsg(&shares[0..2], chain_path) {
159//! let mut rng = ChaCha20Rng::from_entropy();
160//! let relay = coord.connect();
161//!
162//! parties.spawn(sign::run(setup, rng.gen(), relay));
163//! }
164//!
165//! // After all the tasks have finished we extract the signature and verify it against the public key
166//! while let Some(fini) = parties.join_next().await {
167//! let fini = fini.unwrap();
168//!
169//! if let Err(ref err) = fini {
170//! println!("error {err:?}");
171//! }
172//!
173//! let (sign, recid) = fini.unwrap();
174//!
175//! let hash = [1u8; 32];
176//!
177//! let recid2 = RecoveryId::trial_recovery_from_prehash(&vk, &hash, &sign).unwrap();
178//!
179//! assert_eq!(recid, recid2);
180//! }
181//! }
182//!```
183//!
184//! ## Networking
185//! Communication between nodes is happening through a relayer in a pull messaging mode:
186//! Everything is posted on the relayer and the receiver knows when and what to ask. That was a design
187//! decision that maps best the nature of MPC protocols whereby any mpc node depending on the protocol knows what type of messages to expect and from
188//! where.
189//!
190//! The relayer follows the Actor model: It spawns from the caller task, does the assigned task
191//! independently and return the result in the main task. The library itself does not expose networking stack
192//! ,but instead a generic combination of shared state between rust tasks and message channel passing where
193//! receiver and sender channels are interleaved for p2p and broadcast communication. That is a local `SimpleMessageRelay`.
194//! In a real setup the relayer can be an independent network entity, where all the nodes can talk to. It
195//! can be implemented with a variety of existing networking protocols such as websockets; as long as it follows the
196//! underlying pull logic : Each receiver knows what message to subscribe for and so it asks the relayer to deliver it
197//! as long as it arrives from the expected sender.
198//!
199//!
200//!
201//! ## Data Serialization
202//! The library implements zero-copy message serialization. All messages sent between parties
203//! and their components are defined as arrays of bytes. This transformation enables us to safely cast a byte
204//! slice `&[u8]` into a reference to some message structure if the sizes
205//! are equal.
206//!
207//! This allows to implement in-place message construction: Allocate
208//! a memory buffer of an appropriate size, take a mutable reference to
209//! some message structure, and pass it to a message constructor. Then
210//! calculate the message signature or encrypt the message in place
211//! without any extra memory copying.
212//! This provides not only memory efficiency but also more secure code
213//! because there is exactly one copy of secret material in memory and
214//! overwrite it with in-place encryption.
215//! Key share representation also uses the same technique. Allocates a
216//! memory buffer for the key share at the beginning of the key generation
217//! execution and fill it piece by piece. Thus, memory copies are not happening
218#![deny(missing_docs, unsafe_code)]
219
220use rand::SeedableRng;
221use rand_chacha::ChaCha20Rng;
222
223/// DKLs23 keygen.
224pub mod keygen;
225
226/// DKLs23 sign.
227pub mod sign;
228
229/// Setup messages.
230pub mod setup;
231
232/// Misc helper functions.
233pub mod proto;
234
235/// Seed for our RNG.
236pub type Seed = <ChaCha20Rng as SeedableRng>::Seed;
237
238/// Exports a threshold key to a singleton one by consolidating all shares of other nodes.
239pub mod key_export;
240/// Imports a singleton external key and secret shares it among parties to use dkls23 related mpc protocols.
241pub mod key_import;
242
243pub(crate) mod pairs;
244
245/// Version of domain labels
246pub const VERSION: u16 = 1;
247
248pub use k256;
249pub use sl_mpc_mate::coord::{MessageSendError, Relay};
250pub use sl_mpc_mate::message::{InstanceId, MsgId};