diff --git a/gotham-client/Cargo.toml b/gotham-client/Cargo.toml index 75d1ece..7d9a36e 100644 --- a/gotham-client/Cargo.toml +++ b/gotham-client/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "gotham-client" version = "0.1.0" -authors = ["gbenattar "] +authors = [ + "gbenattar ", + "Oded Leiba , - pub endpoint: String, -} - -impl ClientShim { - pub fn new(endpoint: String, auth_token: Option) -> ClientShim { - let client = reqwest::Client::new(); - ClientShim { - client, - auth_token, - endpoint, - } - } -} - -#[derive(Serialize, Deserialize)] -pub struct PrivateShare { - pub id: String, - pub master_key: MasterKey2, -} - -pub fn get_master_key(client_shim: &ClientShim) -> PrivateShare { - keygen::get_master_key(&client_shim) -} - -pub fn sign( - client_shim: &ClientShim, - message: BigInt, - mk: &MasterKey2, - x_pos: BigInt, - y_pos: BigInt, - id: &String, -) -> party_one::SignatureRecid { - sign::sign(&client_shim, message, mk, x_pos, y_pos, id) -} - -#[no_mangle] -pub extern "C" fn get_client_master_key( - c_endpoint: *const c_char, - c_auth_token: *const c_char, -) -> *mut c_char { - let raw_endpoint = unsafe { CStr::from_ptr(c_endpoint) }; - let endpoint = match raw_endpoint.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding raw endpoint"), - }; - - let raw_auth_token = unsafe { CStr::from_ptr(c_auth_token) }; - let auth_token = match raw_auth_token.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding auth token"), - }; - - let client_shim = ClientShim::new(endpoint.to_string(), Some(auth_token.to_string())); - - let private_share: PrivateShare = keygen::get_master_key(&client_shim); - - let private_share_json = match serde_json::to_string(&private_share) { - Ok(share) => share, - Err(_) => panic!("Error while performing keygen to endpoint {}", endpoint), - }; - - CString::new(private_share_json.to_owned()) - .unwrap() - .into_raw() -} - -#[no_mangle] -pub extern "C" fn sign_message( - c_endpoint: *const c_char, - c_auth_token: *const c_char, - c_message_le_hex: *const c_char, - c_master_key_json: *const c_char, - c_x_pos: i32, - c_y_pos: i32, - c_id: *const c_char, -) -> *mut c_char { - let raw_endpoint = unsafe { CStr::from_ptr(c_endpoint) }; - let endpoint = match raw_endpoint.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding raw endpoint"), - }; - - let raw_auth_token = unsafe { CStr::from_ptr(c_auth_token) }; - let auth_token = match raw_auth_token.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding raw auth_token"), - }; - - let raw_message_hex = unsafe { CStr::from_ptr(c_message_le_hex) }; - let message_hex = match raw_message_hex.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding raw message_hex"), - }; - - let raw_master_key_json = unsafe { CStr::from_ptr(c_master_key_json) }; - let master_key_json = match raw_master_key_json.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding raw master_key_json"), - }; - - let raw_id = unsafe { CStr::from_ptr(c_id) }; - let id = match raw_id.to_str() { - Ok(s) => s, - Err(_) => panic!("Error while decoding raw id"), - }; - - let x: BigInt = BigInt::from(c_x_pos);; - - let y: BigInt = BigInt::from(c_y_pos); - - let client_shim = ClientShim::new(endpoint.to_string(), Some(auth_token.to_string())); - - let mk: MasterKey2 = serde_json::from_str(master_key_json).unwrap(); - - let mk_child: MasterKey2 = mk.get_child(vec![x.clone(), y.clone()]); - - let message: BigInt = serde_json::from_str(message_hex).unwrap(); - - let sig = sign::sign( - &client_shim, - message, - &mk_child, - x, - y, - &id.to_string(), - ); - - let signature_json = match serde_json::to_string(&sig) { - Ok(share) => share, - Err(_) => panic!("Error while signing to endpoint {}", endpoint), - }; - - CString::new(signature_json.to_owned()).unwrap().into_raw() -} diff --git a/gotham-client/src/ecdsa/keygen.rs b/gotham-client/src/ecdsa/keygen.rs index 31b14b8..30f48e0 100644 --- a/gotham-client/src/ecdsa/keygen.rs +++ b/gotham-client/src/ecdsa/keygen.rs @@ -15,28 +15,28 @@ use kms::chain_code::two_party as chain_code; use kms::ecdsa::two_party::*; use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::*; -use super::super::api; +use super::types::PrivateShare; use super::super::utilities::requests; +use super::super::ClientShim; + +// iOS bindings +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; const KG_PATH_PRE: &str = "ecdsa/keygen"; -pub fn get_master_key(client_shim: &api::ClientShim) -> api::PrivateShare { +pub fn get_master_key(client_shim: &ClientShim) -> PrivateShare { let start = PreciseTime::now(); - let res_body = requests::post(client_shim, &format!("{}/first", KG_PATH_PRE)).unwrap(); - let (id, kg_party_one_first_message): (String, party_one::KeyGenFirstMsg) = - serde_json::from_str(&res_body).unwrap(); + requests::post(client_shim, &format!("{}/first", KG_PATH_PRE)).unwrap(); let (kg_party_two_first_message, kg_ec_key_pair_party2) = MasterKey2::key_gen_first_message(); let body = &kg_party_two_first_message.d_log_proof; - let res_body = - requests::postb(client_shim, &format!("{}/{}/second", KG_PATH_PRE, id), body).unwrap(); - let kg_party_one_second_message: party1::KeyGenParty1Message2 = - serde_json::from_str(&res_body).unwrap(); + requests::postb(client_shim, &format!("{}/{}/second", KG_PATH_PRE, id), body).unwrap(); let key_gen_second_message = MasterKey2::key_gen_second_message( &kg_party_one_first_message, @@ -48,11 +48,8 @@ pub fn get_master_key(client_shim: &api::ClientShim) -> api::PrivateShare { let body = &party_two_second_message.pdl_first_message; - let res_body = - requests::postb(client_shim, &format!("{}/{}/third", KG_PATH_PRE, id), body).unwrap(); - let party_one_third_message: party_one::PDLFirstMessage = - serde_json::from_str(&res_body).unwrap(); + requests::postb(client_shim, &format!("{}/{}/third", KG_PATH_PRE, id), body).unwrap(); let pdl_decom_party2 = MasterKey2::key_gen_third_message(&party_two_pdl_chal); @@ -60,11 +57,8 @@ pub fn get_master_key(client_shim: &api::ClientShim) -> api::PrivateShare { let body = &party_2_pdl_second_message; - let res_body = - requests::postb(client_shim, &format!("{}/{}/fourth", KG_PATH_PRE, id), body).unwrap(); - let party_one_pdl_second_message: party_one::PDLSecondMessage = - serde_json::from_str(&res_body).unwrap(); + requests::postb(client_shim, &format!("{}/{}/fourth", KG_PATH_PRE, id), body).unwrap(); MasterKey2::key_gen_fourth_message( &party_two_pdl_chal, @@ -73,28 +67,24 @@ pub fn get_master_key(client_shim: &api::ClientShim) -> api::PrivateShare { ) .expect("pdl error party1"); - let res_body = requests::post( + let cc_party_one_first_message: Party1FirstMessage = requests::post( client_shim, &format!("{}/{}/chaincode/first", KG_PATH_PRE, id), ) .unwrap(); - let cc_party_one_first_message: Party1FirstMessage = serde_json::from_str(&res_body).unwrap(); - let (cc_party_two_first_message, cc_ec_key_pair2) = chain_code::party2::ChainCode2::chain_code_first_message(); let body = &cc_party_two_first_message.d_log_proof; - let res_body = requests::postb( + let cc_party_one_second_message: Party1SecondMessage = requests::postb( client_shim, &format!("{}/{}/chaincode/second", KG_PATH_PRE, id), body, ) .unwrap(); - let cc_party_one_second_message: Party1SecondMessage = serde_json::from_str(&res_body).unwrap(); - let cc_party_two_second_message = chain_code::party2::ChainCode2::chain_code_second_message( &cc_party_one_first_message, &cc_party_one_second_message, @@ -121,5 +111,36 @@ pub fn get_master_key(client_shim: &api::ClientShim) -> api::PrivateShare { let end = PreciseTime::now(); println!("(id: {}) Took: {}", id, start.to(end)); - api::PrivateShare { id, master_key } + PrivateShare { id, master_key } } + +#[no_mangle] +pub extern "C" fn get_client_master_key( + c_endpoint: *const c_char, + c_auth_token: *const c_char, +) -> *mut c_char { + let raw_endpoint = unsafe { CStr::from_ptr(c_endpoint) }; + let endpoint = match raw_endpoint.to_str() { + Ok(s) => s, + Err(_) => panic!("Error while decoding raw endpoint"), + }; + + let raw_auth_token = unsafe { CStr::from_ptr(c_auth_token) }; + let auth_token = match raw_auth_token.to_str() { + Ok(s) => s, + Err(_) => panic!("Error while decoding auth token"), + }; + + let client_shim = ClientShim::new(endpoint.to_string(), Some(auth_token.to_string())); + + let private_share: PrivateShare = get_master_key(&client_shim); + + let private_share_json = match serde_json::to_string(&private_share) { + Ok(share) => share, + Err(_) => panic!("Error while performing keygen to endpoint {}", endpoint), + }; + + CString::new(private_share_json.to_owned()) + .unwrap() + .into_raw() +} \ No newline at end of file diff --git a/gotham-client/src/ecdsa/mod.rs b/gotham-client/src/ecdsa/mod.rs index 0f1da6b..06e5253 100644 --- a/gotham-client/src/ecdsa/mod.rs +++ b/gotham-client/src/ecdsa/mod.rs @@ -9,3 +9,10 @@ pub mod keygen; pub mod rotate; pub mod sign; +pub mod types; +pub mod recover; + +pub use keygen::get_master_key; +pub use rotate::rotate_master_key; +pub use sign::sign; +pub use types::PrivateShare; \ No newline at end of file diff --git a/gotham-client/src/ecdsa/recover.rs b/gotham-client/src/ecdsa/recover.rs new file mode 100644 index 0000000..5f41c4f --- /dev/null +++ b/gotham-client/src/ecdsa/recover.rs @@ -0,0 +1,135 @@ +// Gotham-city +// +// Copyright 2018 by Kzen Networks (kzencorp.com) +// Gotham city is free software: you can redistribute +// it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either +// version 3 of the License, or (at your option) any later version. +// + +use curv::{FE, BigInt, GE}; +use kms::ecdsa::two_party::{MasterKey1, MasterKey2}; +use serde_json; +use centipede::juggling::segmentation::Msegmentation; +use centipede::juggling::proof_system::Helgamalsegmented; +use curv::elliptic::curves::traits::{ECScalar, ECPoint}; +use curv::arithmetic::traits::Modulo; +use curv::arithmetic::traits::Converter; +use serde_json::Error; +// iOS bindings +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +#[no_mangle] +#[allow(non_snake_case)] +pub extern "C" fn decrypt_party_one_master_key( + c_master_key_two_json: *const c_char, + c_helgamal_segmented_json: *const c_char, + c_private_key: *const c_char +) -> *mut c_char { + let segment_size = 8; // This is hardcoded on both client and server side + + let G: GE = GE::generator(); + + let party_two_master_key: MasterKey2 = serde_json::from_str( + &get_str_from_c_char(c_master_key_two_json)).unwrap(); + + let encryptions_secret_party1 : Helgamalsegmented = serde_json::from_str( + &get_str_from_c_char(c_helgamal_segmented_json)).unwrap(); + + let y_b : Result = serde_json::from_str(&get_str_from_c_char(c_private_key)); + if y_b.is_err() { + // Invalid BigInt Private key + return CString::new("").unwrap().into_raw(); + } + + let y: FE = ECScalar::from(&y_b.unwrap()); + + let r = Msegmentation::decrypt( + &encryptions_secret_party1, &G, &y, &segment_size); + + if r.is_ok() { + let party_one_master_key_recovered = party_two_master_key + .counter_master_key_from_recovered_secret(r.unwrap().clone()); + + let s = serde_json::to_string(&party_one_master_key_recovered).unwrap(); + return CString::new(s).unwrap().into_raw(); + } else { + return CString::new("").unwrap().into_raw(); + } +} + +#[no_mangle] +pub extern "C" fn get_child_mk1( + c_master_key_one_json: *const c_char, + c_x_pos: i32, + c_y_pos: i32 +) -> *mut c_char { + let party_one_master_key: MasterKey1 = serde_json::from_str( + &get_str_from_c_char(c_master_key_one_json)).unwrap(); + + let x: BigInt = BigInt::from(c_x_pos); + + let y: BigInt = BigInt::from(c_y_pos); + + let derived_mk1 = party_one_master_key.get_child(vec![x, y]); + + let derived_mk1_json = match serde_json::to_string(&derived_mk1) { + Ok(share) => share, + Err(_) => panic!("Error while get_child_mk1"), + }; + + CString::new(derived_mk1_json.to_owned()).unwrap().into_raw() +} + +#[no_mangle] +pub extern "C" fn get_child_mk2( + c_master_key_two_json: *const c_char, + c_x_pos: i32, + c_y_pos: i32 +) -> *mut c_char { + let party_two_master_key: MasterKey2 = serde_json::from_str( + &get_str_from_c_char(c_master_key_two_json)).unwrap(); + + let x: BigInt = BigInt::from(c_x_pos); + + let y: BigInt = BigInt::from(c_y_pos); + + let derived_mk2 = party_two_master_key.get_child(vec![x, y]); + + let derived_mk2_json = match serde_json::to_string(&derived_mk2) { + Ok(share) => share, + Err(_) => panic!("Error while get_child_mk1"), + }; + + CString::new(derived_mk2_json.to_owned()).unwrap().into_raw() +} + +#[no_mangle] +pub extern "C" fn construct_single_private_key( + c_mk1_x1: *const c_char, + c_mk2_x2: *const c_char +) -> *mut c_char { + let mk1_x1: BigInt = BigInt::from_hex(&get_str_from_c_char(c_mk1_x1)); + + let mk2_x2: BigInt = BigInt::from_hex(&get_str_from_c_char(c_mk2_x2)); + + let sk = BigInt::mod_mul(&mk1_x1, &mk2_x2, &FE::q()); + + let sk_json = match serde_json::to_string(&sk) { + Ok(share) => share, + Err(_) => panic!("Error while construct_single_private_key"), + }; + + CString::new(sk_json.to_owned()).unwrap().into_raw() +} + +fn get_str_from_c_char(c: *const c_char) -> String { + let raw = unsafe { CStr::from_ptr(c) }; + let s = match raw.to_str() { + Ok(s) => s, + Err(_) => panic!("Error while decoding c_char to string"), + }; + + s.to_string() +} \ No newline at end of file diff --git a/gotham-client/src/ecdsa/rotate.rs b/gotham-client/src/ecdsa/rotate.rs index 871ae0b..cf1bfb6 100644 --- a/gotham-client/src/ecdsa/rotate.rs +++ b/gotham-client/src/ecdsa/rotate.rs @@ -6,13 +6,10 @@ // License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // - -use serde_json; - -use super::super::api; -use super::super::api::PrivateShare; +use super::types::PrivateShare; use super::super::utilities::requests; use super::super::wallet; +use super::super::ClientShim; use curv::cryptographic_primitives::twoparty::coin_flip_optimal_rounds; use kms::ecdsa::two_party::MasterKey2; use kms::ecdsa::two_party::*; @@ -22,30 +19,26 @@ use std::collections::HashMap; const ROT_PATH_PRE: &str = "ecdsa/rotate"; -pub fn rotate_master_key(wallet: wallet::Wallet, client_shim: &api::ClientShim) -> wallet::Wallet { +pub fn rotate_master_key(wallet: wallet::Wallet, client_shim: &ClientShim) -> wallet::Wallet { let id = &wallet.private_share.id.clone(); - let res_body = requests::post(client_shim, &format!("{}/{}/first", ROT_PATH_PRE, id)).unwrap(); - let coin_flip_party1_first_message: coin_flip_optimal_rounds::Party1FirstMessage = - serde_json::from_str(&res_body).unwrap(); + requests::post(client_shim, &format!("{}/{}/first", ROT_PATH_PRE, id)).unwrap(); let coin_flip_party2_first_message = Rotation2::key_rotate_first_message(&coin_flip_party1_first_message); let body = &coin_flip_party2_first_message; - let res_body = requests::postb( + let (coin_flip_party1_second_message, rotation_party1_first_message): ( + coin_flip_optimal_rounds::Party1SecondMessage, + party1::RotationParty1Message1, + ) = requests::postb( client_shim, &format!("{}/{}/second", ROT_PATH_PRE, id.clone()), body, ) .unwrap(); - let (coin_flip_party1_second_message, rotation_party1_first_message): ( - coin_flip_optimal_rounds::Party1SecondMessage, - party1::RotationParty1Message1, - ) = serde_json::from_str(&res_body).unwrap(); - let random2 = Rotation2::key_rotate_second_message( &coin_flip_party1_second_message, &coin_flip_party2_first_message, @@ -65,30 +58,24 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client_shim: &api::ClientShim) let body = &rotation_party_two_first_message; - let res_body = requests::postb( + let rotation_party1_second_message: party_one::PDLFirstMessage = requests::postb( client_shim, &format!("{}/{}/third", ROT_PATH_PRE, id.clone()), body, ) .unwrap(); - let rotation_party1_second_message: party_one::PDLFirstMessage = - serde_json::from_str(&res_body).unwrap(); - let rotation_party_two_second_message = MasterKey2::rotate_second_message(&party_two_pdl_chal); let body = &rotation_party_two_second_message; - let res_body = requests::postb( + let rotation_party1_third_message: party_one::PDLSecondMessage = requests::postb( client_shim, &format!("{}/{}/fourth", ROT_PATH_PRE, id.clone()), body, ) .unwrap(); - let rotation_party1_third_message: party_one::PDLSecondMessage = - serde_json::from_str(&res_body).unwrap(); - let result_rotate_party_one_third_message = wallet.private_share.master_key.rotate_third_message( &random2, diff --git a/gotham-client/src/ecdsa/sign.rs b/gotham-client/src/ecdsa/sign.rs index 523bb07..1f3f4b9 100644 --- a/gotham-client/src/ecdsa/sign.rs +++ b/gotham-client/src/ecdsa/sign.rs @@ -4,8 +4,14 @@ use kms::ecdsa::two_party::MasterKey2; use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::party_one; use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::party_two; -use super::super::api; use super::super::utilities::requests; +use super::super::utilities::error_to_c_string; +use super::super::Result; +use super::super::ClientShim; + +// iOS bindings +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; #[derive(Serialize, Deserialize, Debug)] pub struct SignSecondMsgRequest { @@ -16,22 +22,22 @@ pub struct SignSecondMsgRequest { } pub fn sign( - client_shim: &api::ClientShim, + client_shim: &ClientShim, message: BigInt, mk: &MasterKey2, x_pos: BigInt, y_pos: BigInt, id: &String, -) -> party_one::SignatureRecid { +) -> Result { let (eph_key_gen_first_message_party_two, eph_comm_witness, eph_ec_key_pair_party2) = MasterKey2::sign_first_message(); let request: party_two::EphKeyGenFirstMsg = eph_key_gen_first_message_party_two; - let res_body = - requests::postb(client_shim, &format!("/ecdsa/sign/{}/first", id), &request).unwrap(); - let sign_party_one_first_message: party_one::EphKeyGenFirstMsg = - serde_json::from_str(&res_body).unwrap(); + match requests::postb(client_shim, &format!("/ecdsa/sign/{}/first", id), &request) { + Some(s) => s, + None => return Err(failure::err_msg("party1 sign first message request failed")) + }; let party_two_sign_message = mk.sign_second_message( &eph_ec_key_pair_party2, @@ -40,26 +46,29 @@ pub fn sign( &message, ); - let signature: party_one::SignatureRecid = get_signature( + let signature = match get_signature( client_shim, message, party_two_sign_message, x_pos, y_pos, &id, - ); + ) { + Ok(s) => s, + Err(e) => return Err(format_err!("ecdsa::get_signature failed failed: {}", e)) + }; - signature + Ok(signature) } fn get_signature( - client_shim: &api::ClientShim, + client_shim: &ClientShim, message: BigInt, party_two_sign_message: party2::SignMessage, x_pos_child_key: BigInt, y_pos_child_key: BigInt, id: &String, -) -> party_one::SignatureRecid { +) -> Result { let request: SignSecondMsgRequest = SignSecondMsgRequest { message, party_two_sign_message, @@ -67,9 +76,83 @@ fn get_signature( y_pos_child_key, }; - let res_body = - requests::postb(client_shim, &format!("/ecdsa/sign/{}/second", id), &request).unwrap(); + let signature: party_one::SignatureRecid = + match requests::postb(client_shim, &format!("/ecdsa/sign/{}/second", id), &request) { + Some(s) => s, + None => return Err(failure::err_msg("party1 sign second message request failed")) + }; + + Ok(signature) +} + +#[no_mangle] +pub extern "C" fn sign_message( + c_endpoint: *const c_char, + c_auth_token: *const c_char, + c_message_le_hex: *const c_char, + c_master_key_json: *const c_char, + c_x_pos: i32, + c_y_pos: i32, + c_id: *const c_char, +) -> *mut c_char { + let raw_endpoint = unsafe { CStr::from_ptr(c_endpoint) }; + let endpoint = match raw_endpoint.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw endpoint failed: {}", e)) + }; + + let raw_auth_token = unsafe { CStr::from_ptr(c_auth_token) }; + let auth_token = match raw_auth_token.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw auth_token failed: {}", e)) + }; + + let raw_message_hex = unsafe { CStr::from_ptr(c_message_le_hex) }; + let message_hex = match raw_message_hex.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw message_hex failed: {}", e)) + }; + + let raw_master_key_json = unsafe { CStr::from_ptr(c_master_key_json) }; + let master_key_json = match raw_master_key_json.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw master_key_json failed: {}", e)) + }; + + let raw_id = unsafe { CStr::from_ptr(c_id) }; + let id = match raw_id.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw id failed: {}", e)) + }; + + let x: BigInt = BigInt::from(c_x_pos); + + let y: BigInt = BigInt::from(c_y_pos); + + let client_shim = ClientShim::new(endpoint.to_string(), Some(auth_token.to_string())); + + let mk: MasterKey2 = serde_json::from_str(master_key_json).unwrap(); + + let mk_child: MasterKey2 = mk.get_child(vec![x.clone(), y.clone()]); + + let message: BigInt = serde_json::from_str(message_hex).unwrap(); + + let sig = match sign( + &client_shim, + message, + &mk_child, + x, + y, + &id.to_string(), + ) { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("signing to endpoint {} failed: {}", endpoint, e)) + }; + + let signature_json = match serde_json::to_string(&sig) { + Ok(share) => share, + Err(e) => return error_to_c_string(format_err!("signing to endpoint {} failed: {}", endpoint, e)), + }; - let signature: party_one::SignatureRecid = serde_json::from_str(&res_body).unwrap(); - signature + CString::new(signature_json.to_owned()).unwrap().into_raw() } diff --git a/gotham-client/src/ecdsa/types.rs b/gotham-client/src/ecdsa/types.rs new file mode 100644 index 0000000..7a59840 --- /dev/null +++ b/gotham-client/src/ecdsa/types.rs @@ -0,0 +1,18 @@ +use kms::ecdsa::two_party::MasterKey2; +use curv::BigInt; + +#[derive(Serialize, Deserialize)] +pub struct PrivateShare { + pub id: String, + pub master_key: MasterKey2 +} + +impl PrivateShare { + pub fn get_child(&self, path: Vec) -> PrivateShare { + let child_key = self.master_key.get_child(path); + PrivateShare { + id: self.id.clone(), + master_key: child_key + } + } +} \ No newline at end of file diff --git a/gotham-client/src/eddsa/keygen.rs b/gotham-client/src/eddsa/keygen.rs new file mode 100644 index 0000000..1b1a7c8 --- /dev/null +++ b/gotham-client/src/eddsa/keygen.rs @@ -0,0 +1,74 @@ +// Gotham-city +// +// Copyright 2018 by Kzen Networks (kzencorp.com) +// Gotham city is free software: you can redistribute +// it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either +// version 3 of the License, or (at your option) any later version. +// + +use super::super::utilities::requests; +use super::super::Result; +use super::super::ClientShim; + +// iOS bindings +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +use multi_party_eddsa::protocols::aggsig::*; + +const PARTY2_INDEX: usize = 1; // client (self) + +pub fn generate_key(client_shim: &ClientShim) -> Result<(KeyPair, KeyAgg, String)> { + let party2_key_pair: KeyPair = KeyPair::create(); + let (id, mut party1_public_key): (String, GE) = requests::postb( + client_shim, + &format!("eddsa/keygen"), + &party2_key_pair.public_key) + .unwrap(); + let eight: FE = ECScalar::from(&BigInt::from(8)); + let eight_inverse: FE = eight.invert(); + party1_public_key = party1_public_key * &eight_inverse; + + // compute apk: + let mut pks: Vec = Vec::new(); + pks.push(party1_public_key.clone()); + pks.push(party2_key_pair.public_key.clone()); + let key_agg = KeyPair::key_aggregation_n(&pks, &PARTY2_INDEX); + + Ok((party2_key_pair, key_agg, id)) +} + +#[no_mangle] +pub extern "C" fn generate_client_key( + c_endpoint: *const c_char, + c_auth_token: *const c_char, +) -> *mut c_char { + let raw_endpoint = unsafe { CStr::from_ptr(c_endpoint) }; + let endpoint = match raw_endpoint.to_str() { + Ok(s) => s, + Err(_) => panic!("Error while decoding raw endpoint"), + }; + + let raw_auth_token = unsafe { CStr::from_ptr(c_auth_token) }; + let auth_token = match raw_auth_token.to_str() { + Ok(s) => s, + Err(_) => panic!("Error while decoding auth token"), + }; + + let client_shim = ClientShim::new(endpoint.to_string(), Some(auth_token.to_string())); + + let key: (KeyPair, KeyAgg, String) = match generate_key(&client_shim) { + Ok(k) => k, + Err(_) => panic!("Error while performing keygen to endpoint {}", endpoint), + }; + + let key_json = match serde_json::to_string(&key) { + Ok(kj) => kj, + Err(_) => panic!("Error while encoding key"), + }; + + CString::new(key_json.to_owned()) + .unwrap() + .into_raw() +} diff --git a/gotham-client/src/eddsa/mod.rs b/gotham-client/src/eddsa/mod.rs new file mode 100644 index 0000000..5083bfa --- /dev/null +++ b/gotham-client/src/eddsa/mod.rs @@ -0,0 +1,16 @@ +// Gotham-city +// +// Copyright 2018 by Kzen Networks (kzencorp.com) +// Gotham city is free software: you can redistribute +// it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either +// version 3 of the License, or (at your option) any later version. +// + +mod keygen; +mod sign; +mod types; + +pub use keygen::generate_key; +pub use sign::sign; +pub use types::*; diff --git a/gotham-client/src/eddsa/sign.rs b/gotham-client/src/eddsa/sign.rs new file mode 100644 index 0000000..04dc62d --- /dev/null +++ b/gotham-client/src/eddsa/sign.rs @@ -0,0 +1,158 @@ +// Gotham-city +// +// Copyright 2018 by Kzen Networks (kzencorp.com) +// Gotham city is free software: you can redistribute +// it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either +// version 3 of the License, or (at your option) any later version. +// + +use super::super::utilities::requests; +use super::super::utilities::error_to_c_string; +use super::super::Result; +use super::super::ClientShim; + +// iOS bindings +use std::ffi::{CStr, CString}; +use std::os::raw::c_char; + +use multi_party_eddsa::protocols::aggsig::*; + +#[allow(non_snake_case)] +pub fn sign( + client_shim: &ClientShim, + message: BigInt, + party2_key_pair: &KeyPair, + key_agg: &KeyAgg, + id: &String +) -> Result { + // round 1: send commitments to ephemeral public keys + let (party2_ephemeral_key, party2_sign_first_msg, party2_sign_second_msg) = + Signature::create_ephemeral_key_and_commit(&party2_key_pair, BigInt::to_vec(&message).as_slice()); + + let party1_sign_first_msg: SignFirstMsg = match requests::postb( + client_shim, + &format!("eddsa/sign/{}/first", id), + &(party2_sign_first_msg, message.clone())) { + Some(s) => s, + None => return Err(failure::err_msg("party1 sign first message request failed")) + }; + + // round 2: send ephemeral public keys and check commitments. + // in the two-party setting, the counterparty can immediately return its local signature. + let (mut party1_sign_second_msg, mut s1): (SignSecondMsg, Signature) = match requests::postb( + client_shim, + &format!("eddsa/sign/{}/second", id), + &party2_sign_second_msg) { + Some(s) => s, + None => return Err(failure::err_msg("party1 sign second message request failed")) + }; + + let eight: FE = ECScalar::from(&BigInt::from(8)); + let eight_inverse: FE = eight.invert(); + party1_sign_second_msg.R = party1_sign_second_msg.R * &eight_inverse; + s1.R = s1.R * &eight_inverse; + assert!(test_com( + &party1_sign_second_msg.R, + &party1_sign_second_msg.blind_factor, + &party1_sign_first_msg.commitment + )); + + // round 3: + // compute R' = sum(Ri): + let mut Ri: Vec = Vec::new(); + Ri.push(party1_sign_second_msg.R.clone()); + Ri.push(party2_sign_second_msg.R.clone()); + // each party i should run this: + let R_tot = Signature::get_R_tot(Ri); + let k = Signature::k(&R_tot, &key_agg.apk, BigInt::to_vec(&message).as_slice()); + let s2 = Signature::partial_sign( + &party2_ephemeral_key.r, + &party2_key_pair, + &k, + &key_agg.hash, + &R_tot, + ); + + let mut s: Vec = Vec::new(); + s.push(s1); + s.push(s2); + let signature = Signature::add_signature_parts(s); + + // verify: + verify(&signature, BigInt::to_vec(&message).as_slice(), &key_agg.apk) + .or_else(|e| Err(format_err!("verifying signature failed: {}", e))) + .and_then(|_| Ok(signature)) +} + +#[no_mangle] +pub extern "C" fn sign_message_eddsa( + c_endpoint: *const c_char, + c_auth_token: *const c_char, + c_message_le_hex: *const c_char, + c_key_pair_json: *const c_char, + c_key_agg_json: *const c_char, + c_id: *const c_char, +) -> *mut c_char { + let raw_endpoint = unsafe { CStr::from_ptr(c_endpoint) }; + let endpoint = match raw_endpoint.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw endpoint failed: {}", e)), + }; + + let raw_auth_token = unsafe { CStr::from_ptr(c_auth_token) }; + let auth_token = match raw_auth_token.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw auth_token failed: {}", e)), + }; + + let raw_message_hex = unsafe { CStr::from_ptr(c_message_le_hex) }; + let message_hex = match raw_message_hex.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw message_hex failed: {}", e)), + }; + + let raw_key_pair_json = unsafe { CStr::from_ptr(c_key_pair_json) }; + let key_pair_json = match raw_key_pair_json.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw key_pair_json failed: {}", e)), + }; + + let raw_key_agg_json = unsafe { CStr::from_ptr(c_key_agg_json) }; + let key_agg_json = match raw_key_agg_json.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw key_agg_json failed: {}", e)), + }; + + let raw_id = unsafe { CStr::from_ptr(c_id) }; + let id = match raw_id.to_str() { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("decoding raw id failed: {}", e)), + }; + + let client_shim = ClientShim::new(endpoint.to_string(), Some(auth_token.to_string())); + + let message: BigInt = serde_json::from_str(message_hex).unwrap(); + + let mut key_pair: KeyPair = serde_json::from_str(key_pair_json).unwrap(); + + let mut key_agg: KeyAgg = serde_json::from_str(key_agg_json).unwrap(); + + let eight: FE = ECScalar::from(&BigInt::from(8)); + let eight_inverse: FE = eight.invert(); + + key_pair.public_key = key_pair.public_key * &eight_inverse; + key_agg.apk = key_agg.apk * &eight_inverse; + + let sig = match sign(&client_shim, message, &key_pair, &key_agg, &id.to_string()) { + Ok(s) => s, + Err(e) => return error_to_c_string(format_err!("signing to endpoint {} failed: {}", endpoint, e)) + }; + + let signature_json = match serde_json::to_string(&sig) { + Ok(share) => share, + Err(e) => return error_to_c_string(format_err!("encoding signature failed: {}", e)), + }; + + CString::new(signature_json.to_owned()).unwrap().into_raw() +} diff --git a/gotham-client/src/eddsa/types.rs b/gotham-client/src/eddsa/types.rs new file mode 100644 index 0000000..538f2d4 --- /dev/null +++ b/gotham-client/src/eddsa/types.rs @@ -0,0 +1 @@ +pub use multi_party_eddsa::protocols::aggsig::{KeyAgg, KeyPair}; diff --git a/gotham-client/src/lib.rs b/gotham-client/src/lib.rs index 334338d..fe73353 100644 --- a/gotham-client/src/lib.rs +++ b/gotham-client/src/lib.rs @@ -23,17 +23,45 @@ extern crate serde_json; #[macro_use] extern crate log; +#[macro_use] +extern crate failure; + extern crate bitcoin; extern crate electrumx_client; extern crate hex; extern crate itertools; -extern crate secp256k1; extern crate time; extern crate uuid; -pub mod api; pub mod ecdsa; pub mod escrow; -pub mod utilities; pub mod wallet; -pub mod tests; + +pub mod eddsa; +pub mod schnorr; + +mod utilities; +mod tests; + +type Result = std::result::Result; + +#[derive(Debug)] +pub struct ClientShim { + pub client: reqwest::Client, + pub auth_token: Option, + pub endpoint: String, +} + +impl ClientShim { + pub fn new(endpoint: String, auth_token: Option) -> ClientShim { + let client = reqwest::Client::new(); + ClientShim { + client, + auth_token, + endpoint, + } + } +} + +pub use curv::{BigInt, arithmetic::traits::Converter}; +pub use multi_party_eddsa::protocols::aggsig::*; diff --git a/gotham-client/src/main.rs b/gotham-client/src/main.rs index 50841c8..be4f376 100644 --- a/gotham-client/src/main.rs +++ b/gotham-client/src/main.rs @@ -11,7 +11,7 @@ extern crate clap; use clap::App; -use client_lib::api; +use client_lib::ClientShim; use client_lib::escrow; use client_lib::wallet; use time::PreciseTime; @@ -34,7 +34,7 @@ fn main() { let hm = settings.try_into::>().unwrap(); let endpoint = hm.get("endpoint").unwrap(); - let client_shim = api::ClientShim::new(endpoint.to_string(), None); + let client_shim = ClientShim::new(endpoint.to_string(), None); let network = "testnet".to_string(); diff --git a/gotham-client/src/schnorr/mod.rs b/gotham-client/src/schnorr/mod.rs new file mode 100644 index 0000000..04fcd40 --- /dev/null +++ b/gotham-client/src/schnorr/mod.rs @@ -0,0 +1,107 @@ +use super::utilities::requests; +use super::Result; +use super::ClientShim; +use multi_party_schnorr::protocols::thresholdsig::zilliqa_schnorr::*; +pub use multi_party_schnorr::protocols::thresholdsig::zilliqa_schnorr::{Signature, Share}; + +const PREFIX: &str = "schnorr"; +const PARTY1_INDEX: usize = 1; // server +const PARTY2_INDEX: usize = 2; // client (self) +const PARAMS: Parameters = Parameters { + threshold: 1, + share_count: 2, +}; + +pub fn generate_key(client_shim: &ClientShim) -> Result { + let key: Keys = Keys::phase1_create(PARTY2_INDEX); + let (msg1, msg2) = key.phase1_broadcast(); + + let (id, party1_msg1): (String, KeyGenBroadcastMessage1) = requests::postb( + client_shim, + &format!("{}/keygen/first", PREFIX), &msg1) + .unwrap(); + + let party1_msg2: KeyGenBroadcastMessage2 = requests::postb( + client_shim, + &format!("{}/keygen/{}/second", PREFIX, id), + &msg2) + .unwrap(); + + let (vss_scheme, secret_shares, _index) = key.phase1_verify_com_phase2_distribute( + &PARAMS, + &vec![party1_msg2.clone(), msg2], + &vec![party1_msg1, msg1], + &vec![PARTY1_INDEX, PARTY2_INDEX]) + .or_else(|e| Err(e))?; + let msg3 = KeyGenMessage3 { + vss_scheme, + secret_share: secret_shares[PARTY1_INDEX - 1], + }; + + let party1_msg3: KeyGenMessage3 = requests::postb( + client_shim, + &format!("{}/keygen/{}/third", PREFIX, id), &msg3) + .unwrap(); + + let vss_scheme_vec = vec![party1_msg3.vss_scheme, msg3.vss_scheme]; + let shared_key: SharedKeys = key.phase2_verify_vss_construct_keypair( + &PARAMS, + &vec![party1_msg2.y_i, key.y_i], + &vec![party1_msg3.secret_share, secret_shares[key.party_index - 1]], + &vss_scheme_vec, + &key.party_index) + .or_else(|e| Err(e))?; + + let share: Share = Share { + id, + shared_key, + vss_scheme_vec, + }; + Ok(share) +} + +pub fn sign( + client_shim: &ClientShim, + message: BigInt, + share: &Share, +) -> Result { + let eph_share = generate_key(client_shim) + .or_else(|e| Err(e))?; + let message_vec = BigInt::to_vec(&message); + let message_slice = message_vec.as_slice(); + let local_sig = LocalSig::compute( + message_slice, + &eph_share.shared_key, + &share.shared_key); + + let sign_msg1 = SignMessage1 { + message, + local_sig, + }; + + let party1_local_sig: LocalSig = requests::postb( + client_shim, + &format!("{}/sign/{}/{}", PREFIX, share.id, eph_share.id), + &sign_msg1) + .unwrap(); + + let local_sig_vec = &vec![party1_local_sig, local_sig]; + let vss_sum_local_sigs: VerifiableSS = LocalSig::verify_local_sigs( + local_sig_vec, + &vec![PARTY1_INDEX - 1, PARTY2_INDEX - 1], + &share.vss_scheme_vec, + &eph_share.vss_scheme_vec) + .or_else(|e| Err(e))?; + + let signature = Signature::generate( + &vss_sum_local_sigs, + local_sig_vec, + &vec![PARTY1_INDEX - 1, PARTY2_INDEX - 1], + &eph_share.shared_key.y, + &share.shared_key.y, + message_slice, + ); + signature.verify(message_slice, &share.shared_key.y) + .or_else(|e| Err(format_err!("{}", e))) + .and_then(|()| Ok(signature)) +} \ No newline at end of file diff --git a/gotham-client/src/utilities/mod.rs b/gotham-client/src/utilities/mod.rs index df573cd..631fc60 100644 --- a/gotham-client/src/utilities/mod.rs +++ b/gotham-client/src/utilities/mod.rs @@ -7,4 +7,11 @@ // version 3 of the License, or (at your option) any later version. // +use std::ffi::CString; +use std::os::raw::c_char; + pub mod requests; + +pub fn error_to_c_string(e: failure::Error) -> *mut c_char { + CString::new(format!("Error: {}", e.to_string())).unwrap().into_raw() +} diff --git a/gotham-client/src/utilities/requests.rs b/gotham-client/src/utilities/requests.rs index f147dfd..836ecae 100644 --- a/gotham-client/src/utilities/requests.rs +++ b/gotham-client/src/utilities/requests.rs @@ -6,34 +6,28 @@ // License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // - -use super::super::api; use serde; use time::PreciseTime; +use super::super::ClientShim; -pub fn post(client_shim: &api::ClientShim, path: &str) -> Option { - let start = PreciseTime::now(); - - let mut b = client_shim - .client - .post(&format!("{}/{}", client_shim.endpoint, path)); - - if client_shim.auth_token.is_some() { - b = b.bearer_auth(client_shim.auth_token.clone().unwrap()); - } - - let res = b.json("{}").send(); - - let end = PreciseTime::now(); - - info!("(req {}, took: {})", path, start.to(end)); - - Some(res.unwrap().text().unwrap()) +pub fn post(client_shim: &ClientShim, path: &str) -> Option + where V: serde::de::DeserializeOwned +{ + _postb(client_shim, path, "{}") } -pub fn postb(client_shim: &api::ClientShim, path: &str, body: T) -> Option +pub fn postb(client_shim: &ClientShim, path: &str, body: T) -> Option where T: serde::ser::Serialize, + V: serde::de::DeserializeOwned +{ + _postb(client_shim, path, body) +} + +fn _postb(client_shim: &ClientShim, path: &str, body: T) -> Option + where + T: serde::ser::Serialize, + V: serde::de::DeserializeOwned { let start = PreciseTime::now(); @@ -51,5 +45,10 @@ where info!("(req {}, took: {})", path, start.to(end)); - Some(res.unwrap().text().unwrap()) + let value = match res { + Ok(mut v) => v.text().unwrap(), + Err(_) => return None + }; + + Some(serde_json::from_str(value.as_str()).unwrap()) } diff --git a/gotham-client/src/wallet/mod.rs b/gotham-client/src/wallet/mod.rs index 31d1bc8..3367523 100644 --- a/gotham-client/src/wallet/mod.rs +++ b/gotham-client/src/wallet/mod.rs @@ -12,6 +12,8 @@ use bitcoin::consensus::encode::serialize; use bitcoin::network::constants::Network; use bitcoin::util::bip143::SighashComponents; use bitcoin::{TxIn, TxOut}; +use bitcoin::hashes::{sha256d, hex::FromHex}; +use bitcoin::secp256k1::Signature; use curv::elliptic::curves::traits::ECPoint; use curv::{BigInt, GE}; use electrumx_client::{electrumx_client::ElectrumxClient, interface::Electrumx}; @@ -25,15 +27,14 @@ use centipede::juggling::proof_system::{Helgamalsegmented, Proof}; use centipede::juggling::segmentation::Msegmentation; use kms::chain_code::two_party::party2::ChainCode2; -use super::api; -use super::api::PrivateShare; -use super::ecdsa::rotate; +use super::ecdsa; +use super::ecdsa::types::PrivateShare; use super::escrow; use super::utilities::requests; +use super::ClientShim; use curv::arithmetic::traits::Converter; use hex; use itertools::Itertools; -use secp256k1::Signature; use std::collections::HashMap; use std::str::FromStr; @@ -88,9 +89,9 @@ pub struct Wallet { } impl Wallet { - pub fn new(client_shim: &api::ClientShim, net: &String) -> Wallet { + pub fn new(client_shim: &ClientShim, net: &String) -> Wallet { let id = Uuid::new_v4().to_string(); - let private_share = api::get_master_key(client_shim); + let private_share = ecdsa::get_master_key(client_shim); let last_derived_pos = 0; let addresses_derivation_map = HashMap::new(); let network = net.clone(); @@ -104,8 +105,8 @@ impl Wallet { } } - pub fn rotate(self, client_shim: &api::ClientShim) -> Self { - rotate::rotate_master_key(self, client_shim) + pub fn rotate(self, client_shim: &ClientShim) -> Self { + ecdsa::rotate_master_key(self, client_shim) } pub fn backup(&self, escrow_service: escrow::Escrow) { @@ -162,7 +163,7 @@ impl Wallet { pub fn recover_and_save_share( escrow_service: escrow::Escrow, net: &String, - client_shim: &api::ClientShim, + client_shim: &ClientShim, ) -> Wallet { let g: GE = ECPoint::generator(); let y_priv = escrow_service.get_private_key(); @@ -181,9 +182,8 @@ impl Wallet { let client_master_key_recovered = MasterKey2::recover_master_key(sk.unwrap(), public_data, chain_code2); - let res_body = requests::post(client_shim, &format!("ecdsa/{}/recover", key_id)).unwrap(); + let pos_old: u32 = requests::post(client_shim, &format!("ecdsa/{}/recover", key_id)).unwrap(); - let pos_old: u32 = serde_json::from_str(&res_body).unwrap(); let pos_old = if pos_old < 10 { 10 } else { pos_old }; //TODO: temporary, server will keep updated pos, to do so we need to send update to server for every get_new_address @@ -238,7 +238,7 @@ impl Wallet { &mut self, to_address: String, amount_btc: f32, - client_shim: &api::ClientShim, + client_shim: &ClientShim, ) -> String { let selected = self.select_tx_in(amount_btc); if selected.is_empty() { @@ -252,7 +252,7 @@ impl Wallet { .into_iter() .map(|s| bitcoin::TxIn { previous_output: bitcoin::OutPoint { - txid: bitcoin::util::hash::Sha256dHash::from_hex(&s.tx_hash).unwrap(), + txid: sha256d::Hash::from_hex(&s.tx_hash).unwrap(), vout: s.tx_pos as u32, }, script_sig: bitcoin::Script::default(), @@ -304,30 +304,33 @@ impl Wallet { let comp = SighashComponents::new(&transaction); let sig_hash = comp.sighash_all( &transaction.input[i], - &bitcoin::Address::p2pkh(&pk, self.get_bitcoin_network()).script_pubkey(), + &bitcoin::Address::p2pkh( + &to_bitcoin_public_key(pk), + self.get_bitcoin_network()).script_pubkey(), (selected[i].value as u32).into(), ); - let signature = api::sign( + let signature = ecdsa::sign( client_shim, - BigInt::from_hex(&sig_hash.le_hex_string()), + BigInt::from_hex(&hex::encode(&sig_hash[..])), &mk, BigInt::from(0), BigInt::from(address_derivation.pos), &self.private_share.id, - ); + ).unwrap(); let mut v = BigInt::to_vec(&signature.r); v.extend(BigInt::to_vec(&signature.s)); - let mut sig = Signature::from_compact(&v[..]).unwrap().serialize_der(); + let mut sig_vec = Signature::from_compact(&v[..]) + .unwrap() + .serialize_der() + .to_vec(); + sig_vec.push(01); - sig.push(01); - let mut witness = Vec::new(); - witness.push(sig); - witness.push(pk.serialize().to_vec()); + let pk_vec = pk.serialize().to_vec(); - signed_transaction.input[i].witness = witness; + signed_transaction.input[i].witness = vec![sig_vec, pk_vec]; } let mut electrum = ElectrumxClient::new(ELECTRUM_HOST).unwrap(); @@ -341,7 +344,10 @@ impl Wallet { pub fn get_new_bitcoin_address(&mut self) -> bitcoin::Address { let (pos, mk) = Self::derive_new_key(&self.private_share, self.last_derived_pos); let pk = mk.public.q.get_element(); - let address = bitcoin::Address::p2wpkh(&pk, self.get_bitcoin_network()); + let address = bitcoin::Address::p2wpkh( + &to_bitcoin_public_key(pk), + self.get_bitcoin_network() + ); self.addresses_derivation_map .insert(address.to_string(), AddressDerivation { mk, pos }); @@ -356,7 +362,10 @@ impl Wallet { let (pos, mk) = Self::derive_new_key(&self.private_share, i); let address = - bitcoin::Address::p2wpkh(&mk.public.q.get_element(), self.get_bitcoin_network()); + bitcoin::Address::p2wpkh( + &to_bitcoin_public_key(mk.public.q.get_element()), + self.get_bitcoin_network() + ); self.addresses_derivation_map .insert(address.to_string(), AddressDerivation { mk, pos }); @@ -383,7 +392,7 @@ impl Wallet { let list_unspent: Vec = self .get_all_addresses_balance() .into_iter() - .filter(|b| b.confirmed > 0) +// .filter(|b| b.confirmed > 0) .map(|a| self.list_unspent_for_addresss(a.address.to_string())) .flatten() .sorted_by(|a, b| a.value.partial_cmp(&b.value).unwrap()) @@ -489,13 +498,24 @@ impl Wallet { } fn to_bitcoin_address(mk: &MasterKey2, network: Network) -> bitcoin::Address { - bitcoin::Address::p2wpkh(&mk.public.q.get_element(), network) + bitcoin::Address::p2wpkh( + &to_bitcoin_public_key(mk.public.q.get_element()), + network + ) + } +} + +// type conversion +fn to_bitcoin_public_key(pk: curv::PK) -> bitcoin::util::key::PublicKey { + bitcoin::util::key::PublicKey { + compressed: true, + key: pk } } #[cfg(test)] mod tests { - use bitcoin::util::hash::Sha256dHash; + use bitcoin::hashes::sha256d; use curv::arithmetic::traits::Converter; use curv::BigInt; @@ -507,7 +527,7 @@ mod tests { ]; // 14abf5ed107ff58bf844ee7f447bec317c276b00905c09a45434f8848599597e - let hash = Sha256dHash::from_data(&message); + let hash = sha256d::Hash::from_slice(&message); // 7e59998584f83454a4095c90006b277c31ec7b447fee44f88bf57f10edf5ab14 let ser = hash.le_hex_string(); diff --git a/gotham-server/Cargo.toml b/gotham-server/Cargo.toml index 8b06507..715b6d9 100644 --- a/gotham-server/Cargo.toml +++ b/gotham-server/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "gotham-server" version = "0.1.0" -authors = ["gbenattar "] +authors = [ + "gbenattar ", + "Oded Leiba FromRequest<'a, 'r> for Claims { && config.region.is_empty() && config.pool_id.is_empty() { - info!("!!! Auth config empty, request in PASSTHROUGH mode !!! "); + debug!("!!! Auth config empty, request in PASSTHROUGH mode !!! "); if auths.is_empty() { // No Authorization header - info!("!!! No Authorization header, request accepted !!! "); + debug!("!!! No Authorization header, request accepted !!! "); return Outcome::Success(passthrough::get_empty_claim()); } else { error!("!!! Auth config empty but authorization header, rejecting requests !!!"); diff --git a/gotham-server/src/lib.rs b/gotham-server/src/lib.rs index a94791e..607bc8f 100644 --- a/gotham-server/src/lib.rs +++ b/gotham-server/src/lib.rs @@ -30,11 +30,6 @@ extern crate serde_derive; extern crate serde; extern crate serde_json; -extern crate strum; - -#[macro_use] -extern crate strum_macros; - #[macro_use] extern crate log; @@ -57,3 +52,7 @@ pub mod storage; pub mod tests; type Result = std::result::Result; + +pub struct Config { + pub db: storage::db::DB, +} diff --git a/gotham-server/src/routes/ecdsa.rs b/gotham-server/src/routes/ecdsa.rs index 5118436..76530ad 100644 --- a/gotham-server/src/routes/ecdsa.rs +++ b/gotham-server/src/routes/ecdsa.rs @@ -27,10 +27,7 @@ use uuid::Uuid; use super::super::auth::jwt::Claims; use super::super::storage::db; -use super::super::storage::db::DB; - -use self::Share::*; -use std::slice::Iter; +use super::super::Config; #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] struct HDPos { @@ -42,8 +39,8 @@ struct Alpha { value: BigInt } -#[derive(ToString, Debug)] -pub enum Share { +#[derive(Debug)] +pub enum EcdsaStruct { KeyGenFirstMsg, CommWitness, EcKeyPair, @@ -78,43 +75,23 @@ pub enum Share { POS, } -impl Share { - pub fn iterator() -> Iter<'static, Share> { - static FIELDS: [Share; 26] = [ - KeyGenFirstMsg, - CommWitness, - EcKeyPair, - PaillierKeyPair, - Party1Private, - Party2Public, - PDLProver, - PDLDecommit, - Alpha, - Party2PDLFirstMsg, - CCKeyGenFirstMsg, - CCCommWitness, - CCEcKeyPair, - CC, - Party1MasterKey, - EphEcKeyPair, - EphKeyGenFirstMsg, - RotateCommitMessage1M, - RotateCommitMessage1R, - RotateRandom1, - RotateFirstMsg, - RotatePrivateNew, - RotatePdlDecom, - RotateParty2First, - RotateParty1Second, - POS, - ]; - - FIELDS.iter() +impl db::MPCStruct for EcdsaStruct { + fn to_string(&self) -> String { + format!("{:?}", self) } -} -pub struct Config { - pub db: DB, + // backward compatibility + fn to_table_name(&self, env: &str) -> String { + if self.to_string() == "Party1MasterKey" { + format!("{}_{}", env, self.to_string()) + } else { + format!("{}-gotham-{}", env, self.to_string()) + } + } + + fn require_customer_id(&self) -> bool { + self.to_string() == "Party1MasterKey" + } } #[post("/ecdsa/keygen/first", format = "json")] @@ -131,7 +108,7 @@ pub fn first_message( &state.db, &claim.sub, &id, - &Share::POS, + &EcdsaStruct::POS, &HDPos { pos: 0u32 }, )?; @@ -139,17 +116,17 @@ pub fn first_message( &state.db, &claim.sub, &id, - &Share::KeyGenFirstMsg, + &EcdsaStruct::KeyGenFirstMsg, &key_gen_first_msg, )?; db::insert( &state.db, &claim.sub, &id, - &Share::CommWitness, + &EcdsaStruct::CommWitness, &comm_witness, )?; - db::insert(&state.db, &claim.sub, &id, &Share::EcKeyPair, &ec_key_pair)?; + db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::EcKeyPair, &ec_key_pair)?; Ok(Json((id, key_gen_first_msg))) } @@ -166,14 +143,14 @@ pub fn second_message( &state.db, &claim.sub, &id, - &Share::Party2Public, + &EcdsaStruct::Party2Public, &party2_public, )?; let comm_witness: party_one::CommWitness = - db::get(&state.db, &claim.sub, &id, &Share::CommWitness)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CommWitness)? .ok_or(format_err!("No data for such identifier {}", id))?; - let ec_key_pair: party_one::EcKeyPair = db::get(&state.db, &claim.sub, &id, &Share::EcKeyPair)? + let ec_key_pair: party_one::EcKeyPair = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::EcKeyPair)? .ok_or(format_err!("No data for such identifier {}", id))?; let (kg_party_one_second_message, paillier_key_pair, party_one_private) = @@ -183,14 +160,14 @@ pub fn second_message( &state.db, &claim.sub, &id, - &Share::PaillierKeyPair, + &EcdsaStruct::PaillierKeyPair, &paillier_key_pair, )?; db::insert( &state.db, &claim.sub, &id, - &Share::Party1Private, + &EcdsaStruct::Party1Private, &party_one_private, )?; @@ -209,7 +186,7 @@ pub fn third_message( party_2_pdl_first_message: Json, ) -> Result> { let party_one_private: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &Share::Party1Private)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1Private)? .ok_or(format_err!("No data for such identifier {}", id))?; let (party_one_third_message, party_one_pdl_decommit, alpha) = @@ -219,17 +196,17 @@ pub fn third_message( &state.db, &claim.sub, &id, - &Share::PDLDecommit, + &EcdsaStruct::PDLDecommit, &party_one_pdl_decommit, )?; - db::insert(&state.db, &claim.sub, &id, &Share::Alpha, &Alpha { value: alpha })?; + db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::Alpha, &Alpha { value: alpha })?; db::insert( &state.db, &claim.sub, &id, - &Share::Party2PDLFirstMsg, + &EcdsaStruct::Party2PDLFirstMsg, &party_2_pdl_first_message.0, )?; @@ -248,18 +225,18 @@ pub fn fourth_message( party_two_pdl_second_message: Json, ) -> Result> { let party_one_private: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &Share::Party1Private)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1Private)? .ok_or(format_err!("No data for such identifier {}", id))?; let party_one_pdl_decommit: party_one::PDLdecommit = - db::get(&state.db, &claim.sub, &id, &Share::PDLDecommit)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::PDLDecommit)? .ok_or(format_err!("No data for such identifier {}", id))?; let party_2_pdl_first_message: party_two::PDLFirstMessage = - db::get(&state.db, &claim.sub, &id, &Share::Party2PDLFirstMsg)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party2PDLFirstMsg)? .ok_or(format_err!("No data for such identifier {}", id))?; - let alpha: Alpha = db::get(&state.db, &claim.sub, &id, &Share::Alpha)? + let alpha: Alpha = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Alpha)? .ok_or(format_err!("No data for such identifier {}", id))?; let res = MasterKey1::key_gen_fourth_message( @@ -288,21 +265,21 @@ pub fn chain_code_first_message( &state.db, &claim.sub, &id, - &Share::CCKeyGenFirstMsg, + &EcdsaStruct::CCKeyGenFirstMsg, &cc_party_one_first_message, )?; db::insert( &state.db, &claim.sub, &id, - &Share::CCCommWitness, + &EcdsaStruct::CCCommWitness, &cc_comm_witness, )?; db::insert( &state.db, &claim.sub, &id, - &Share::CCEcKeyPair, + &EcdsaStruct::CCEcKeyPair, &cc_ec_key_pair1, )?; @@ -320,7 +297,7 @@ pub fn chain_code_second_message( id: String, cc_party_two_first_message_d_log_proof: Json, ) -> Result> { - let cc_comm_witness: CommWitness = db::get(&state.db, &claim.sub, &id, &Share::CCCommWitness)? + let cc_comm_witness: CommWitness = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CCCommWitness)? .ok_or(format_err!("No data for such identifier {}", id))?; let party1_cc = chain_code::party1::ChainCode1::chain_code_second_message( @@ -341,36 +318,36 @@ pub fn chain_code_compute_message( cc_party2_public: &GE, ) -> Result> { let cc_ec_key_pair_party1: EcKeyPair = - db::get(&state.db, &claim.sub, &id, &Share::CCEcKeyPair)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CCEcKeyPair)? .ok_or(format_err!("No data for such identifier {}", id))?; let party1_cc = chain_code::party1::ChainCode1::compute_chain_code( &cc_ec_key_pair_party1, &cc_party2_public, ); - db::insert(&state.db, &claim.sub, &id, &Share::CC, &party1_cc)?; + db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::CC, &party1_cc)?; master_key(state, claim, id)?; Ok(Json(())) } pub fn master_key(state: State, claim: Claims, id: String) -> Result<()> { - let party2_public: GE = db::get(&state.db, &claim.sub, &id, &Share::Party2Public)? + let party2_public: GE = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party2Public)? .ok_or(format_err!("No data for such identifier {}", id))?; let paillier_key_pair: party_one::PaillierKeyPair = - db::get(&state.db, &claim.sub, &id, &Share::PaillierKeyPair)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::PaillierKeyPair)? .ok_or(format_err!("No data for such identifier {}", id))?; let party1_cc: chain_code::party1::ChainCode1 = - db::get(&state.db, &claim.sub, &id, &Share::CC)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CC)? .ok_or(format_err!("No data for such identifier {}", id))?; let party_one_private: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &Share::Party1Private)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1Private)? .ok_or(format_err!("No data for such identifier {}", id))?; let comm_witness: party_one::CommWitness = - db::get(&state.db, &claim.sub, &id, &Share::CommWitness)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CommWitness)? .ok_or(format_err!("No data for such identifier {}", id))?; let masterKey = MasterKey1::set_master_key( @@ -385,7 +362,7 @@ pub fn master_key(state: State, claim: Claims, id: String) -> Result<()> &state.db, &claim.sub, &id, - &Share::Party1MasterKey, + &EcdsaStruct::Party1MasterKey, &masterKey, ) } @@ -407,7 +384,7 @@ pub fn sign_first( &state.db, &claim.sub, &id, - &Share::EphKeyGenFirstMsg, + &EcdsaStruct::EphKeyGenFirstMsg, &eph_key_gen_first_message_party_two.0, )?; @@ -415,7 +392,7 @@ pub fn sign_first( &state.db, &claim.sub, &id, - &Share::EphEcKeyPair, + &EcdsaStruct::EphEcKeyPair, &eph_ec_key_pair_party1, )?; @@ -437,7 +414,7 @@ pub fn sign_second( id: String, request: Json, ) -> Result> { - let master_key: MasterKey1 = db::get(&state.db, &claim.sub, &id, &Share::Party1MasterKey)? + let master_key: MasterKey1 = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1MasterKey)? .ok_or(format_err!("No data for such identifier {}", id))?; let x: BigInt = request.x_pos_child_key.clone(); @@ -446,11 +423,11 @@ pub fn sign_second( let child_master_key = master_key.get_child(vec![x, y]); let eph_ec_key_pair_party1: party_one::EphEcKeyPair = - db::get(&state.db, &claim.sub, &id, &Share::EphEcKeyPair)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::EphEcKeyPair)? .ok_or(format_err!("No data for such identifier {}", id))?; let eph_key_gen_first_message_party_two: party_two::EphKeyGenFirstMsg = - db::get(&state.db, &claim.sub, &id, &Share::EphKeyGenFirstMsg)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::EphKeyGenFirstMsg)? .ok_or(format_err!("No data for such identifier {}", id))?; let signature_with_recid = child_master_key.sign_second_message( @@ -468,7 +445,7 @@ pub fn sign_second( } pub fn get_mk(state: &State, claim: Claims, id: &String) -> Result { - db::get(&state.db, &claim.sub, &id, &Share::Party1MasterKey)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1MasterKey)? .ok_or(format_err!("No data for such identifier {}", id)) } @@ -483,14 +460,14 @@ pub fn rotate_first( &state.db, &claim.sub, &id, - &Share::RotateCommitMessage1M, + &EcdsaStruct::RotateCommitMessage1M, &m1, )?; db::insert( &state.db, &claim.sub, &id, - &Share::RotateCommitMessage1R, + &EcdsaStruct::RotateCommitMessage1R, &r1, )?; Ok(Json(party1_coin_flip_first_message)) @@ -516,15 +493,15 @@ pub fn rotate_second( > { let party_one_master_key = get_mk(&state, claim.clone(), &id)?; - let m1: Secp256k1Scalar = db::get(&state.db, &claim.sub, &id, &Share::RotateCommitMessage1M)? + let m1: Secp256k1Scalar = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotateCommitMessage1M)? .ok_or(format_err!("No data for such identifier {}", id))?; - let r1: Secp256k1Scalar = db::get(&state.db, &claim.sub, &id, &Share::RotateCommitMessage1R)? + let r1: Secp256k1Scalar = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotateCommitMessage1R)? .ok_or(format_err!("No data for such identifier {}", id))?; let (party1_second_message, random1) = Rotation1::key_rotate_second_message(&party2_first_message.0, &m1, &r1); - db::insert(&state.db, &claim.sub, &id, &Share::RotateRandom1, &random1)?; + db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::RotateRandom1, &random1)?; let (rotation_party_one_first_message, party_one_private_new) = party_one_master_key.rotation_first_message(&random1); @@ -533,14 +510,14 @@ pub fn rotate_second( &state.db, &claim.sub, &id, - &Share::RotateFirstMsg, + &EcdsaStruct::RotateFirstMsg, &rotation_party_one_first_message, )?; db::insert( &state.db, &claim.sub, &id, - &Share::RotatePrivateNew, + &EcdsaStruct::RotatePrivateNew, &party_one_private_new, )?; Ok(Json(( @@ -561,7 +538,7 @@ pub fn rotate_third( rotation_party_two_first_message: Json, ) -> Result> { let party_one_private_new: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &Share::RotatePrivateNew)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotatePrivateNew)? .ok_or(format_err!("No data for such identifier {}", id))?; let (rotation_party_one_second_message, party_one_pdl_decommit, alpha) = @@ -570,27 +547,27 @@ pub fn rotate_third( &party_one_private_new, ); - db::insert(&state.db, &claim.sub, &id, &Share::Alpha, &Alpha { value: alpha })?; + db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::Alpha, &Alpha { value: alpha })?; db::insert( &state.db, &claim.sub, &id, - &Share::RotatePdlDecom, + &EcdsaStruct::RotatePdlDecom, &party_one_pdl_decommit, )?; db::insert( &state.db, &claim.sub, &id, - &Share::RotateParty2First, + &EcdsaStruct::RotateParty2First, &rotation_party_two_first_message.0, )?; db::insert( &state.db, &id, &claim.sub, - &Share::RotateParty1Second, + &EcdsaStruct::RotateParty1Second, &rotation_party_one_second_message, )?; @@ -611,26 +588,26 @@ pub fn rotate_fourth( let party_one_master_key = get_mk(&state, claim.clone(), &id)?; let rotation_party_one_first_message: party1::RotationParty1Message1 = - db::get(&state.db, &claim.sub, &id, &Share::RotateFirstMsg)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotateFirstMsg)? .ok_or(format_err!("No data for such identifier {}", id))?; let party_one_private_new: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &Share::RotatePrivateNew)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotatePrivateNew)? .ok_or(format_err!("No data for such identifier {}", id))?; let random1: kms::rotation::two_party::Rotation = - db::get(&state.db, &claim.sub, &id, &Share::RotateRandom1)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotateRandom1)? .ok_or(format_err!("No data for such identifier {}", id))?; let rotation_party_two_first_message: party_two::PDLFirstMessage = - db::get(&state.db, &claim.sub, &id, &Share::RotateParty2First)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotateParty2First)? .ok_or(format_err!("No data for such identifier {}", id))?; let party_one_pdl_decommit: party_one::PDLdecommit = - db::get(&state.db, &claim.sub, &id, &Share::RotatePdlDecom)? + db::get(&state.db, &claim.sub, &id, &EcdsaStruct::RotatePdlDecom)? .ok_or(format_err!("No data for such identifier {}", id))?; - let alpha: Alpha = db::get(&state.db, &claim.sub, &id, &Share::Alpha)? + let alpha: Alpha = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Alpha)? .ok_or(format_err!("No data for such identifier {}", id))?; let result_rotate_party_two_second_message = party_one_master_key.rotation_third_message( @@ -652,7 +629,7 @@ pub fn rotate_fourth( &state.db, &claim.sub, &id, - &Share::Party1MasterKey, + &EcdsaStruct::Party1MasterKey, &party_one_master_key_rotated, )?; @@ -661,7 +638,7 @@ pub fn rotate_fourth( #[post("/ecdsa//recover", format = "json")] pub fn recover(state: State, claim: Claims, id: String) -> Result> { - let pos_old: u32 = db::get(&state.db, &claim.sub, &id, &Share::POS)? + let pos_old: u32 = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::POS)? .ok_or(format_err!("No data for such identifier {}", id))?; Ok(Json(pos_old)) } diff --git a/gotham-server/src/routes/eddsa.rs b/gotham-server/src/routes/eddsa.rs new file mode 100644 index 0000000..c6b0296 --- /dev/null +++ b/gotham-server/src/routes/eddsa.rs @@ -0,0 +1,218 @@ +use super::super::Result; +use rocket::State; +use rocket_contrib::json::Json; +use uuid::Uuid; + +use super::super::auth::jwt::Claims; +use super::super::storage::db; +use super::super::Config; + +use multi_party_eddsa::protocols::aggsig::*; +use self::EddsaStruct::*; + +const PARTY1_INDEX: usize = 0; + +#[derive(Debug)] +pub enum EddsaStruct { + Party2PublicKey, + Party1KeyPair, + AggregatedPublicKey, + Party2SignFirstMsg, + Message, + Party1EphemeralKey, + Party1SignFirstMsg, + Party1SignSecondMsg +} + +impl db::MPCStruct for EddsaStruct { + fn to_string(&self) -> String { + format!("Eddsa{:?}", self) + } +} + +// creating a wrapper for dynamodb insertion compatibility +#[derive(Debug, Serialize, Deserialize)] +struct MessageStruct { + message: BigInt, +} + +#[post("/eddsa/keygen", format = "json", data = "")] +pub fn keygen( + state: State, + claim: Claims, + party2_public_key_json: Json, +) -> Result> { + let id = Uuid::new_v4().to_string(); + let party1_key_pair: KeyPair = KeyPair::create(); + let eight: FE = ECScalar::from(&BigInt::from(8)); + let eight_inverse: FE = eight.invert(); + let party2_public_key = party2_public_key_json.0 * &eight_inverse; + db::insert( + &state.db, + &claim.sub, + &id, + &Party2PublicKey, + &party2_public_key, + )?; + + // compute apk: + let mut pks: Vec = Vec::new(); + pks.push(party1_key_pair.public_key.clone()); + pks.push(party2_public_key.clone()); + let key_agg = KeyPair::key_aggregation_n(&pks, &PARTY1_INDEX); + db::insert( + &state.db, + &claim.sub, + &id, + &Party1KeyPair, + &party1_key_pair, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &AggregatedPublicKey, + &key_agg, + )?; + + Ok(Json((id, party1_key_pair.public_key))) +} + +#[post("/eddsa/sign//first", format = "json", data = "")] +pub fn sign_first( + state: State, + claim: Claims, + id: String, + party2_sign_first_msg_obj: Json<(SignFirstMsg, BigInt)>, +) -> Result> { + let (party2_sign_first_msg, message): (SignFirstMsg, BigInt) = + party2_sign_first_msg_obj.0; + + let party1_key_pair: KeyPair = db::get( + &state.db, + &claim.sub, + &id, + &Party1KeyPair)? + .ok_or(format_err!("No data for such identifier {}", id))?; + + let (party1_ephemeral_key, party1_sign_first_msg, party1_sign_second_msg) = + Signature::create_ephemeral_key_and_commit(&party1_key_pair, &BigInt::to_vec(&message).as_slice()); + + db::insert( + &state.db, + &claim.sub, + &id, + &Party2SignFirstMsg, + &party2_sign_first_msg, + )?; + + let message_struct = MessageStruct { + message, + }; + db::insert( + &state.db, + &claim.sub, + &id, + &Message, + &message_struct, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Party1EphemeralKey, + &party1_ephemeral_key, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Party1SignFirstMsg, + &party1_sign_first_msg, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Party1SignSecondMsg, + &party1_sign_second_msg, + )?; + + Ok(Json(party1_sign_first_msg)) +} + +#[allow(non_snake_case)] +#[post("/eddsa/sign//second", format = "json", data = "")] +pub fn sign_second( + state: State, + claim: Claims, + id: String, + mut party2_sign_second_msg: Json, +) -> Result> { + let party2_sign_first_msg: SignFirstMsg = db::get( + &state.db, + &claim.sub, + &id, + &Party2SignFirstMsg)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let eight: FE = ECScalar::from(&BigInt::from(8)); + let eight_inverse: FE = eight.invert(); + party2_sign_second_msg.R = party2_sign_second_msg.R * &eight_inverse; + assert!(test_com( + &party2_sign_second_msg.R, + &party2_sign_second_msg.blind_factor, + &party2_sign_first_msg.commitment + )); + + let party1_key_pair: KeyPair = db::get( + &state.db, + &claim.sub, + &id, + &Party1KeyPair)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let mut party1_ephemeral_key: EphemeralKey = db::get( + &state.db, + &claim.sub, + &id, + &Party1EphemeralKey)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let mut party1_sign_second_msg: SignSecondMsg = db::get( + &state.db, + &claim.sub, + &id, + &Party1SignSecondMsg)? + .ok_or(format_err!("No data for such identifier {}", id))?; + party1_ephemeral_key.R = party1_ephemeral_key.R * &eight_inverse; + party1_sign_second_msg.R = party1_sign_second_msg.R * &eight_inverse; + let mut key_agg: KeyAgg = db::get( + &state.db, + &claim.sub, + &id, + &AggregatedPublicKey)? + .ok_or(format_err!("No data for such identifier {}", id))?; + key_agg.apk = key_agg.apk * &eight_inverse; + let message_struct: MessageStruct = db::get( + &state.db, + &claim.sub, + &id, + &Message)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let message: BigInt = message_struct.message; + + // compute R' = sum(Ri): + let mut Ri: Vec = Vec::new(); + Ri.push(party1_sign_second_msg.R.clone()); + Ri.push(party2_sign_second_msg.R.clone()); + // each party i should run this: + let R_tot = Signature::get_R_tot(Ri); + let k = Signature::k(&R_tot, &key_agg.apk, &BigInt::to_vec(&message).as_slice()); + let s1 = Signature::partial_sign( + &party1_ephemeral_key.r, + &party1_key_pair, + &k, + &key_agg.hash, + &R_tot, + ); + + Ok(Json((party1_sign_second_msg, s1))) +} \ No newline at end of file diff --git a/gotham-server/src/routes/mod.rs b/gotham-server/src/routes/mod.rs index 98e0bb2..379074f 100644 --- a/gotham-server/src/routes/mod.rs +++ b/gotham-server/src/routes/mod.rs @@ -8,4 +8,6 @@ // pub mod ecdsa; +pub mod schnorr; +pub mod eddsa; pub mod ping; diff --git a/gotham-server/src/routes/schnorr.rs b/gotham-server/src/routes/schnorr.rs new file mode 100644 index 0000000..dcbbea4 --- /dev/null +++ b/gotham-server/src/routes/schnorr.rs @@ -0,0 +1,273 @@ +use super::super::Result; +use rocket::State; +use rocket_contrib::json::Json; + +use super::super::auth::jwt::Claims; +use super::super::storage::db; +use super::super::Config; + +use uuid::Uuid; +use multi_party_schnorr::protocols::thresholdsig::zilliqa_schnorr::*; +use self::SchnorrStruct::*; + +const PARTY1_INDEX: usize = 1; +const PARTY2_INDEX: usize = 2; +const PARAMS: Parameters = Parameters { + threshold: 1, + share_count: 2, +}; + +#[derive(Debug)] +pub enum SchnorrStruct { + Party1Key, + Party1KeyGenBroadcastMessage1, + Party2KeyGenBroadcastMessage1, + Party1KeyGenBroadcastMessage2, + Party2KeyGenBroadcastMessage2, + Party1VerifiableSecretShares, + Party2VerifiableSecretShares, + Party1SecretShares, + Party1SharedKey +} + +impl db::MPCStruct for SchnorrStruct { + fn to_string(&self) -> String { + format!("Schnorr{:?}", self) + } +} + +#[post("/schnorr/keygen/first", format = "json", data = "")] +pub fn keygen_first( + state: State, + claim: Claims, + party2_msg1: Json, +) -> Result> { + let id = Uuid::new_v4().to_string(); + db::insert( + &state.db, + &claim.sub, + &id, + &Party2KeyGenBroadcastMessage1, + &party2_msg1.0, + )?; + + let key: Keys = Keys::phase1_create(1); + db::insert( + &state.db, + &claim.sub, + &id, + &Party1Key, + &key, + )?; + + let (msg1, msg2) = key.phase1_broadcast(); + db::insert( + &state.db, + &claim.sub, + &id, + &Party1KeyGenBroadcastMessage1, + &msg1, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Party1KeyGenBroadcastMessage2, + &msg2, + )?; + + Ok(Json((id, msg1))) +} + +#[post("/schnorr/keygen//second", format = "json", data = "")] +pub fn keygen_second( + state: State, + claim: Claims, + id: String, + party2_msg2: Json, +) -> Result> { + let key: Keys = db::get( + &state.db, + &claim.sub, + &id, + &Party1Key)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let msg1: KeyGenBroadcastMessage1 = db::get( + &state.db, + &claim.sub, + &id, + &Party1KeyGenBroadcastMessage1)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let msg2: KeyGenBroadcastMessage2 = db::get( + &state.db, + &claim.sub, + &id, + &Party1KeyGenBroadcastMessage2)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let party2_msg1: KeyGenBroadcastMessage1 = db::get( + &state.db, + &claim.sub, + &id, + &Party2KeyGenBroadcastMessage1)? + .ok_or(format_err!("No data for such identifier {}", id))?; + + let (vss_scheme, secret_shares, _index) = key.phase1_verify_com_phase2_distribute( + &PARAMS, + &vec![msg2, party2_msg2.0.clone()], + &vec![msg1, party2_msg1], + &vec![PARTY1_INDEX, PARTY2_INDEX]) + .or_else(|e| Err(e))?; + db::insert( + &state.db, + &claim.sub, + &id, + &Party2KeyGenBroadcastMessage2, + &party2_msg2.0, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Party1VerifiableSecretShares, + &vss_scheme, + )?; + db::insert( + &state.db, +&claim.sub, + &id, + &Party1SecretShares, + &secret_shares, + )?; + let msg2: KeyGenBroadcastMessage2 = db::get( + &state.db, +&claim.sub, + &id, + &Party1KeyGenBroadcastMessage2)? + .ok_or(format_err!("No data for such identifier {}", id))?; + + Ok(Json(msg2)) +} + +#[post("/schnorr/keygen//third", format = "json", data = "")] +pub fn keygen_third( + state: State, + claim: Claims, + id: String, + party2_msg3: Json, +) -> Result> { + let key: Keys = db::get( + &state.db, +&claim.sub, + &id, + &Party1Key)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let party2_msg2: KeyGenBroadcastMessage2 = db::get( + &state.db, +&claim.sub, + &id, + &Party2KeyGenBroadcastMessage2)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let vss_scheme: VerifiableSS = db::get( + &state.db, +&claim.sub, + &id, + &Party1VerifiableSecretShares)? + .ok_or(format_err!("No data for such identifier {}", id))?; + let secret_shares: Vec = db::get( + &state.db, +&claim.sub, + &id, + &Party1SecretShares)? + .ok_or(format_err!("No data for such identifier {}", id))?; + + let shared_key: SharedKeys = key.phase2_verify_vss_construct_keypair( + &PARAMS, + &vec![key.y_i, party2_msg2.y_i], + &vec![secret_shares[key.party_index - 1], party2_msg3.0.secret_share], + &vec![vss_scheme.clone(), party2_msg3.0.vss_scheme.clone()], + &(key.party_index)) + .or_else(|e| Err(e))?; + + db::insert( + &state.db, +&claim.sub, + &id, + &Party1SharedKey, + &shared_key, + )?; + + db::insert( + &state.db, +&claim.sub, + &id, + &Party2VerifiableSecretShares, + &party2_msg3.0.vss_scheme, + )?; + + let msg3: KeyGenMessage3 = KeyGenMessage3 { + vss_scheme, + secret_share: secret_shares[PARTY2_INDEX - 1], + }; + + Ok(Json(msg3)) +} + +#[post("/schnorr/sign//", format = "json", data = "")] +pub fn sign( + state: State, + claim: Claims, + keygen_id: String, + eph_keygen_id: String, + party2_sign_msg1: Json, +) -> Result> { + let shared_key: SharedKeys = db::get( + &state.db, +&claim.sub, + &keygen_id, + &Party1SharedKey)? + .ok_or(format_err!("No data for such identifier {}", keygen_id))?; + let eph_shared_key: SharedKeys = db::get( + &state.db, +&claim.sub, + &eph_keygen_id, + &Party1SharedKey)? + .ok_or(format_err!("No data for such identifier {}", eph_keygen_id))?; + + let local_sig = LocalSig::compute( + &BigInt::to_vec(&party2_sign_msg1.message).as_slice(), + &eph_shared_key, + &shared_key); + + let vss_scheme: VerifiableSS = db::get( + &state.db, +&claim.sub, + &keygen_id, + &Party1VerifiableSecretShares)? + .ok_or(format_err!("No data for such identifier {}", keygen_id))?; + let party2_vss_scheme: VerifiableSS = db::get( + &state.db, +&claim.sub, + &keygen_id, + &Party2VerifiableSecretShares)? + .ok_or(format_err!("No data for such identifier {}", keygen_id))?; + let eph_vss_scheme: VerifiableSS = db::get( + &state.db, + &claim.sub, + &eph_keygen_id, + &Party1VerifiableSecretShares)? + .ok_or(format_err!("No data for such identifier {}", eph_keygen_id))?; + let party2_eph_vss_scheme: VerifiableSS = db::get( + &state.db, + &claim.sub, + &eph_keygen_id, + &Party2VerifiableSecretShares)? + .ok_or(format_err!("No data for such identifier {}", eph_keygen_id))?; + + LocalSig::verify_local_sigs( + &vec![local_sig, party2_sign_msg1.local_sig], + &vec![PARTY1_INDEX - 1, PARTY2_INDEX - 1], + &vec![vss_scheme, party2_vss_scheme], + &vec![eph_vss_scheme, party2_eph_vss_scheme]) + .or_else(|e| Err(format_err!("{}", e))) + .and_then(|_vss_scheme| Ok(Json(local_sig))) +} \ No newline at end of file diff --git a/gotham-server/src/server.rs b/gotham-server/src/server.rs index ee08a96..e8c28ad 100644 --- a/gotham-server/src/server.rs +++ b/gotham-server/src/server.rs @@ -15,9 +15,9 @@ use rocksdb; use rusoto_core::Region; use rusoto_dynamodb::DynamoDbClient; -use super::routes::ecdsa; -use super::routes::ping; +use super::routes::*; use super::storage::db; +use super::Config; use std::collections::HashMap; use std::str::FromStr; @@ -69,15 +69,10 @@ fn not_found(req: &Request) -> String { pub fn get_server() -> Rocket { let settings = get_settings_as_map(); - let db_config = ecdsa::Config { + let db_config = Config { db: get_db(settings.clone()), }; - match db::init(&db_config.db) { - Err(_e) => panic!("Error while initializing DB."), - _ => {} - }; - let auth_config = AuthConfig::load(settings.clone()); rocket::ignite() @@ -99,6 +94,13 @@ pub fn get_server() -> Rocket { ecdsa::rotate_third, ecdsa::rotate_fourth, ecdsa::recover, + schnorr::keygen_first, + schnorr::keygen_second, + schnorr::keygen_third, + schnorr::sign, + eddsa::keygen, + eddsa::sign_first, + eddsa::sign_second, ], ) .manage(db_config) diff --git a/gotham-server/src/storage/aws/dynamodb.rs b/gotham-server/src/storage/aws/dynamodb.rs index a3147eb..367c205 100644 --- a/gotham-server/src/storage/aws/dynamodb.rs +++ b/gotham-server/src/storage/aws/dynamodb.rs @@ -1,4 +1,3 @@ -use super::super::super::routes::ecdsa::Share; use super::error::*; use super::*; use failure; @@ -36,9 +35,12 @@ where customerId: user_id.to_string(), id: id.to_string(), }; + println!("identifier = {:?}", identifier); let mut item = serde_dynamodb::to_hashmap(&identifier).unwrap(); + println!("item #1 = {:?}", item); item.extend(serde_dynamodb::to_hashmap(&v).unwrap()); + println!("item #2 = {:?}", item); let put_item_input = PutItemInput { item: item, @@ -58,13 +60,14 @@ pub fn get<'a, T>( user_id: &str, id: &str, table_name: String, + require_customer_id: bool ) -> std::result::Result, failure::Error> where T: serde::de::Deserialize<'a>, { let mut query_key: HashMap = HashMap::new(); - if table_name.contains(&Share::Party1MasterKey.to_string()) { + if require_customer_id { query_key.insert( "customerId".to_string(), AttributeValue { @@ -82,7 +85,7 @@ where }, ); - info!("Querying table {}, key: {:?}", table_name, query_key); + println!("Querying table {}, key: {:?}", table_name, query_key); let query_item = GetItemInput { key: query_key, @@ -93,27 +96,36 @@ where match dynamodb_client.get_item(query_item).sync() { Ok(item_from_dynamo) => match item_from_dynamo.item { None => { - info!("nothing received from Dynamo, item may not exist"); + println!("nothing received from Dynamo, item may not exist"); Ok(None) } Some(mut attributes_map) => { // This is not the best we can do but if you look at the DBItemIdentifier above // we augment it with the ser/de of the actual object, so we remove extra fields // here. TODO: Is there something cleaner? + println!("#1"); attributes_map.remove(CUSTOMER_ID_IDENTIFIER); attributes_map.remove(ID_IDENTIFIER); + println!("#2, attributes_map = {:?}", attributes_map); let raw_item: serde_dynamodb::error::Result = serde_dynamodb::from_hashmap(attributes_map); + println!("#3"); match raw_item { - Ok(s) => Ok(Some(s)), - Err(_e) => Ok(None), + Ok(s) => { + println!("#4"); + Ok(Some(s)) + }, + Err(_e) => { + println!("#5, {}", _e); + Ok(None) + }, } } }, Err(err) => { - info!("Error retrieving object: {:?}", err); + println!("Error retrieving object: {:?}", err); Err(failure::err_msg(format!("{:?}", err))) } } diff --git a/gotham-server/src/storage/db.rs b/gotham-server/src/storage/db.rs index 90557b2..5e156ea 100644 --- a/gotham-server/src/storage/db.rs +++ b/gotham-server/src/storage/db.rs @@ -6,8 +6,6 @@ // License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // - -use super::super::routes::ecdsa; use super::super::Result; use rocksdb; use serde; @@ -19,46 +17,29 @@ pub enum DB { AWS(rusoto_dynamodb::DynamoDbClient, String), } -fn idify(user_id: &str, id: &str, name: &ecdsa::Share) -> String { - format!("{}_{}_{}", user_id, id, name.to_string()) -} - -pub fn init(db: &DB) -> Result<()> { - match db { - DB::AWS(_dynamodb_client, _env) => { - // !!! Keep this code commented unless you are willing to risk an implicit table - // creation which can lead to data corruption. This needs disciplines and tables - // Schema would better live in a cloud formation !!! +pub trait MPCStruct { + fn to_string(&self) -> String; - /* - println!("Creating tables if necessary..."); - for share_field in ecdsa::Share::iterator() { - let name = format!("{}", share_field.to_string()); - let table_name = calculate_table_name(&name.to_string(), &env); + fn to_table_name(&self, env: &str) -> String { + format!("{}_{}", env, self.to_string()) + } - match aws::dynamodb::create_table_if_needed(&dynamodb_client, &table_name, 1, 1) { - Err(e) => return Err(format_err!("{}", e)), - _ => {} - }; - match aws::dynamodb::wait_for_table(&dynamodb_client, &table_name) { - Err(e) => return Err(format_err!("{}", e)), - _ => {} - } - } - */ - Ok(()) - } - _ => Ok(()), + fn require_customer_id(&self) -> bool { + true } } -pub fn insert(db: &DB, user_id: &str, id: &str, name: &ecdsa::Share, v: T) -> Result<()> +fn idify(user_id: &str, id: &str, name: &dyn MPCStruct) -> String { + format!("{}_{}_{}", user_id, id, name.to_string()) +} + +pub fn insert(db: &DB, user_id: &str, id: &str, name: &dyn MPCStruct, v: T) -> Result<()> where T: serde::ser::Serialize, { match db { DB::AWS(dynamodb_client, env) => { - let table_name = calculate_table_name(&name.to_string(), &env); + let table_name = name.to_table_name(env); aws::dynamodb::insert(&dynamodb_client, user_id, id, &table_name, v)?; Ok(()) } @@ -71,19 +52,25 @@ where } } -pub fn get(db: &DB, user_id: &str, id: &str, name: &ecdsa::Share) -> Result> +pub fn get(db: &DB, user_id: &str, id: &str, name: &dyn MPCStruct) -> Result> where T: serde::de::DeserializeOwned, { match db { DB::AWS(dynamodb_client, env) => { - let table_name = calculate_table_name(&name.to_string(), &env); - let res: Option = aws::dynamodb::get(&dynamodb_client, user_id, id, table_name)?; + let table_name = name.to_table_name(env); + println!("table_name = {}", table_name); + let require_customer_id = name.require_customer_id(); + println!("require_customer_id = {}", require_customer_id); + println!("user_id = {}", user_id); + println!("id = {}", id); + let res: Option = aws::dynamodb::get(&dynamodb_client, user_id, id, table_name, require_customer_id)?; + println!("res.is_none() = {}", res.is_none()); Ok(res) } DB::Local(rocksdb_client) => { let identifier = idify(user_id, id, name); - info!("Getting from db ({})", identifier); + debug!("Getting from db ({})", identifier); let db_option = rocksdb_client.get(identifier.as_ref())?; let vec_option: Option> = db_option.map(|v| v.to_vec()); @@ -94,12 +81,3 @@ where } } } - -fn calculate_table_name(name: &str, env: &str) -> String { - if !name.contains(&ecdsa::Share::Party1MasterKey.to_string()) { - return format!("{}-gotham-{}", env, name); - } - - // This is ugly, TODO: handle this properly in a configuration (when or not to use 'gotham' in the table name) - return format!("{}_{}", env, name); -} diff --git a/gotham-utilities/server/docker-build-img/Dockerfile b/gotham-utilities/server/docker-build-img/Dockerfile index 0191df8..e3c9ea4 100644 --- a/gotham-utilities/server/docker-build-img/Dockerfile +++ b/gotham-utilities/server/docker-build-img/Dockerfile @@ -16,27 +16,29 @@ RUN apt-get update && apt-get install -y \ RUN curl -sL https://deb.nodesource.com/setup_11.x | bash - RUN apt-get install -y nodejs -# Adding utilities for JWT and Cognito auth -ADD gotham-utilities /gotham-utilities +ADD . / + WORKDIR /gotham-utilities/server/cognito RUN ["npm", "install"] -WORKDIR / - # Rust ARG CHANNEL="nightly" -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${CHANNEL} +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly-2019-07-10 -# Server ENV rocket_address=0.0.0.0 ENV rocket_port=8000 -ENV db=AWS EXPOSE 8000 -ADD gotham-server /app -WORKDIR /app -RUN ["/root/.cargo/bin/cargo", "update"] +WORKDIR /gotham-server RUN ["/root/.cargo/bin/cargo", "build", "--release"] + + +WORKDIR /integration-tests +RUN ["/root/.cargo/bin/cargo", "test", "--release", "--", "--nocapture"] + +# Server +ENV db=AWS +WORKDIR /gotham-server CMD ["/root/.cargo/bin/cargo", "run", "--release"] diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index bcb002c..183a25d 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -5,23 +5,12 @@ authors = ["oleiba "] edition = "2018" [dependencies] -rocket = "0.4.0" -rocket_contrib = "0.4.0" -rocksdb = "0.10.1" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" -strum = "0.12.0" -strum_macros = "0.12.0" time-test = "0.2.1" log = "0.4" time = "*" -uuid = { version = "0.7", features = ["v4"] } -clap = { version = "2.32", features = ["yaml"] } -reqwest = "0.9.5" -itertools = "0.8.0" -hex = "0.3.2" -bitcoin = "0.16.0" [dependencies.gotham-server] path = "../gotham-server" @@ -29,18 +18,5 @@ path = "../gotham-server" [dependencies.gotham-client] path = "../gotham-client" -[dependencies.kms] -git = "https://github.com/KZen-networks/kms" - -[dependencies.multi-party-ecdsa] -git = "https://github.com/KZen-networks/multi-party-ecdsa" - -[dependencies.curv] -git = "https://github.com/KZen-networks/curv" -features = ["curvesecp256k1"] - [patch.crates-io] -rust-gmp = { version = "0.5.0", features = ["serde_support"], git = "https://github.com/KZen-networks/rust-gmp" } - -[dependencies.centipede] -git = "https://github.com/KZen-networks/centipede" \ No newline at end of file +rust-gmp = { version = "0.5.0", features = ["serde_support"], git = "https://github.com/KZen-networks/rust-gmp" } \ No newline at end of file diff --git a/integration-tests/src/test.rs b/integration-tests/src/test.rs index f46520a..1dbe188 100644 --- a/integration-tests/src/test.rs +++ b/integration-tests/src/test.rs @@ -2,41 +2,90 @@ mod tests { extern crate server_lib; extern crate client_lib; - extern crate bitcoin; - extern crate kms; - use client_lib::api::{PrivateShare, ClientShim}; - use curv::arithmetic::traits::Converter; - use curv::BigInt; + use client_lib::*; use server_lib::server; use std::{thread, time}; #[test] - fn test_api() { - // Rocket server is blocking, so we spawn a new thread. - thread::spawn(move || { - server::get_server().launch(); - }); + fn test_ecdsa() { + spawn_server(); + + let client_shim = ClientShim::new("http://localhost:8000".to_string(), None); + + let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); + + for y in 0..10 { + let x_pos = BigInt::from(0); + let y_pos = BigInt::from(y); + println!("Deriving child_master_key at [x: {}, y:{}]", x_pos, y_pos); + + let child_master_key = ps + .master_key + .get_child(vec![x_pos.clone(), y_pos.clone()]); + + let msg: BigInt = BigInt::from(y + 1); // arbitrary message + let signature = + ecdsa::sign(&client_shim, msg, &child_master_key, x_pos, y_pos, &ps.id) + .expect("ECDSA signature failed"); + + println!( + "signature = (r: {}, s: {})", + signature.r.to_hex(), + signature.s.to_hex() + ); + } + } + + #[test] + fn test_schnorr() { + spawn_server(); + + let client_shim = ClientShim::new("http://localhost:8000".to_string(), None); + + let share: schnorr::Share = schnorr::generate_key(&client_shim).unwrap(); + + let msg: BigInt = BigInt::from(1234); // arbitrary message + let signature = schnorr::sign(&client_shim, msg, &share) + .expect("Schnorr signature failed"); + + println!( + "signature = (e: {:?}, s: {:?})", + signature.e, + signature.s + ); + } + + #[test] + fn test_eddsa() { + spawn_server(); let client_shim = ClientShim::new("http://localhost:8000".to_string(), None); let five_seconds = time::Duration::from_millis(5000); thread::sleep(five_seconds); - let ps: PrivateShare = client_lib::api::get_master_key(&client_shim); + let (key_pair, key_agg, id) = client_lib::eddsa::generate_key(&client_shim).unwrap(); - let x_pos = BigInt::from(0); - let y_pos = BigInt::from(0); - let child_master_key = ps - .master_key - .get_child(vec![x_pos.clone(), y_pos.clone()]); + let message = BigInt::from(1234); + let signature = + client_lib::eddsa::sign(&client_shim, message, &key_pair, &key_agg, &id) + .expect("EdDSA signature failed"); - let msg: BigInt = BigInt::from(1234); // arbitrary message - let signature = client_lib::api::sign(&client_shim, msg, &child_master_key, x_pos, y_pos, &ps.id); println!( - "signature = (r: {}, s: {})", - signature.r.to_hex(), - signature.s.to_hex() + "signature = (R: {}, s: {})", + signature.R.bytes_compressed_to_big_int().to_hex(), + signature.s.to_big_int().to_hex() ); } + + fn spawn_server() { + // Rocket server is blocking, so we spawn a new thread. + thread::spawn(move || { + server::get_server().launch(); + }); + + let five_seconds = time::Duration::from_millis(5000); + thread::sleep(five_seconds); + } }