From ebe4ef6a293f269ea5380192b881717dbfcd9173 Mon Sep 17 00:00:00 2001 From: maurges Date: Fri, 8 Mar 2024 12:11:41 +0100 Subject: [PATCH] Readme should correspond to docs now --- README.md | 98 +++++++++++++++++++++++----------------------- cggmp21/src/lib.rs | 2 +- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index ae39f72..ea0cba2 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,17 @@ [![Docs](https://docs.rs/cggmp21/badge.svg)](https://docs.rs/cggmp21) [![Crates io](https://img.shields.io/crates/v/cggmp21.svg)](https://crates.io/crates/cggmp21) -# Threshold ECDSA based on CGGMP21 paper +# Threshold ECDSA based on [CGGMP21] paper [CGGMP21] is a state-of-art ECDSA TSS protocol that supports 1-round signing (requires preprocessing), identifiable abort, provides two signing protocols (3+1 and 5+1 rounds with different complexity of abort identification) and key refresh protocol out of the box. This crate implements: -* Threshold and non-threshold key generation -* 3+1 rounds threshold and non-threshold signing +* General threshold (i.e., t-out-of-n) and full threshold (i.e., n-out-of-n) key generation +* (3+1)-round general threshold and full threshold signing * Auxiliary info generation protocol -* Key refresh for non-threshold keys +* Key refresh for full-threshold keys * HD-wallets support based on [slip10] standard (compatible with [bip32]) \ Requires `hd-wallets` feature @@ -20,16 +20,16 @@ We also provide auxiliary tools like: * Secret key reconstruction (exporting key from TSS) * Trusted dealer (importing key into TSS) -This crate **does not** support (currently): -* Threshold key refresh +This crate **does not** (currently) support: +* Key refresh for general thresholds * Identifiable abort -* 5+1 rounds signing protocol +* The (5+1)-round signing protocol -## Running protocol +## Running the protocol ### Networking -In order to run protocol, you need to define how signer can communicate with other signers. We -use `round_based` framework that handles network part. Basically, you need to define: a stream +In order to run the protocol, you need to define how each signer can communicate with other signers. We +use a `round_based` framework that handles networking. Basically, you need to define a stream of `incoming` messages and sink of `outgoing` messages: ```rust @@ -38,7 +38,7 @@ let outgoing: impl Sink>; ``` where: -* `Msg` is protocol message (e.g., `signing::msg::Msg`) +* `Msg` is a protocol message (e.g., `signing::msg::Msg`) * `round_based::Incoming` and `round_based::Outgoing` wrap `Msg` and provide additional data (e.g., sender/recepient) * `futures::Stream` and `futures::Sink` are well-known async primitives. @@ -48,32 +48,32 @@ let delivery = (incoming, outgoing); let party = round_based::MpcParty::connected(delivery); ``` -#### Signers indexes -Each signer in protocol execution (keygen/signing/etc.) occupies a unique index $i$ ($0 \le i < n$, -where $n$ is amount of parties in the protocol). For instance, if Signer A occupies index `2`, then all -other signers must acknowledge that `i=2` corresponds to Signer A. +#### Signer indexes +Each signer in a protocol execution (keygen/signing/etc.) occupies a unique index $i$ ($0 \le i < n$, +where $n$ is number of parties overall). For instance, if Signer A occupies index `2`, then all +other signers must agree that `i=2` corresponds to Signer A. -Assuming you have some sort of PKI (which you need to comply with [security requirements]) and each signer -has a public key which uniqely idenitifies that signer, you can easily assign unique indexes to the signers: -1. Make a list of signers public keys +Assuming you have a PKI (which is anyway needed to comply with [security requirements]) and each signer +has a public key uniqely idenitifying that signer, you can assign unique indexes to the signers as follows: +1. Make a list of signers' public keys 2. Sort the list of public keys -3. Assign each signer index `i` such that `i` corresponds to position of signer public key in sorted list of - public keys +3. Assign each signer an index `i` such that `i` corresponds to the position of the signer's public key in the + sorted list of public keys [security requirements]: #security #### Security -Make sure that communication layer complies with security requirements: -* All messages sent between parties must be authenticated +Make sure that the communication layer complies with security requirements: +* All messages sent must be authenticated * All p2p messages must be encrypted ### Execution ID -Final step of preparation, all the signers need to agree on unique identifier of protocol execution `ExecutionId`. -Execution ID needs to be unique per protocol execution (keygen/signing/etc.), otherwise it may compromise security. -Execution ID needs to be the same for all signers taking part in the protocol, otherwise protocol will abort. -Execution ID **doesn't** need to be secret. +When executing a protocol, signers need to agree on a unique identifier of the protocol execution `ExecutionId`. +The Execution ID needs to be unique per protocol execution (keygen/signing/etc.), otherwise it may compromise security. +The Execution ID needs to be the same for all signers taking part in the protocol, otherwise protocol will abort. +Execution ID **does not** need to be secret. -Now that signers can talk to each other and they have an execution ID, they're ready to generate a key! +Once signers can talk to each other and share an execution ID, they're ready to generate a key! ### Distributed Key Generation ```rust @@ -81,7 +81,7 @@ use cggmp21::supported_curves::Secp256k1; let eid = cggmp21::ExecutionId::new(b"execution id, unique per protocol execution"); let i = /* signer index (0 <= i < n) */; -let n = /* amount of signers taking part in key generation */; +let n = /* number of signers taking part in key generation */; let t = /* threshold */; let incomplete_key_share = cggmp21::keygen::(eid, i, n) @@ -89,19 +89,19 @@ let incomplete_key_share = cggmp21::keygen::(eid, i, n) .start(&mut OsRng, party) .await?; ``` -This code outputs `IncompleteKeyShare`. Note that this key share is not ready yet to do signing. You need to “complete” it +This code outputs `IncompleteKeyShare`. Note that this key share is not yet ready to do signing. You need to “complete” it by generating auxiliary info (see below). ### Auxiliary info generation -After key generation, all signers need to take part in auxiliary information generation. Make sure all signers occupy exactly +After key generation, all signers need to take part in generation of auxiliary information. Make sure all signers occupy exactly the same indexes as at keygen. ```rust -// Primes generation can take a while +// Prime generation can take a while let pregenerated_primes = cggmp21::PregeneratedPrimes::generate(&mut OsRng); let eid = cggmp21::ExecutionId::new(b"execution id, unique per protocol execution"); let i = /* signer index, same as at keygen */; -let n = /* amount of signers */; +let n = /* number of signers */; let aux_info = cggmp21::aux_info_gen(eid, i, n, pregenerated_primes) .start(&mut OsRng, party) @@ -114,9 +114,9 @@ let key_share = cggmp21::KeyShare::from_parts((incomplete_key_share, aux_info))? ``` ### Signing -Once completed key share is obtained, signers can do signing or generate presignatures. In either case, threshold amount of -signers must take part in the protocol. Similar to previous protocols, at signing each signer needs to be assigned an index -`0 <= i < min_signers`, but we also need to know which index each signer occupied at keygen. +Once a complete key share is obtained, signers can sign or generate presignatures. In either case, the required threshold t of +signers must take part in the protocol. Each signer needs to be assigned a unique index +`0 <= i < t`, but we also need to know which index each signer had at keygen. In the example below, we do a full signing: ```rust @@ -124,7 +124,7 @@ let eid = cggmp21::ExecutionId::new(b"execution id, unique per protocol executio let i = /* signer index (0 <= i < min_signers) */; let parties_indexes_at_keygen: [u16; MIN_SIGNERS] = - /* parties_indexes_at_keygen[i] is index which i-th party occupied at keygen */; + /* parties_indexes_at_keygen[i] is the index the i-th party had at keygen */; let key_share = /* completed key share */; let data_to_sign = cggmp21::DataToSign::digest::(b"data to be signed"); @@ -134,15 +134,15 @@ let signature = cggmp21::signing(eid, i, &parties_indexes_at_keygen, &key_share) .await?; ``` -Alternatively, you can generate presignature and use it to sign data: -1. Use `SigningBuilder::generate_presignature` to run presignature generation protocol -2. Once signing request is received, each signer issues a partial signature using +Alternatively, you can generate a presignature and later use it to sign: +1. Use `SigningBuilder::generate_presignature` to run the presignature generation protocol +2. Later, when a signing request is received, each signer issues a partial signature using `Presignature::issue_partial_signature` -3. Combine threshold amount of partial signatures using `PartialSignature::combine` to - obtain a regular signature +3. The requisite number of partial signatures can be combined using `PartialSignature::combine` to + obtain a full signature **Never reuse presignatures!** If you use the same presignature to sign two different messages, -it leaks private key to anyone who can observe the signatures. +it leaks information about key shares to anyone who can observe the signatures. ## HD wallets support Library supports non-hardened deterministic key derivation based on [slip10] standard (compatible @@ -167,14 +167,14 @@ Such use-cases contradict to nature of MPC so we don't include those primitives However, you may opt for them by enabling `spof` feature, then you can use `trusted_dealer` for key import and `key_share::reconstruct_secret_key` for key export. -## Implementation vs CGGMP21 paper differences -Original CGGMP21 paper only defines non-threshold (n-out-of-n) protocol. To support threshold -(t-out-of-n) signing, we defined our own CGGMP21-like key generation and threshold signing -protocol which works based on original non-threshold signing protocol. However, we keep both -threshold and non-threshold versions of the protocols in the crate, so if you opt for non-threshold -protocol, you will be running original protocol defined in the paper. +## Differences between the implementation and [CGGMP21] +[CGGMP21] only defines a full threshold protocol. To support general thresholds, +we defined our own CGGMP21-like key generation and threshold signing +protocols. However, we keep both +general threshold and full threshold versions of the protocols in the crate, so if you opt for the full threshold +protocol, you will be running the original protocol defined in the paper. -There are other differences in the implementation compared to original paper (mostly typo fixes), +There are other (small) differences in the implementation compared to the original paper (mostly typo fixes); they are all documented in [the spec]. [CGGMP21]: https://ia.cr/2021/060 diff --git a/cggmp21/src/lib.rs b/cggmp21/src/lib.rs index db82939..493ce02 100644 --- a/cggmp21/src/lib.rs +++ b/cggmp21/src/lib.rs @@ -109,7 +109,7 @@ //! //! ### Auxiliary info generation //! After key generation, all signers need to take part in generation of auxiliary information. Make sure all signers occupy exactly -//! the same indices as at keygen. +//! the same indexes as at keygen. //! ```rust,no_run //! # async fn doc() -> Result<(), cggmp21::KeyRefreshError> { //! # type Msg = cggmp21::key_refresh::msg::aux_only::Msg;