diff --git a/Cargo.toml b/Cargo.toml index 11cfcc3f..e499b976 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ hex = "0.4" tokio = { version = "1", default-features = false, features = ["macros"] } futures = "0.3" rocket = { version = "0.5.0-rc.1", default-features = false, features = ["json"] } -reqwest = { version = "0.9", default-features = false } +reqwest = { version = "0.10.1", default-features = false, features = ["native-tls", "json", "blocking"] } uuid = { version = "0.8", features = ["v4"] } serde_json = "1.0" rand = "0.8" @@ -84,6 +84,10 @@ name = "gg18_keygen_client" name = "common" crate-type = ["lib"] +[[example]] +name = "party_client" +crate-type = ["lib"] + [[bench]] name = "cclst_keygen" path = "benches/two_party_ecdsa/cclst_2019/keygen.rs" diff --git a/examples/common.rs b/examples/common.rs index 522cbc57..e933127d 100644 --- a/examples/common.rs +++ b/examples/common.rs @@ -1,7 +1,5 @@ #![allow(dead_code)] -use std::{env, thread, time, time::Duration}; - use aes_gcm::aead::{Aead, NewAead}; use aes_gcm::{Aes256Gcm, Nonce}; use rand::{rngs::OsRng, RngCore}; @@ -12,7 +10,6 @@ use curv::{ BigInt, }; -use reqwest::Client; use serde::{Deserialize, Serialize}; pub type Key = String; @@ -43,7 +40,7 @@ pub struct Entry { pub value: String, } -#[derive(Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Params { pub parties: String, pub threshold: String, @@ -78,124 +75,14 @@ pub fn aes_decrypt(key: &[u8], aead_pack: AEAD) -> Vec { out.unwrap() } -pub fn postb(client: &Client, path: &str, body: T) -> Option -where - T: serde::ser::Serialize, -{ - let addr = env::args() - .nth(1) - .unwrap_or_else(|| "http://127.0.0.1:8001".to_string()); - let retries = 3; - let retry_delay = time::Duration::from_millis(250); - for _i in 1..retries { - let res = client - .post(&format!("{}/{}", addr, path)) - .json(&body) - .send(); - - if let Ok(mut res) = res { - return Some(res.text().unwrap()); - } - thread::sleep(retry_delay); - } - None -} - -pub fn broadcast( - client: &Client, - party_num: u16, - round: &str, - data: String, - sender_uuid: String, -) -> Result<(), ()> { - let key = format!("{}-{}-{}", party_num, round, sender_uuid); - let entry = Entry { key, value: data }; - - let res_body = postb(client, "set", entry).unwrap(); - serde_json::from_str(&res_body).unwrap() -} - -pub fn sendp2p( - client: &Client, - party_from: u16, - party_to: u16, - round: &str, - data: String, - sender_uuid: String, -) -> Result<(), ()> { - let key = format!("{}-{}-{}-{}", party_from, party_to, round, sender_uuid); - - let entry = Entry { key, value: data }; - - let res_body = postb(client, "set", entry).unwrap(); - serde_json::from_str(&res_body).unwrap() -} - -pub fn poll_for_broadcasts( - client: &Client, - party_num: u16, - n: u16, - delay: Duration, - round: &str, - sender_uuid: String, -) -> Vec { - let mut ans_vec = Vec::new(); - for i in 1..=n { - if i != party_num { - let key = format!("{}-{}-{}", i, round, sender_uuid); - let index = Index { key }; - loop { - // add delay to allow the server to process request: - thread::sleep(delay); - let res_body = postb(client, "get", index.clone()).unwrap(); - let answer: Result = serde_json::from_str(&res_body).unwrap(); - if let Ok(answer) = answer { - ans_vec.push(answer.value); - println!("[{:?}] party {:?} => party {:?}", round, i, party_num); - break; - } - } - } - } - ans_vec -} - -pub fn poll_for_p2p( - client: &Client, - party_num: u16, - n: u16, - delay: Duration, - round: &str, - sender_uuid: String, -) -> Vec { - let mut ans_vec = Vec::new(); - for i in 1..=n { - if i != party_num { - let key = format!("{}-{}-{}-{}", i, party_num, round, sender_uuid); - let index = Index { key }; - loop { - // add delay to allow the server to process request: - thread::sleep(delay); - let res_body = postb(client, "get", index.clone()).unwrap(); - let answer: Result = serde_json::from_str(&res_body).unwrap(); - if let Ok(answer) = answer { - ans_vec.push(answer.value); - println!("[{:?}] party {:?} => party {:?}", round, i, party_num); - break; - } - } - } - } - ans_vec -} - #[allow(dead_code)] pub fn check_sig( r: &Scalar, s: &Scalar, msg: &BigInt, pk: &Point, -) { +) +{ use secp256k1::{verify, Message, PublicKey, PublicKeyFormat, Signature}; let raw_msg = BigInt::to_bytes(msg); diff --git a/examples/gg18_keygen_client.rs b/examples/gg18_keygen_client.rs index bc5b8f0b..b6483b78 100644 --- a/examples/gg18_keygen_client.rs +++ b/examples/gg18_keygen_client.rs @@ -4,168 +4,241 @@ /// 2: cargo run from PARTIES number of terminals use curv::{ arithmetic::traits::Converter, - cryptographic_primitives::{ - proofs::sigma_dlog::DLogProof, secret_sharing::feldman_vss::VerifiableSS, - }, 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 std::{env, fs, time::Duration}; +use curv::cryptographic_primitives::proofs::sigma_dlog::DLogProof; +use curv::cryptographic_primitives::secret_sharing::feldman_vss::VerifiableSS; +use curv::elliptic::curves::Curve; use sha2::Sha256; -use std::{env, fs, time}; mod common; use common::{ - aes_decrypt, aes_encrypt, broadcast, poll_for_broadcasts, poll_for_p2p, postb, sendp2p, Params, - PartySignup, AEAD, AES_KEY_BYTES_LEN, + aes_decrypt, aes_encrypt, Params, + AEAD, AES_KEY_BYTES_LEN, }; +mod party_client; +use party_client::PartyClient; +use crate::party_client::ClientPurpose; + +#[derive(Clone, Debug)] +struct GG18KeygenArguments { + address: String, + delay: Duration, + tn_params: Params, +} + +#[derive(Clone, Debug)] +struct Round1Result { + party_keys: Keys, + bc1_vec: Vec, + decommit_i: KeyGenDecommitMessage1 +} + fn main() { + let arguments = parse_keygen_arguments(); + + //Instantiates a party client and performs signup for the given purpose: + let client = PartyClient::new( + ClientPurpose::Keygen, + Secp256k1::CURVE_NAME, + arguments.address, + arguments.delay, + arguments.tn_params.clone() + ); + + let tn_params = arguments.tn_params.clone(); + let THRESHOLD = tn_params.threshold.parse::().unwrap(); + let PARTIES = tn_params.parties.parse::().unwrap(); + + let params = Parameters { + threshold: THRESHOLD, + share_count: PARTIES, + }; + + let round1_result = run_round1( + client.clone(), + PARTIES + ); + + let (public_key, + vss_scheme_vec, + secret_shares, + enc_keys, + point_vec + ) = run_round2( + client.clone(), + PARTIES, + round1_result.clone(), + params.clone(), + ); + + ////////////////////////////////////////////////////////////////////////////// + let party_shares = run_round3( + client.clone(), + PARTIES, + enc_keys, + secret_shares + ); + + // round 4: send vss commitments and collect them + let (shared_keys, dlog_proof) = run_round4( + client.clone(), + PARTIES, + vss_scheme_vec.clone(), + round1_result.party_keys.clone(), + params.clone(), + point_vec.clone(), + &party_shares[..] + ); + + // round 5: + let paillier_key_vec = run_round5( + client.clone(), + PARTIES, + dlog_proof, + params, + point_vec, + round1_result.bc1_vec + ); + + //save key to file: + let keygen_json = serde_json::to_string(&( + round1_result.party_keys, + shared_keys, + client.party_number, + vss_scheme_vec, + paillier_key_vec, + public_key, + )) + .unwrap(); + fs::write(env::args().nth(2).unwrap(), keygen_json).expect("Unable to save !"); +} + +fn parse_keygen_arguments() -> GG18KeygenArguments { if env::args().nth(3).is_some() { panic!("too many arguments") } if env::args().nth(2).is_none() { panic!("too few arguments") } + let address = env::args() + .nth(1) + .unwrap_or_else(|| "http://127.0.0.1:8001".to_string()); + //read parameters: let data = fs::read_to_string("params.json") .expect("Unable to read params, make sure config file is present in the same folder "); - let params: Params = serde_json::from_str(&data).unwrap(); - let PARTIES: u16 = params.parties.parse::().unwrap(); - let THRESHOLD: u16 = params.threshold.parse::().unwrap(); - - let client = Client::new(); - + let tn_params: Params = serde_json::from_str(&data).unwrap(); + // delay: - let delay = time::Duration::from_millis(25); - let params = Parameters { - threshold: THRESHOLD, - share_count: PARTIES, - }; + let delay = Duration::from_millis(25); - //signup: - let (party_num_int, uuid) = match signup(&client).unwrap() { - PartySignup { number, uuid } => (number, uuid), - }; - println!("number: {:?}, uuid: {:?}", party_num_int, uuid); + GG18KeygenArguments { + address, + delay, + tn_params, + } +} - let party_keys = Keys::create(party_num_int); - let (bc_i, decom_i) = party_keys.phase1_broadcast_phase3_proof_of_correct_key(); +fn run_round1(client: PartyClient, PARTIES: u16) -> Round1Result { + let party_keys = Keys::create(client.party_number); + let (bc_i, decommit_i) = party_keys.phase1_broadcast_phase3_proof_of_correct_key(); // send commitment to ephemeral public keys, get round 1 commitments of other parties - assert!(broadcast( - &client, - party_num_int, - "round1", - serde_json::to_string(&bc_i).unwrap(), - uuid.clone() - ) - .is_ok()); - let round1_ans_vec = poll_for_broadcasts( - &client, - party_num_int, + let bc1_vec = client.exchange_data( PARTIES, - delay, "round1", - uuid.clone(), + bc_i ); - let mut bc1_vec = round1_ans_vec - .iter() - .map(|m| serde_json::from_str::(m).unwrap()) - .collect::>(); - - bc1_vec.insert(party_num_int as usize - 1, bc_i); + Round1Result { + party_keys, + bc1_vec, + decommit_i + } +} +fn run_round2( + client: PartyClient, + PARTIES: u16, + round1_result: Round1Result, + params: Parameters, +) -> ( + Point, VerifiableSS, Vec>, Vec>, Vec> +) +{ // send ephemeral public keys and check commitments correctness - assert!(broadcast( - &client, - party_num_int, - "round2", - serde_json::to_string(&decom_i).unwrap(), - uuid.clone() - ) - .is_ok()); - let round2_ans_vec = poll_for_broadcasts( - &client, - party_num_int, - PARTIES, - delay, - "round2", - uuid.clone(), - ); + let decommit_vector: Vec = client.exchange_data(PARTIES, "round2", round1_result.decommit_i); + + let point_vec: Vec> = decommit_vector + .iter() + .map(|x| x.clone().y_i) + .collect(); - let mut j = 0; - let mut point_vec: Vec> = Vec::new(); - let mut decom_vec: Vec = Vec::new(); let mut enc_keys: Vec> = Vec::new(); for i in 1..=PARTIES { - if i == party_num_int { - point_vec.push(decom_i.y_i.clone()); - decom_vec.push(decom_i.clone()); - } else { - let decom_j: KeyGenDecommitMessage1 = serde_json::from_str(&round2_ans_vec[j]).unwrap(); - point_vec.push(decom_j.y_i.clone()); - decom_vec.push(decom_j.clone()); - let key_bn: BigInt = (decom_j.y_i.clone() * party_keys.u_i.clone()) + if i != client.party_number { + let decommit_j: KeyGenDecommitMessage1 = decommit_vector[(i-1) as usize].clone(); + let key_bn: BigInt = (decommit_j.y_i.clone() * round1_result.party_keys.u_i.clone()) .x_coord() .unwrap(); let key_bytes = BigInt::to_bytes(&key_bn); let mut template: Vec = vec![0u8; AES_KEY_BYTES_LEN - key_bytes.len()]; template.extend_from_slice(&key_bytes[..]); enc_keys.push(template); - j += 1; } } let (head, tail) = point_vec.split_at(1); - let y_sum = tail.iter().fold(head[0].clone(), |acc, x| acc + x); + let public_key = tail.iter().fold(head[0].clone(), |acc, x| acc + x); - let (vss_scheme, secret_shares, _index) = party_keys + let (vss_scheme, secret_shares, _index) = round1_result.party_keys .phase1_verify_com_phase3_verify_correct_key_phase2_distribute( - ¶ms, &decom_vec, &bc1_vec, + ¶ms, &decommit_vector, &round1_result.bc1_vec, ) .expect("invalid key"); - ////////////////////////////////////////////////////////////////////////////// + (public_key, vss_scheme, secret_shares, enc_keys, point_vec) +} +fn run_round3( + client: PartyClient, + PARTIES: u16, + enc_keys: Vec>, + secret_shares: Vec> +) -> Vec> +{ let mut j = 0; for (k, i) in (1..=PARTIES).enumerate() { - if i != party_num_int { + if i != client.party_number { // prepare encrypted ss for party i: let key_i = &enc_keys[j]; let plaintext = BigInt::to_bytes(&secret_shares[k].to_bigint()); let aead_pack_i = aes_encrypt(key_i, &plaintext); - assert!(sendp2p( - &client, - party_num_int, + assert!(client.sendp2p( i, "round3", serde_json::to_string(&aead_pack_i).unwrap(), - uuid.clone() ) - .is_ok()); + .is_ok()); j += 1; } } - let round3_ans_vec = poll_for_p2p( - &client, - party_num_int, + let round3_ans_vec = client.poll_for_p2p( PARTIES, - delay, "round3", - uuid.clone(), ); let mut j = 0; let mut party_shares: Vec> = Vec::new(); for i in 1..=PARTIES { - if i == party_num_int { + if i == client.party_number { party_shares.push(secret_shares[(i - 1) as usize].clone()); } else { let aead_pack: AEAD = serde_json::from_str(&round3_ans_vec[j]).unwrap(); @@ -179,93 +252,59 @@ fn main() { } } - // round 4: send vss commitments - assert!(broadcast( - &client, - party_num_int, - "round4", - serde_json::to_string(&vss_scheme).unwrap(), - uuid.clone() - ) - .is_ok()); - let round4_ans_vec = poll_for_broadcasts( - &client, - party_num_int, + party_shares +} + +fn run_round4( + client: PartyClient, + PARTIES: u16, + vss_scheme: VerifiableSS, + party_keys: Keys, + params: Parameters, + point_vec: Vec>, + party_shares: &[Scalar] +) -> (SharedKeys, DLogProof) +{ + let vss_scheme_vec = client.exchange_data( PARTIES, - delay, "round4", - uuid.clone(), + vss_scheme ); - let mut j = 0; - let mut vss_scheme_vec: Vec> = Vec::new(); - for i in 1..=PARTIES { - if i == party_num_int { - vss_scheme_vec.push(vss_scheme.clone()); - } else { - let vss_scheme_j: VerifiableSS = - serde_json::from_str(&round4_ans_vec[j]).unwrap(); - vss_scheme_vec.push(vss_scheme_j); - j += 1; - } - } - let (shared_keys, dlog_proof) = party_keys .phase2_verify_vss_construct_keypair_phase3_pok_dlog( ¶ms, &point_vec, &party_shares, &vss_scheme_vec, - party_num_int, + client.party_number, ) .expect("invalid vss"); - // round 5: send dlog proof - assert!(broadcast( - &client, - party_num_int, + (shared_keys, dlog_proof) +} + +fn run_round5( + client: PartyClient, + PARTIES: u16, + dlog_proof: DLogProof, + params: Parameters, + point_vec: Vec>, + bc1_vec: Vec +) -> Vec +{ + //send dlog proof and collect them + let dlog_proof_vec = client.exchange_data( + PARTIES, "round5", - serde_json::to_string(&dlog_proof).unwrap(), - uuid.clone() - ) - .is_ok()); - let round5_ans_vec = - poll_for_broadcasts(&client, party_num_int, PARTIES, delay, "round5", uuid); + dlog_proof + ); - let mut j = 0; - let mut dlog_proof_vec: Vec> = Vec::new(); - for i in 1..=PARTIES { - if i == party_num_int { - dlog_proof_vec.push(dlog_proof.clone()); - } else { - let dlog_proof_j: DLogProof = - serde_json::from_str(&round5_ans_vec[j]).unwrap(); - dlog_proof_vec.push(dlog_proof_j); - j += 1; - } - } Keys::verify_dlog_proofs(¶ms, &dlog_proof_vec, &point_vec).expect("bad dlog proof"); - //save key to file: let paillier_key_vec = (0..PARTIES) .map(|i| bc1_vec[i as usize].e.clone()) .collect::>(); - let keygen_json = serde_json::to_string(&( - party_keys, - shared_keys, - party_num_int, - vss_scheme_vec, - paillier_key_vec, - y_sum, - )) - .unwrap(); - fs::write(env::args().nth(2).unwrap(), keygen_json).expect("Unable to save !"); -} - -pub fn signup(client: &Client) -> Result { - let key = "signup-keygen".to_string(); - - let res_body = postb(client, "signupkeygen", key).unwrap(); - serde_json::from_str(&res_body).unwrap() -} + paillier_key_vec +} \ No newline at end of file diff --git a/examples/gg18_sign_client.rs b/examples/gg18_sign_client.rs index 86e7ed2a..3717c3c4 100644 --- a/examples/gg18_sign_client.rs +++ b/examples/gg18_sign_client.rs @@ -9,22 +9,44 @@ use curv::{ elliptic::curves::{secp256_k1::Secp256k1, Point, Scalar}, BigInt, }; -use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::{ - Keys, LocalSignature, PartyPrivate, Phase5ADecom1, Phase5Com1, Phase5Com2, Phase5DDecom2, - SharedKeys, SignBroadcastPhase1, SignDecommitPhase1, SignKeys, -}; +use multi_party_ecdsa::protocols::multi_party_ecdsa::gg_2018::party_i::{Keys, LocalSignature, PartyPrivate, Phase5ADecom1, Phase5Com1, Phase5Com2, Phase5DDecom2, SharedKeys, SignatureRecid, SignBroadcastPhase1, SignDecommitPhase1, SignKeys}; use multi_party_ecdsa::utilities::mta::*; use sha2::Sha256; use paillier::EncryptionKey; -use reqwest::Client; use std::{env, fs, time}; +use curv::elliptic::curves::Curve; mod common; use common::{ - broadcast, check_sig, poll_for_broadcasts, poll_for_p2p, postb, sendp2p, Params, PartySignup, + check_sig, Params, }; +mod party_client; +use crate::party_client::{ClientPurpose, PartyClient}; + +#[derive(Clone, Debug)] +struct StoreFileData { + party_keys: Keys, + shared_keys: SharedKeys, + party_id:u16, + vss_scheme_vec: Vec>, + paillier_key_vector: Vec, + public_key: Point +} + +#[derive(Clone, Debug)] +struct Round1Result { + sign_keys: SignKeys, + bc1_vec: Vec, + xi_com_vec: Vec>, + decommit: SignDecommitPhase1, + m_b_w_send_vec: Vec, + m_b_gamma_send_vec: Vec, + beta_vec: Vec>, + ni_vec: Vec> +} + #[allow(clippy::cognitive_complexity)] fn main() { if env::args().nth(4).is_some() { @@ -39,13 +61,88 @@ fn main() { Err(_e) => message_str.as_bytes().to_vec(), }; let message = &message[..]; - let client = Client::new(); + let address = env::args() + .nth(1) + .unwrap_or_else(|| "http://127.0.0.1:8001".to_string()); // delay: let delay = time::Duration::from_millis(25); + //read parameters: + let data = fs::read_to_string("params.json") + .expect("Unable to read params, make sure config file is present in the same folder "); + let params: Params = serde_json::from_str(&data).unwrap(); + let THRESHOLD = params.threshold.parse::().unwrap(); + + let client = PartyClient::new( + ClientPurpose::Sign, + Secp256k1::CURVE_NAME, + address, + delay, + params + ); + // read key file + let store_file_data: StoreFileData = read_store_file(); + + // round 0: collect signers IDs + let signers_vec: Vec = client.exchange_data(THRESHOLD+1, "round0", store_file_data.party_id - 1); + + let round1_result = run_round1( + client.clone(), + store_file_data.clone(), + signers_vec.clone(), + THRESHOLD + ); + + let (alpha_vec, miu_vec, m_b_gamma_rec_vec) = run_round2( + client.clone(), + THRESHOLD, + round1_result.clone(), + signers_vec.clone(), + store_file_data.clone(), + + ); + + ////////////////////////////////////////////////////////////////////////////// + let delta_inv = run_round3( + client.clone(), + round1_result.clone(), + alpha_vec, + THRESHOLD + ); + + ////////////////////////////////////////////////////////////////////////////// + // decommit to gamma_i + let R = run_round4( + client.clone(), + THRESHOLD, + round1_result.clone(), + delta_inv, + m_b_gamma_rec_vec + ); + + // we assume the message is already hashed (by the signer). + let message_bn = BigInt::from_bytes(message); + + let sig = run_round5( + client.clone(), + THRESHOLD, + message_bn.clone(), + round1_result.clone(), + store_file_data.clone(), + R, + miu_vec + ); + + // check sig against secp256k1 + check_sig(&sig.r, &sig.s, &message_bn, &store_file_data.public_key); + + print_and_save_signature(sig, client.party_number.clone()); +} + +fn read_store_file() -> StoreFileData { let data = fs::read_to_string(env::args().nth(2).unwrap()) .expect("Unable to load keys, did you run keygen first? "); - let (party_keys, shared_keys, party_id, vss_scheme_vec, paillier_key_vector, y_sum): ( + let (party_keys, shared_keys, party_id, vss_scheme_vec, paillier_key_vector, public_key): ( Keys, SharedKeys, u16, @@ -54,47 +151,24 @@ fn main() { Point, ) = serde_json::from_str(&data).unwrap(); - //read parameters: - let data = fs::read_to_string("params.json") - .expect("Unable to read params, make sure config file is present in the same folder "); - let params: Params = serde_json::from_str(&data).unwrap(); - let THRESHOLD = params.threshold.parse::().unwrap(); + StoreFileData { + party_keys, + shared_keys, + party_id, + vss_scheme_vec, + paillier_key_vector, + public_key + } +} - //signup: - let (party_num_int, uuid) = match signup(&client).unwrap() { - PartySignup { number, uuid } => (number, uuid), - }; - println!("number: {:?}, uuid: {:?}", party_num_int, uuid); - // round 0: collect signers IDs - assert!(broadcast( - &client, - party_num_int, - "round0", - serde_json::to_string(&party_id).unwrap(), - uuid.clone() - ) - .is_ok()); - let round0_ans_vec = poll_for_broadcasts( - &client, - party_num_int, - THRESHOLD + 1, - delay, - "round0", - uuid.clone(), - ); +fn run_round1(client: PartyClient, store_file_data: StoreFileData, signers_vec:Vec, THRESHOLD: u16) -> Round1Result { + let party_keys = store_file_data.party_keys; + let shared_keys = store_file_data.shared_keys; + let vss_scheme_vec = store_file_data.vss_scheme_vec; + let paillier_key_vector = store_file_data.paillier_key_vector; - let mut j = 0; - let mut signers_vec: Vec = Vec::new(); - for i in 1..=THRESHOLD + 1 { - if i == party_num_int { - signers_vec.push(party_id - 1); - } else { - let signer_j: u16 = serde_json::from_str(&round0_ans_vec[j]).unwrap(); - signers_vec.push(signer_j - 1); - j += 1; - } - } + let party_num_int = client.party_number; let private = PartyPrivate::set_private(party_keys.clone(), shared_keys); @@ -109,39 +183,26 @@ fn main() { ////////////////////////////////////////////////////////////////////////////// let (com, decommit) = sign_keys.phase1_broadcast(); let (m_a_k, _) = MessageA::a(&sign_keys.k_i, &party_keys.ek, &[]); - assert!(broadcast( - &client, - party_num_int, - "round1", - serde_json::to_string(&(com.clone(), m_a_k)).unwrap(), - uuid.clone() - ) - .is_ok()); - let round1_ans_vec = poll_for_broadcasts( - &client, - party_num_int, + let round1_ans_vector = client.exchange_data( THRESHOLD + 1, - delay, "round1", - uuid.clone(), + (com.clone(), m_a_k.clone()) ); let mut j = 0; - let mut bc1_vec: Vec = Vec::new(); + let bc1_vec: Vec = round1_ans_vector + .iter() + .map(|x1| x1.0.clone()) + .collect(); + let mut m_a_vec: Vec = Vec::new(); for i in 1..THRESHOLD + 2 { - if i == party_num_int { - bc1_vec.push(com.clone()); - // m_a_vec.push(m_a_k.clone()); - } else { + if i != party_num_int { // if signers_vec.contains(&(i as usize)) { - let (bc1_j, m_a_party_j): (SignBroadcastPhase1, MessageA) = - serde_json::from_str(&round1_ans_vec[j]).unwrap(); - bc1_vec.push(bc1_j); + let (_bc1_j, m_a_party_j): (SignBroadcastPhase1, MessageA) = round1_ans_vector[(i-1) as usize].clone(); m_a_vec.push(m_a_party_j); - - j += 1; + j = j + 1; // } } } @@ -161,14 +222,14 @@ fn main() { m_a_vec[j].clone(), &[], ) - .unwrap(); + .unwrap(); let (m_b_w, beta_wi, _, _) = MessageB::b( &sign_keys.w_i, &paillier_key_vector[usize::from(signers_vec[usize::from(i - 1)])], m_a_vec[j].clone(), &[], ) - .unwrap(); + .unwrap(); m_b_gamma_send_vec.push(m_b_gamma); m_b_w_send_vec.push(m_b_w); beta_vec.push(beta_gamma); @@ -177,30 +238,40 @@ fn main() { } } + Round1Result { + sign_keys, + bc1_vec, + xi_com_vec, + decommit, + m_b_w_send_vec, + m_b_gamma_send_vec, + beta_vec, + ni_vec + } +} + + +fn run_round2(client: PartyClient, THRESHOLD: u16, round1_result: Round1Result, signers_vec: Vec, store_file_data: StoreFileData) -> + (Vec>, Vec>, Vec) +{ + let mut j = 0; for i in 1..THRESHOLD + 2 { - if i != party_num_int { - assert!(sendp2p( - &client, - party_num_int, + if i != client.party_number { + assert!(client.sendp2p( i, "round2", - serde_json::to_string(&(m_b_gamma_send_vec[j].clone(), m_b_w_send_vec[j].clone())) + serde_json::to_string(&(round1_result.m_b_gamma_send_vec[j].clone(), round1_result.m_b_w_send_vec[j].clone())) .unwrap(), - uuid.clone() ) - .is_ok()); + .is_ok()); j += 1; } } - let round2_ans_vec = poll_for_p2p( - &client, - party_num_int, + let round2_ans_vec = client.poll_for_p2p( THRESHOLD + 1, - delay, "round2", - uuid.clone(), ); let mut m_b_gamma_rec_vec: Vec = Vec::new(); @@ -220,21 +291,21 @@ fn main() { let mut j = 0; for i in 1..THRESHOLD + 2 { - if i != party_num_int { + if i != client.party_number { let m_b = m_b_gamma_rec_vec[j].clone(); let alpha_ij_gamma = m_b - .verify_proofs_get_alpha(&party_keys.dk, &sign_keys.k_i) + .verify_proofs_get_alpha(&store_file_data.party_keys.dk, &round1_result.sign_keys.k_i) .expect("wrong dlog or m_b"); let m_b = m_b_w_rec_vec[j].clone(); let alpha_ij_wi = m_b - .verify_proofs_get_alpha(&party_keys.dk, &sign_keys.k_i) + .verify_proofs_get_alpha(&store_file_data.party_keys.dk, &round1_result.sign_keys.k_i) .expect("wrong dlog or m_b"); alpha_vec.push(alpha_ij_gamma.0); miu_vec.push(alpha_ij_wi.0); let g_w_i = Keys::update_commitments_to_xi( - &xi_com_vec[usize::from(signers_vec[usize::from(i - 1)])], - &vss_scheme_vec[usize::from(signers_vec[usize::from(i - 1)])], + &round1_result.xi_com_vec[usize::from(signers_vec[usize::from(i - 1)])], + &store_file_data.vss_scheme_vec[usize::from(signers_vec[usize::from(i - 1)])], signers_vec[usize::from(i - 1)], &signers_vec, ); @@ -242,63 +313,31 @@ fn main() { j += 1; } } - ////////////////////////////////////////////////////////////////////////////// - let delta_i = sign_keys.phase2_delta_i(&alpha_vec, &beta_vec); - let sigma = sign_keys.phase2_sigma_i(&miu_vec, &ni_vec); - - assert!(broadcast( - &client, - party_num_int, - "round3", - serde_json::to_string(&delta_i).unwrap(), - uuid.clone() - ) - .is_ok()); - let round3_ans_vec = poll_for_broadcasts( - &client, - party_num_int, - THRESHOLD + 1, - delay, - "round3", - uuid.clone(), - ); - let mut delta_vec: Vec> = Vec::new(); - format_vec_from_reads( - &round3_ans_vec, - party_num_int as usize, - delta_i, - &mut delta_vec, - ); + + (alpha_vec, miu_vec, m_b_gamma_rec_vec) +} + +fn run_round3(client: PartyClient, round1_result: Round1Result, alpha_vec: Vec>, THRESHOLD: u16) -> Scalar { + let delta_i = round1_result.sign_keys.phase2_delta_i(&alpha_vec, &round1_result.beta_vec); + + let delta_vec: Vec> = client.exchange_data(THRESHOLD+1, "round3", delta_i); + let delta_inv = SignKeys::phase3_reconstruct_delta(&delta_vec); - ////////////////////////////////////////////////////////////////////////////// - // decommit to gamma_i - assert!(broadcast( - &client, - party_num_int, - "round4", - serde_json::to_string(&decommit).unwrap(), - uuid.clone() - ) - .is_ok()); - let round4_ans_vec = poll_for_broadcasts( - &client, - party_num_int, - THRESHOLD + 1, - delay, + delta_inv +} + +fn run_round4(client: PartyClient, THRESHOLD: u16, round1_result: Round1Result, delta_inv: Scalar, m_b_gamma_rec_vec: Vec) -> Point +{ + let mut decommit_vec: Vec = client.exchange_data( + THRESHOLD+1, "round4", - uuid.clone(), + round1_result.decommit ); - let mut decommit_vec: Vec = Vec::new(); - format_vec_from_reads( - &round4_ans_vec, - party_num_int as usize, - decommit, - &mut decommit_vec, - ); - let decomm_i = decommit_vec.remove(usize::from(party_num_int - 1)); - bc1_vec.remove(usize::from(party_num_int - 1)); + let decomm_i = decommit_vec.remove(usize::from(client.party_number - 1)); + let mut bc1_vec = round1_result.bc1_vec; + bc1_vec.remove(usize::from(client.party_number - 1)); let b_proof_vec = (0..m_b_gamma_rec_vec.len()) .map(|i| &m_b_gamma_rec_vec[i].b_proof) .collect::>>(); @@ -308,78 +347,56 @@ fn main() { // adding local g_gamma_i let R = R + decomm_i.g_gamma_i * delta_inv; - // we assume the message is already hashed (by the signer). - let message_bn = BigInt::from_bytes(message); + R +} + + +fn run_round5( + client: PartyClient, + THRESHOLD: u16, + message_bn: BigInt, + round1_result: Round1Result, + store_file_data: StoreFileData, + R: Point, + miu_vec: Vec> +) -> SignatureRecid +{ + + let party_index = usize::from(client.party_number - 1); + let sigma = round1_result.sign_keys.phase2_sigma_i(&miu_vec, &round1_result.ni_vec); + let local_sig = - LocalSignature::phase5_local_sig(&sign_keys.k_i, &message_bn, &R, &sigma, &y_sum); + LocalSignature::phase5_local_sig(&round1_result.sign_keys.k_i, &message_bn, &R, &sigma, &store_file_data.public_key); let (phase5_com, phase_5a_decom, helgamal_proof, dlog_proof_rho) = local_sig.phase5a_broadcast_5b_zkproof(); //phase (5A) broadcast commit - assert!(broadcast( - &client, - party_num_int, + let mut commit5a_vec: Vec = client.exchange_data( + THRESHOLD+1, "round5", - serde_json::to_string(&phase5_com).unwrap(), - uuid.clone() - ) - .is_ok()); - let round5_ans_vec = poll_for_broadcasts( - &client, - party_num_int, - THRESHOLD + 1, - delay, - "round5", - uuid.clone(), - ); - - let mut commit5a_vec: Vec = Vec::new(); - format_vec_from_reads( - &round5_ans_vec, - party_num_int as usize, - phase5_com, - &mut commit5a_vec, + phase5_com ); //phase (5B) broadcast decommit and (5B) ZK proof - assert!(broadcast( - &client, - party_num_int, + let mut decommit5a_and_elgamal_and_dlog_vec: Vec<( + Phase5ADecom1, + HomoELGamalProof, + DLogProof, + )> = client.exchange_data( + THRESHOLD+1, "round6", - serde_json::to_string(&( + ( phase_5a_decom.clone(), helgamal_proof.clone(), dlog_proof_rho.clone() - )) - .unwrap(), - uuid.clone() - ) - .is_ok()); - let round6_ans_vec = poll_for_broadcasts( - &client, - party_num_int, - THRESHOLD + 1, - delay, - "round6", - uuid.clone(), + ) ); - let mut decommit5a_and_elgamal_and_dlog_vec: Vec<( - Phase5ADecom1, - HomoELGamalProof, - DLogProof, - )> = Vec::new(); - format_vec_from_reads( - &round6_ans_vec, - party_num_int as usize, - (phase_5a_decom.clone(), helgamal_proof, dlog_proof_rho), - &mut decommit5a_and_elgamal_and_dlog_vec, - ); let decommit5a_and_elgamal_and_dlog_vec_includes_i = decommit5a_and_elgamal_and_dlog_vec.clone(); - decommit5a_and_elgamal_and_dlog_vec.remove(usize::from(party_num_int - 1)); - commit5a_vec.remove(usize::from(party_num_int - 1)); + decommit5a_and_elgamal_and_dlog_vec.remove(party_index); + commit5a_vec.remove(party_index); let phase_5a_decomm_vec = (0..THRESHOLD) .map(|i| decommit5a_and_elgamal_and_dlog_vec[i as usize].0.clone()) .collect::>(); @@ -401,55 +418,17 @@ fn main() { .expect("error phase5"); ////////////////////////////////////////////////////////////////////////////// - assert!(broadcast( - &client, - party_num_int, - "round7", - serde_json::to_string(&phase5_com2).unwrap(), - uuid.clone() - ) - .is_ok()); - let round7_ans_vec = poll_for_broadcasts( - &client, - party_num_int, + let commit5c_vec: Vec = client.exchange_data( THRESHOLD + 1, - delay, "round7", - uuid.clone(), - ); - - let mut commit5c_vec: Vec = Vec::new(); - format_vec_from_reads( - &round7_ans_vec, - party_num_int as usize, - phase5_com2, - &mut commit5c_vec, + phase5_com2 ); //phase (5B) broadcast decommit and (5B) ZK proof - assert!(broadcast( - &client, - party_num_int, - "round8", - serde_json::to_string(&phase_5d_decom2).unwrap(), - uuid.clone() - ) - .is_ok()); - let round8_ans_vec = poll_for_broadcasts( - &client, - party_num_int, + let decommit5d_vec: Vec = client.exchange_data( THRESHOLD + 1, - delay, "round8", - uuid.clone(), - ); - - let mut decommit5d_vec: Vec = Vec::new(); - format_vec_from_reads( - &round8_ans_vec, - party_num_int as usize, - phase_5d_decom2, - &mut decommit5d_vec, + phase_5d_decom2 ); let phase_5a_decomm_vec_includes_i = (0..=THRESHOLD) @@ -468,24 +447,22 @@ fn main() { .expect("bad com 5d"); ////////////////////////////////////////////////////////////////////////////// - assert!(broadcast( - &client, - party_num_int, + let mut s_i_vec: Vec> = client.exchange_data( + THRESHOLD + 1, "round9", - serde_json::to_string(&s_i).unwrap(), - uuid.clone() - ) - .is_ok()); - let round9_ans_vec = - poll_for_broadcasts(&client, party_num_int, THRESHOLD + 1, delay, "round9", uuid); - - let mut s_i_vec: Vec> = Vec::new(); - format_vec_from_reads(&round9_ans_vec, party_num_int as usize, s_i, &mut s_i_vec); + s_i + ); - s_i_vec.remove(usize::from(party_num_int - 1)); + s_i_vec.remove(party_index); let sig = local_sig .output_signature(&s_i_vec) .expect("verification failed"); + + sig +} + +fn print_and_save_signature(sig: SignatureRecid, party_num_int: u16) { + println!("party {:?} Output Signature: \n", party_num_int); println!("R: {:?}", sig.r); println!("s: {:?} \n", sig.s); @@ -497,35 +474,7 @@ fn main() { "s", BigInt::from_bytes(sig.s.to_bytes().as_ref()).to_str_radix(16), )) - .unwrap(); - - // check sig against secp256k1 - check_sig(&sig.r, &sig.s, &message_bn, &y_sum); + .unwrap(); fs::write("signature".to_string(), sign_json).expect("Unable to save !"); -} - -fn format_vec_from_reads<'a, T: serde::Deserialize<'a> + Clone>( - ans_vec: &'a [String], - party_num: usize, - value_i: T, - new_vec: &'a mut Vec, -) { - let mut j = 0; - for i in 1..ans_vec.len() + 2 { - if i == party_num { - new_vec.push(value_i.clone()); - } else { - let value_j: T = serde_json::from_str(&ans_vec[j]).unwrap(); - new_vec.push(value_j); - j += 1; - } - } -} - -pub fn signup(client: &Client) -> Result { - let key = "signup-sign".to_string(); - - let res_body = postb(client, "signupsign", key).unwrap(); - serde_json::from_str(&res_body).unwrap() -} +} \ No newline at end of file diff --git a/examples/party_client.rs b/examples/party_client.rs new file mode 100644 index 00000000..89b4bcfe --- /dev/null +++ b/examples/party_client.rs @@ -0,0 +1,224 @@ +/* + This file contains implementation of a client for each party in MPC applications + Copyright 2022 + Developed by: + HRezaei (https://github.com/HRezaei) +*/ +use std::{thread, time}; +use std::time::Duration; +use reqwest::blocking::Client; + +use crate::common::{Index, Params, PartySignup, Entry}; + +#[derive(Clone)] +pub struct PartyClient { + client: Client, + address: String, + uuid: String, + delay: Duration, + pub party_number: u16, +} + +pub enum ClientPurpose { + Keygen, + Sign +} + +impl ClientPurpose { + fn as_str(&self) -> &'static str { + match self { + ClientPurpose::Keygen => "keygen", + ClientPurpose::Sign => "sign" + } + } +} + +impl PartyClient { + pub fn new(purpose: ClientPurpose, curve_name: &str, address: String, delay: Duration, tn_params: Params) -> Self { + + let mut instance = Self { + client: Client::new(), + address, + delay, + uuid: "".to_string(), + party_number: 0 + }; + + //Purpose is set to segregate the sessions on the manager + let signup_path = "signup".to_owned() + &purpose.as_str(); + let (party_num_int, uuid) = match instance.signup(&signup_path, &tn_params, curve_name).unwrap() { + PartySignup { number, uuid } => (number, uuid), + }; + + println!("number: {:?}, uuid: {:?}, curve: {:?}", party_num_int, uuid, curve_name); + + instance.uuid = uuid; + instance.party_number = party_num_int; + + instance + } + + pub fn signup(&self, path:&str, params: &Params, curve_name: &str) -> Result { + let res_body = self.post_request(path, (params, curve_name)).unwrap(); + serde_json::from_str(&res_body).unwrap() + } + + pub fn post_request(&self, path: &str, body: T) -> Option + where + T: serde::ser::Serialize, + { + let address = self.address.clone(); + let retries = 3; + let retry_delay = time::Duration::from_millis(250); + for _i in 1..retries { + let url = format!("{}/{}", address, path); + let res = self.client.post(&url).json(&body).send(); + + if let Ok(res) = res { + return Some(res.text().unwrap()); + } + thread::sleep(retry_delay); + } + None + } + + pub fn broadcast( + &self, + round: &str, + data: String, + ) -> Result<(), ()> { + let party_num: u16 = self.party_number; + let sender_uuid: String = self.uuid.clone(); + let key = format!("{}-{}-{}", party_num, round, sender_uuid); + let entry = Entry { + key: key.clone(), + value: data, + }; + let res_body = self.post_request("set", entry).unwrap(); + serde_json::from_str(&res_body).unwrap() + } + + pub fn sendp2p( + &self, + party_to: u16, + round: &str, + data: String, + ) -> Result<(), ()> { + let party_from: u16 = self.party_number; + let sender_uuid: String = self.uuid.clone(); + + let key = format!("{}-{}-{}-{}", party_from, party_to, round, sender_uuid); + + let entry = Entry { + key: key.clone(), + value: data, + }; + + let res_body = self.post_request("set", entry).unwrap(); + serde_json::from_str(&res_body).unwrap() + } + + pub fn poll_for_broadcasts( + &self, + n: u16, + round: &str, + ) -> Vec { + let party_num: u16 = self.party_number; + let sender_uuid: String = self.uuid.clone(); + + let mut ans_vec = Vec::new(); + for i in 1..=n { + if i != party_num { + let key = format!("{}-{}-{}", i, round, sender_uuid); + let index = Index { key }; + loop { + // add delay to allow the server to process request: + thread::sleep(self.delay); + let res_body = self.post_request("get", index.clone()).unwrap(); + let answer: Result = serde_json::from_str(&res_body).unwrap(); + if let Ok(answer) = answer { + ans_vec.push(answer.value); + println!("[{:?}] party {:?} => party {:?}", round, i, party_num); + break; + } + } + } + } + ans_vec + } + + pub fn poll_for_p2p( + &self, + n: u16, + round: &str, + ) -> Vec { + let party_num: u16 = self.party_number; + let sender_uuid: String = self.uuid.clone(); + + let mut ans_vec = Vec::new(); + for i in 1..=n { + if i != party_num { + let key = format!("{}-{}-{}-{}", i, party_num, round, sender_uuid); + let index = Index { key }; + loop { + // add delay to allow the server to process request: + thread::sleep(self.delay); + let res_body = self.post_request("get", index.clone()).unwrap(); + let answer: Result = serde_json::from_str(&res_body).unwrap(); + if let Ok(answer) = answer { + ans_vec.push(answer.value); + println!("[{:?}] party {:?} => party {:?}", round, i, party_num); + break; + } + } + } + } + ans_vec + } + + pub fn exchange_data(&self, parties_count:u16, round: &str, data:T) -> Vec + where + T: Clone + serde::de::DeserializeOwned + serde::Serialize, + { + let party_num:u16 = self.party_number; + assert!(self.broadcast( + &round, + serde_json::to_string(&data).unwrap(), + ) + .is_ok()); + let round_ans_vec = self.poll_for_broadcasts( + parties_count, + &round, + ); + + let json_answers = &round_ans_vec.clone(); + let mut answers: Vec = Vec::new(); + PartyClient::format_vec_from_reads( + json_answers, + party_num as usize, + data, + &mut answers + ); + + answers.clone() + } + + fn format_vec_from_reads<'a, T: serde::Deserialize<'a> + Clone>( + ans_vec: &'a [String], + party_num: usize, + value_i: T, + new_vec: &'a mut Vec, + ) { + let mut j = 0; + for i in 1..ans_vec.len() + 2 { + if i == party_num { + new_vec.push(value_i.clone()); + } else { + let value_j: T = serde_json::from_str(&ans_vec[j]).unwrap(); + new_vec.push(value_j); + j += 1; + } + } + } + +} diff --git a/src/protocols/multi_party_ecdsa/gg_2018/party_i.rs b/src/protocols/multi_party_ecdsa/gg_2018/party_i.rs index 7899f311..787fb7c7 100644 --- a/src/protocols/multi_party_ecdsa/gg_2018/party_i.rs +++ b/src/protocols/multi_party_ecdsa/gg_2018/party_i.rs @@ -41,7 +41,7 @@ use crate::Error::{self, InvalidCom, InvalidKey, InvalidSS, InvalidSig}; const SECURITY: usize = 256; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Parameters { pub threshold: u16, //t pub share_count: u16, //n