Skip to content

Add Key Rotation to GG18 examples #172

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
49 changes: 44 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ The following steps are for setup, key generation with `n` parties and signing w

### Setup

1. We use shared state machine architecture (see [white city](https://github.com/KZen-networks/white-city)). The parameters `parties` and `threshold` can be configured by changing the file: `param`. a keygen will run with `parties` parties and signing will run with any subset of `threshold + 1` parties. `param` file should be located in the same path of the client software.
1. We use shared state machine architecture (see [white city](https://github.com/KZen-networks/white-city)). The parameters `parties` and `threshold` can be configured by changing the file: `params.json`. A keygen will run with `parties` parties and signing will run with any subset of `threshold + 1` parties. `params.json` file should be located in the same path of the client software.

2. Install [Rust](https://rustup.rs/). Run `cargo build --release --examples` (it will build into `/target/release/examples/`)

Expand All @@ -107,6 +107,23 @@ The following steps are for setup, key generation with `n` parties and signing w

run `gg18_keygen_client` as follows: `./gg18_keygen_client http://127.0.0.1:8000 keys.store`. Replace IP and port with the ones configured in setup. Once `n` parties join the application will run till finish. At the end each party will get a local keys file `keys.store` (change filename in command line). This contains secret and public data of the party after keygen. The file therefore should remain private.

#### Example

First edit the file `params.json` and save it with this content:
`{"parties":"3", "threshold":"2"}
`

Then run manager:

`./gg18_sm_manager`

Then open 3 other terminal tabs for each party. Run:

1. `./gg18_keygen_client http://127.0.0.1:8000 local-share1.json`
2. `./gg18_keygen_client http://127.0.0.1:8000 local-share2.json`
3. `./gg18_keygen_client http://127.0.0.1:8000 local-share3.json`


### Sign

Run `./gg18_sign_client`. The application should be in the same folder as the `keys.store` file (or custom filename generated in keygen). the application takes three arguments: `IP:port` as in keygen, `filename` and message to be signed: `./gg18_sign_client http://127.0.0.1:8001 keys.store "KZen Networks"`. The same message should be used by all signers. Once `t+1` parties join the protocol will run and will output to screen signature (R,s).
Expand All @@ -119,10 +136,32 @@ Simply put, the safest way to use the signing binary is to just always hex your

#### Example
To sign the message `hello world`, first calculate its hexadecimal representation. This yields the `68656c6c6f20776f726c64`.
Then, run:
```bash
./gg18_sign_client http://127.0.0.1:8000 keys.store "68656c6c6f20776f726c64"
```
Then, run the commands below in 3 terminal tabs:

1. `./gg18_sign_client http://127.0.0.1:8000 local-share1.json "68656c6c6f20776f726c64"`
2. `./gg18_sign_client http://127.0.0.1:8000 local-share2.json "68656c6c6f20776f726c64"`
3. `./gg18_sign_client http://127.0.0.1:8000 local-share3.json "68656c6c6f20776f726c64"`

### Run Key Rotation

This version supports [Key Rotation](https://academy.binance.com/en/articles/threshold-signatures-explained#header-6) for GG18. It takes previously
generated local key files and replaces them with new key files (new secret shares)
without changing the public key.

Assuming that you have executed gg18_keygen_client example above, here is an example how to
perform key rotation on its generated key files:

First run manager as you did for keygen. Then open 3 terminal tabs for each party. Run:

1. `./gg18_keygen_client http://127.0.0.1:8000 local-share1.json rotate`
2. `./gg18_keygen_client http://127.0.0.1:8000 local-share2.json rotate`
3. `./gg18_keygen_client http://127.0.0.1:8000 local-share3.json rotate`

Each command corresponds to one party. Once key rotation is completed, you'll have 3 new files:
`local-share1.json`, `local-share2.json`, `local-share3.json` corresponding to rotated local secret
share of each party. The original shares of each party will be saved in three
files like `local-share1.json.rotation.backup`, `local-share2.json.rotation.backup`,
`local-share3.json.rotation.backup` respectively.

### GG18 demo

Expand Down
34 changes: 29 additions & 5 deletions examples/gg18_keygen_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ use curv::{
elliptic::curves::{secp256_k1::Secp256k1, Point, Scalar},
BigInt,
};
use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::{
KeyGenBroadcastMessage1, KeyGenDecommitMessage1, Keys, Parameters,
};
use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::{KeyGenBroadcastMessage1, KeyGenDecommitMessage1, Keys, Parameters, SharedKeys};
use paillier::EncryptionKey;
use reqwest::Client;
use sha2::Sha256;
Expand All @@ -25,9 +23,13 @@ use common::{
};

fn main() {
if env::args().nth(3).is_some() {
if env::args().nth(4).is_some() {
panic!("too many arguments")
}
let mut rotate = false;
if env::args().nth(3).is_some() {
rotate = env::args().nth(3).unwrap() == "rotate";
}
if env::args().nth(2).is_none() {
panic!("too few arguments")
}
Expand All @@ -53,7 +55,29 @@ fn main() {
};
println!("number: {:?}, uuid: {:?}", party_num_int, uuid);

let party_keys = Keys::create(party_num_int);
let party_keys = if rotate {
// read key file
let key_file_path = env::args().nth(2).unwrap();
let key_file_data = fs::read_to_string(key_file_path.clone())
.expect("Unable to load keys, for key rotation the key file should be generated and readable");

let (party_keys, _shared_keys_old, _party_id, _vss_scheme_vec_old, _paillier_key_vector_old, _y_sum_old): (
Keys,
SharedKeys,
u16,
Vec<VerifiableSS<Secp256k1>>,
Vec<EncryptionKey>,
Point<Secp256k1>,
) = serde_json::from_str(&key_file_data).unwrap();

let result = fs::copy(key_file_path.clone(), key_file_path + ".rotation.backup");
result.expect("Unable to create backup from key file");
party_keys
}
else {
Keys::create(party_num_int)
};

let (bc_i, decom_i) = party_keys.phase1_broadcast_phase3_proof_of_correct_key();

// send commitment to ephemeral public keys, get round 1 commitments of other parties
Expand Down