mirror of
https://github.com/luxfi/multi-party-ecdsa.git
synced 2026-10-30 15:24:24 +00:00
535 lines
16 KiB
Rust
535 lines
16 KiB
Rust
#![allow(non_snake_case)]
|
|
|
|
use curv::{
|
|
arithmetic::traits::*,
|
|
cryptographic_primitives::{
|
|
proofs::sigma_correct_homomorphic_elgamal_enc::HomoELGamalProof,
|
|
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::{
|
|
Keys, LocalSignature, PartyPrivate, Phase5ADecom1, Phase5Com1, Phase5Com2, Phase5DDecom2,
|
|
SharedKeys, SignBroadcastPhase1, SignDecommitPhase1, SignKeys,
|
|
};
|
|
use multi_party_ecdsa::utilities::mta::*;
|
|
use sha2::Sha256;
|
|
|
|
use paillier::EncryptionKey;
|
|
use reqwest::Client;
|
|
use std::{env, fs, time};
|
|
|
|
mod common;
|
|
use common::{
|
|
broadcast, check_sig, poll_for_broadcasts, poll_for_p2p, postb, sendp2p, Params, PartySignup,
|
|
};
|
|
|
|
#[allow(clippy::cognitive_complexity)]
|
|
fn main() {
|
|
if env::args().nth(4).is_some() {
|
|
panic!("too many arguments")
|
|
}
|
|
if env::args().nth(3).is_none() {
|
|
panic!("too few arguments")
|
|
}
|
|
let message_str = env::args().nth(3).unwrap_or_else(|| "".to_string());
|
|
let message = match hex::decode(message_str.clone()) {
|
|
Ok(x) => x,
|
|
Err(_e) => message_str.as_bytes().to_vec(),
|
|
};
|
|
let message = &message[..];
|
|
let client = Client::new();
|
|
// delay:
|
|
let delay = time::Duration::from_millis(25);
|
|
// read key file
|
|
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): (
|
|
Keys,
|
|
SharedKeys,
|
|
u16,
|
|
Vec<VerifiableSS<Secp256k1>>,
|
|
Vec<EncryptionKey>,
|
|
Point<Secp256k1>,
|
|
) = 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::<u16>().unwrap();
|
|
|
|
//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(),
|
|
);
|
|
|
|
let mut j = 0;
|
|
let mut signers_vec: Vec<u16> = 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 private = PartyPrivate::set_private(party_keys.clone(), shared_keys);
|
|
|
|
let sign_keys = SignKeys::create(
|
|
&private,
|
|
&vss_scheme_vec[usize::from(signers_vec[usize::from(party_num_int - 1)])],
|
|
signers_vec[usize::from(party_num_int - 1)],
|
|
&signers_vec,
|
|
);
|
|
|
|
let xi_com_vec = Keys::get_commitments_to_xi(&vss_scheme_vec);
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
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,
|
|
THRESHOLD + 1,
|
|
delay,
|
|
"round1",
|
|
uuid.clone(),
|
|
);
|
|
|
|
let mut j = 0;
|
|
let mut bc1_vec: Vec<SignBroadcastPhase1> = Vec::new();
|
|
let mut m_a_vec: Vec<MessageA> = 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 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);
|
|
m_a_vec.push(m_a_party_j);
|
|
|
|
j += 1;
|
|
// }
|
|
}
|
|
}
|
|
assert_eq!(signers_vec.len(), bc1_vec.len());
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
let mut m_b_gamma_send_vec: Vec<MessageB> = Vec::new();
|
|
let mut beta_vec: Vec<Scalar<Secp256k1>> = Vec::new();
|
|
let mut m_b_w_send_vec: Vec<MessageB> = Vec::new();
|
|
let mut ni_vec: Vec<Scalar<Secp256k1>> = Vec::new();
|
|
let mut j = 0;
|
|
for i in 1..THRESHOLD + 2 {
|
|
if i != party_num_int {
|
|
let (m_b_gamma, beta_gamma, _, _) = MessageB::b(
|
|
&sign_keys.gamma_i,
|
|
&paillier_key_vector[usize::from(signers_vec[usize::from(i - 1)])],
|
|
m_a_vec[j].clone(),
|
|
&[],
|
|
)
|
|
.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();
|
|
m_b_gamma_send_vec.push(m_b_gamma);
|
|
m_b_w_send_vec.push(m_b_w);
|
|
beta_vec.push(beta_gamma);
|
|
ni_vec.push(beta_wi);
|
|
j += 1;
|
|
}
|
|
}
|
|
|
|
let mut j = 0;
|
|
for i in 1..THRESHOLD + 2 {
|
|
if i != party_num_int {
|
|
assert!(sendp2p(
|
|
&client,
|
|
party_num_int,
|
|
i,
|
|
"round2",
|
|
serde_json::to_string(&(m_b_gamma_send_vec[j].clone(), m_b_w_send_vec[j].clone()))
|
|
.unwrap(),
|
|
uuid.clone()
|
|
)
|
|
.is_ok());
|
|
j += 1;
|
|
}
|
|
}
|
|
|
|
let round2_ans_vec = poll_for_p2p(
|
|
&client,
|
|
party_num_int,
|
|
THRESHOLD + 1,
|
|
delay,
|
|
"round2",
|
|
uuid.clone(),
|
|
);
|
|
|
|
let mut m_b_gamma_rec_vec: Vec<MessageB> = Vec::new();
|
|
let mut m_b_w_rec_vec: Vec<MessageB> = Vec::new();
|
|
|
|
for i in 0..THRESHOLD {
|
|
// if signers_vec.contains(&(i as usize)) {
|
|
let (m_b_gamma_i, m_b_w_i): (MessageB, MessageB) =
|
|
serde_json::from_str(&round2_ans_vec[i as usize]).unwrap();
|
|
m_b_gamma_rec_vec.push(m_b_gamma_i);
|
|
m_b_w_rec_vec.push(m_b_w_i);
|
|
// }
|
|
}
|
|
|
|
let mut alpha_vec: Vec<Scalar<Secp256k1>> = Vec::new();
|
|
let mut miu_vec: Vec<Scalar<Secp256k1>> = Vec::new();
|
|
|
|
let mut j = 0;
|
|
for i in 1..THRESHOLD + 2 {
|
|
if i != party_num_int {
|
|
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)
|
|
.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)
|
|
.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)])],
|
|
signers_vec[usize::from(i - 1)],
|
|
&signers_vec,
|
|
);
|
|
assert_eq!(m_b.b_proof.pk, g_w_i);
|
|
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<Scalar<Secp256k1>> = Vec::new();
|
|
format_vec_from_reads(
|
|
&round3_ans_vec,
|
|
party_num_int as usize,
|
|
delta_i,
|
|
&mut delta_vec,
|
|
);
|
|
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,
|
|
"round4",
|
|
uuid.clone(),
|
|
);
|
|
|
|
let mut decommit_vec: Vec<SignDecommitPhase1> = 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 b_proof_vec = (0..m_b_gamma_rec_vec.len())
|
|
.map(|i| &m_b_gamma_rec_vec[i].b_proof)
|
|
.collect::<Vec<&DLogProof<Secp256k1, Sha256>>>();
|
|
let R = SignKeys::phase4(&delta_inv, &b_proof_vec, decommit_vec, &bc1_vec)
|
|
.expect("bad gamma_i decommit");
|
|
|
|
// 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);
|
|
let local_sig =
|
|
LocalSignature::phase5_local_sig(&sign_keys.k_i, &message_bn, &R, &sigma, &y_sum);
|
|
|
|
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,
|
|
"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<Phase5Com1> = Vec::new();
|
|
format_vec_from_reads(
|
|
&round5_ans_vec,
|
|
party_num_int as usize,
|
|
phase5_com,
|
|
&mut commit5a_vec,
|
|
);
|
|
|
|
//phase (5B) broadcast decommit and (5B) ZK proof
|
|
assert!(broadcast(
|
|
&client,
|
|
party_num_int,
|
|
"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<Secp256k1, Sha256>,
|
|
DLogProof<Secp256k1, Sha256>,
|
|
)> = 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));
|
|
let phase_5a_decomm_vec = (0..THRESHOLD)
|
|
.map(|i| decommit5a_and_elgamal_and_dlog_vec[i as usize].0.clone())
|
|
.collect::<Vec<Phase5ADecom1>>();
|
|
let phase_5a_elgamal_vec = (0..THRESHOLD)
|
|
.map(|i| decommit5a_and_elgamal_and_dlog_vec[i as usize].1.clone())
|
|
.collect::<Vec<HomoELGamalProof<Secp256k1, Sha256>>>();
|
|
let phase_5a_dlog_vec = (0..THRESHOLD)
|
|
.map(|i| decommit5a_and_elgamal_and_dlog_vec[i as usize].2.clone())
|
|
.collect::<Vec<DLogProof<Secp256k1, Sha256>>>();
|
|
let (phase5_com2, phase_5d_decom2) = local_sig
|
|
.phase5c(
|
|
&phase_5a_decomm_vec,
|
|
&commit5a_vec,
|
|
&phase_5a_elgamal_vec,
|
|
&phase_5a_dlog_vec,
|
|
&phase_5a_decom.V_i,
|
|
&R,
|
|
)
|
|
.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,
|
|
THRESHOLD + 1,
|
|
delay,
|
|
"round7",
|
|
uuid.clone(),
|
|
);
|
|
|
|
let mut commit5c_vec: Vec<Phase5Com2> = Vec::new();
|
|
format_vec_from_reads(
|
|
&round7_ans_vec,
|
|
party_num_int as usize,
|
|
phase5_com2,
|
|
&mut commit5c_vec,
|
|
);
|
|
|
|
//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,
|
|
THRESHOLD + 1,
|
|
delay,
|
|
"round8",
|
|
uuid.clone(),
|
|
);
|
|
|
|
let mut decommit5d_vec: Vec<Phase5DDecom2> = Vec::new();
|
|
format_vec_from_reads(
|
|
&round8_ans_vec,
|
|
party_num_int as usize,
|
|
phase_5d_decom2,
|
|
&mut decommit5d_vec,
|
|
);
|
|
|
|
let phase_5a_decomm_vec_includes_i = (0..=THRESHOLD)
|
|
.map(|i| {
|
|
decommit5a_and_elgamal_and_dlog_vec_includes_i[i as usize]
|
|
.0
|
|
.clone()
|
|
})
|
|
.collect::<Vec<Phase5ADecom1>>();
|
|
let s_i = local_sig
|
|
.phase5d(
|
|
&decommit5d_vec,
|
|
&commit5c_vec,
|
|
&phase_5a_decomm_vec_includes_i,
|
|
)
|
|
.expect("bad com 5d");
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
assert!(broadcast(
|
|
&client,
|
|
party_num_int,
|
|
"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<Scalar<Secp256k1>> = Vec::new();
|
|
format_vec_from_reads(&round9_ans_vec, party_num_int as usize, s_i, &mut s_i_vec);
|
|
|
|
s_i_vec.remove(usize::from(party_num_int - 1));
|
|
let sig = local_sig
|
|
.output_signature(&s_i_vec)
|
|
.expect("verification failed");
|
|
//println!("party {:?} Output Signature: \n", party_num_int);
|
|
//println!("R: {:?}", sig.r);
|
|
//println!("s: {:?} \n", sig.s);
|
|
//println!("recid: {:?} \n", sig.recid.clone());
|
|
|
|
let sign_json = serde_json::to_string(&(
|
|
"r",
|
|
BigInt::from_bytes(sig.r.to_bytes().as_ref()).to_str_radix(16),
|
|
"s",
|
|
BigInt::from_bytes(sig.s.to_bytes().as_ref()).to_str_radix(16),
|
|
))
|
|
.unwrap();
|
|
|
|
println!("sig_json: {:?}, {:?}, {:?}", BigInt::from_bytes(sig.r.to_bytes().as_ref()).to_str_radix(16), BigInt::from_bytes(sig.s.to_bytes().as_ref()).to_str_radix(16), sig.recid.clone());
|
|
|
|
|
|
// check sig against secp256k1
|
|
check_sig(&sig.r, &sig.s, &message_bn, &y_sum);
|
|
|
|
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<T>,
|
|
) {
|
|
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<PartySignup, ()> {
|
|
let key = "signup-sign".to_string();
|
|
|
|
let res_body = postb(client, "signupsign", key).unwrap();
|
|
serde_json::from_str(&res_body).unwrap()
|
|
}
|