Skip to content

Commit

Permalink
Readme should correspond to docs now
Browse files Browse the repository at this point in the history
  • Loading branch information
maurges committed Mar 8, 2024
1 parent 83877db commit ebe4ef6
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 50 deletions.
98 changes: 49 additions & 49 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,34 @@
[![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

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
Expand All @@ -38,7 +38,7 @@ let outgoing: impl Sink<Outgoing<Msg>>;
```

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.

Expand All @@ -48,60 +48,60 @@ 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
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::<Secp256k1>(eid, i, n)
.set_threshold(t)
.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)
Expand All @@ -114,17 +114,17 @@ 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
let eid = cggmp21::ExecutionId::new(b"execution id, unique per protocol execution");

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::<Sha256>(b"data to be signed");
Expand All @@ -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
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion cggmp21/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<sha2::Sha256, cggmp21::security_level::SecurityLevel128>;
Expand Down

0 comments on commit ebe4ef6

Please sign in to comment.