Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update documentation #51

Merged
merged 6 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 100 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,129 +2,162 @@
[![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
* Threshold (i.e., t-out-of-n) and non-threshold (i.e., n-out-of-n) key generation
* (3+1)-round general threshold and non-threshold signing
* Auxiliary info generation protocol
* Key refresh for non-threshold keys
* HD-wallets support based on [slip10] standard (compatible with [bip32]) \
Requires `hd-wallets` feature

A self-contained description of the protocols we implemented is available [here][the spec].

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 threshold keys (i.e., t-out-of-n)
* Identifiable abort
* 5+1 rounds signing protocol
* The (5+1)-round signing protocol

> About notion of threshold and non-threshold keys: originally, CGGMP21 paper does not have support of
arbitrary `t` and only works with non-threshold n-out-of-n keys. We have added support of arbitrary
threshold $2 \le t \le n$, however, we made it possible to opt out therhsoldness so original CGGMP21
protocol can be carried out if needed.

## 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
of `incoming` messages and sink of `outgoing` messages:
The most essential part of running an interactive protocol is to define how parties can communicate with
each other. Our `cggmp21` library is agnostic to the network layer and only requires you to provide two
things: a stream of incoming messages and a sink for outgoing messages, i.e.:

```rust
let incoming: impl Stream<Item = Result<Incoming<Msg>>>;
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.

Then, construct a `round_based::MpcParty`:
Once you have that, you can construct an [MpcParty][round_based::MpcParty]:
```rust
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.
The concrete networking implementation to use will depend heavily on the specific application.
Some applications may use libp2p; others may prefer having a central delivery server or a database
(like Redis or Postgres); some specific applications may want to communicate over a public
blockchain, and so on.

Whatever networking implementation you use, keep in mind that:

* All messages must be authenticated \
Whenever one party receives a message from another, the receiver should cryptographically
verify that the message comes from the claimed sender.
* All p2p messages must be encrypted \
Only the designated recipient should be able to read the message

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
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
#### Signer indices
Our library uses indices to uniquely refer to particular signers sharing a key. Each index `i`
is an unsigned integer `u16` with $0 \le i < n$ where `n` is the total number of parties.

[security requirements]: #security
All signers should have the same view about each others' indices. For instance, if Signer A
holds index 2, then all other signers must agree that i=2 corresponds to Signer A.

#### Security
Make sure that communication layer complies with security requirements:
* All messages sent between parties must be authenticated
* All p2p messages must be encrypted
Assuming some sort of PKI (which would anyway likely be used to ensure secure communication,
as described above), each signer has a public key that uniquely identifies that signer.
It is then possible to assign unique indices to the signers by lexicographically sorting the
signers' public keys, and letting the index of a signer be the position of that signer's public
key in the sorted list.

### 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.
Execution of our protocols requires all participants to agree on unique execution ID (aka
session identifier) that is assumed never to repeat. This string provides context separation
between different executions of the protocol to ensure that an adversary cannot replay messages
from one execution to another.

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 do MPC!

### Auxiliary info generation
In the usual flow, signers run a protocol for auxiliary-data generation before running distributed
key generation. This protocol sets up certain parameters (in particular, Paillier moduli
for each of the signers) that will be used during the signing protocols. This protocol can be
run as follows:

### Distributed Key Generation
```rust
use cggmp21::supported_curves::Secp256k1;
// 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 (0 <= i < n) */;
let n = /* amount of signers taking part in key generation */;
let t = /* threshold */;
let i = /* signer index, same as at keygen */;
let n = /* number of signers */;

let incomplete_key_share = cggmp21::keygen::<Secp256k1>(eid, i, n)
.set_threshold(t)
let aux_info = cggmp21::aux_info_gen(eid, i, n, pregenerated_primes)
.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
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
the same indexes as at keygen.
The auxiliary-data generation protocol is computationally heavy as it requires the generation
of safe primes and involves several zero-knowledge (ZK) proofs.

#### On reusability of the auxiliary data
The CGGMP21 paper assumes that new auxiliary data is generated for each secret key that is shared.
However, examination of the proof shows that this is not necessary, and a fixed group of signers
can use the same auxiliary data for the secure sharing/usage of multiple keys.

### Distributed Key Generation (DKG)
The DKG protocol involves all signers who will co-share a key. All signers need to agree on
some basic parameters including the participants' indices, the execution ID, and the
threshold value (i.e., t). The protocol can be executed as

```rust
// Primes generation can take a while
let pregenerated_primes = cggmp21::PregeneratedPrimes::generate(&mut OsRng);
use cggmp21::supported_curves::Secp256k1;

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 i = /* signer index (0 <= i < n) */;
let n = /* number of signers taking part in key generation */;
let t = /* threshold */;

let aux_info = cggmp21::aux_info_gen(eid, i, n, pregenerated_primes)
let incomplete_key_share = cggmp21::keygen::<Secp256k1>(eid, i, n)
.set_threshold(t)
.start(&mut OsRng, party)
.await?;
```

After keygen and aux info gen are done, you can make a “complete” key share that can be used for signing:
The above produces an `IncompleteKeyShare`. An incomplete key share can be saved on disk by serializing using
[`serde` crate][serde]. Treat this material appropriately as it contains sensitive information.

Assuming auxiliary-data generation has already been done (see above), you can "complete" the
key share using:

```rust
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 signers have a set of "completed" key shares, they can sign or generate presignatures.
In either case, exactly the threshold number (i.e., t) of signers must take part in the protocol.
As in the DKG protocol, each signer needs to be assigned a unique index, now in the range from 0
to t-1. But the signers also need to know which index each signer occupied at the time of 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 +167,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. A threshold 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.
the private key may be leaked.

## HD wallets support
Library supports non-hardened deterministic key derivation based on [slip10] standard (compatible
Expand All @@ -167,14 +200,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 non-threshold protocol. To support general thresholds,
we defined our own CGGMP21-like key generation and threshold signing
protocols. However, we keep both
threshold and non-threshold versions of the protocols in the crate, so if you opt for the non-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
5 changes: 5 additions & 0 deletions cggmp21-keygen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## v0.1.0

Initial release
5 changes: 5 additions & 0 deletions cggmp21/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## Version history lost to time

Everything before v0.1.1 was not recorded
Loading
Loading