diff --git a/README.md b/README.md index 0a5368b2..bed80010 100644 --- a/README.md +++ b/README.md @@ -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/`) @@ -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). @@ -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 diff --git a/examples/gg18_keygen_client.rs b/examples/gg18_keygen_client.rs index bc5b8f0b..76774393 100644 --- a/examples/gg18_keygen_client.rs +++ b/examples/gg18_keygen_client.rs @@ -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; @@ -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") } @@ -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>, + Vec, + Point, + ) = 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