diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index e2d9380..0000000 --- a/Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -FROM 542401451332.dkr.ecr.us-west-2.amazonaws.com/gothambuild:latest - -EXPOSE 8080 -CMD ["/root/.cargo/bin/cargo", "run", "--release"] - - - diff --git a/Dockerrun.aws.json b/Dockerrun.aws.json deleted file mode 100644 index f11ebe7..0000000 --- a/Dockerrun.aws.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "AWSEBDockerrunVersion": "1", - "Ports": [ - { - "ContainerPort": 8080 - } - ] -} diff --git a/gotham-client/.gitignore b/gotham-client/.gitignore index 8890fae..c8278a8 100644 --- a/gotham-client/.gitignore +++ b/gotham-client/.gitignore @@ -2,7 +2,7 @@ # will have compiled files and executables /target/ -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# Remove Cargo.lock from .gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock @@ -11,7 +11,7 @@ Cargo.lock .idea db/ *.priv -wallet.data +wallet/wallet.data escrow/client.backup escrow/escrow-sk.json escrow/sk-recovered.json diff --git a/gotham-client/Settings.toml b/gotham-client/Settings.toml new file mode 100644 index 0000000..67d6374 --- /dev/null +++ b/gotham-client/Settings.toml @@ -0,0 +1 @@ +endpoint = "http://localhost:8000" \ No newline at end of file diff --git a/gotham-client/escrow/.gitkeep b/gotham-client/escrow/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/gotham-client/src/api/mod.rs b/gotham-client/src/api/mod.rs new file mode 100644 index 0000000..dbbbaf1 --- /dev/null +++ b/gotham-client/src/api/mod.rs @@ -0,0 +1,36 @@ +use super::ecdsa::{keygen, sign}; +use kms::ecdsa::two_party::MasterKey2; +use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::party_one; +use reqwest; + +pub struct ClientShim { + pub client: reqwest::Client, + pub endpoint: String, +} + +impl ClientShim { + pub fn new(endpoint: String) -> ClientShim { + let client = reqwest::Client::new(); + ClientShim { client, 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: bitcoin::util::hash::Sha256dHash, + mk: &MasterKey2, + pos: u32, + id: &String, +) -> party_one::Signature { + sign::sign(&client_shim, message, mk, pos, id) +} diff --git a/gotham-client/src/ecdsa/keygen.rs b/gotham-client/src/ecdsa/keygen.rs index db89858..1a4b338 100644 --- a/gotham-client/src/ecdsa/keygen.rs +++ b/gotham-client/src/ecdsa/keygen.rs @@ -7,7 +7,6 @@ // version 3 of the License, or (at your option) any later version. // -use reqwest; use serde_json; use time::PreciseTime; @@ -16,29 +15,26 @@ 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::super::utilities::requests; -use super::super::wallet; const KG_PATH_PRE: &str = "ecdsa/keygen"; -pub fn get_master_key(client: &reqwest::Client) -> wallet::PrivateShares { +pub fn get_master_key(client_shim: &api::ClientShim) -> api::PrivateShare { let start = PreciseTime::now(); - let res_body = requests::post(client, &format!("{}/first", KG_PATH_PRE)).unwrap(); + 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(); - println!("(id: {}) Generating master key...", id); - 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, &format!("{}/{}/second", KG_PATH_PRE, id), body).unwrap(); + requests::postb(client_shim, &format!("{}/{}/second", KG_PATH_PRE, id), body).unwrap(); - // TODO: second param not needed let kg_party_one_second_message: party1::KeyGenParty1Message2 = serde_json::from_str(&res_body).unwrap(); @@ -52,7 +48,8 @@ pub fn get_master_key(client: &reqwest::Client) -> wallet::PrivateShares { let body = &party_two_second_message.pdl_first_message; - let res_body = requests::postb(client, &format!("{}/{}/third", KG_PATH_PRE, id), body).unwrap(); + 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(); @@ -64,7 +61,7 @@ pub fn get_master_key(client: &reqwest::Client) -> wallet::PrivateShares { let body = &party_2_pdl_second_message; let res_body = - requests::postb(client, &format!("{}/{}/fourth", KG_PATH_PRE, id), body).unwrap(); + 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(); @@ -76,8 +73,11 @@ pub fn get_master_key(client: &reqwest::Client) -> wallet::PrivateShares { ) .expect("pdl error party1"); - let res_body = - requests::post(client, &format!("{}/{}/chaincode/first", KG_PATH_PRE, id)).unwrap(); + let res_body = 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(); @@ -87,7 +87,7 @@ pub fn get_master_key(client: &reqwest::Client) -> wallet::PrivateShares { let body = &cc_party_two_first_message.d_log_proof; let res_body = requests::postb( - client, + client_shim, &format!("{}/{}/chaincode/second", KG_PATH_PRE, id), body, ) @@ -117,10 +117,8 @@ pub fn get_master_key(client: &reqwest::Client) -> wallet::PrivateShares { &party_two_paillier, ); - println!("(id: {}) Master key gen completed", id); - let end = PreciseTime::now(); println!("(id: {}) Took: {}", id, start.to(end)); - wallet::PrivateShares { id, master_key } + api::PrivateShare { id, master_key } } diff --git a/gotham-client/src/ecdsa/mod.rs b/gotham-client/src/ecdsa/mod.rs index 4cd6a10..0f1da6b 100644 --- a/gotham-client/src/ecdsa/mod.rs +++ b/gotham-client/src/ecdsa/mod.rs @@ -6,6 +6,6 @@ // License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // - pub mod keygen; pub mod rotate; +pub mod sign; diff --git a/gotham-client/src/ecdsa/rotate.rs b/gotham-client/src/ecdsa/rotate.rs index 7303f10..871ae0b 100644 --- a/gotham-client/src/ecdsa/rotate.rs +++ b/gotham-client/src/ecdsa/rotate.rs @@ -7,9 +7,10 @@ // version 3 of the License, or (at your option) any later version. // -use reqwest; use serde_json; +use super::super::api; +use super::super::api::PrivateShare; use super::super::utilities::requests; use super::super::wallet; use curv::cryptographic_primitives::twoparty::coin_flip_optimal_rounds; @@ -21,9 +22,9 @@ use std::collections::HashMap; const ROT_PATH_PRE: &str = "ecdsa/rotate"; -pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wallet::Wallet { - let id = &wallet.private_shares.id.clone(); - let res_body = requests::post(client, &format!("{}/{}/first", ROT_PATH_PRE, id)).unwrap(); +pub fn rotate_master_key(wallet: wallet::Wallet, client_shim: &api::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(); @@ -34,7 +35,7 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wa let body = &coin_flip_party2_first_message; let res_body = requests::postb( - client, + client_shim, &format!("{}/{}/second", ROT_PATH_PRE, id.clone()), body, ) @@ -52,7 +53,7 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wa ); let result_rotate_party_one_first_message = wallet - .private_shares + .private_share .master_key .rotate_first_message(&random2, &rotation_party1_first_message); if result_rotate_party_one_first_message.is_err() { @@ -65,7 +66,7 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wa let body = &rotation_party_two_first_message; let res_body = requests::postb( - client, + client_shim, &format!("{}/{}/third", ROT_PATH_PRE, id.clone()), body, ) @@ -79,7 +80,7 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wa let body = &rotation_party_two_second_message; let res_body = requests::postb( - client, + client_shim, &format!("{}/{}/fourth", ROT_PATH_PRE, id.clone()), body, ) @@ -89,7 +90,7 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wa serde_json::from_str(&res_body).unwrap(); let result_rotate_party_one_third_message = - wallet.private_shares.master_key.rotate_third_message( + wallet.private_share.master_key.rotate_third_message( &random2, &party_two_paillier, &party_two_pdl_chal, @@ -102,15 +103,16 @@ pub fn rotate_master_key(wallet: wallet::Wallet, client: &reqwest::Client) -> wa let party_two_master_key_rotated = result_rotate_party_one_third_message.unwrap(); - let private_shares = wallet::PrivateShares { - id: wallet.private_shares.id.clone(), + let private_share = PrivateShare { + id: wallet.private_share.id.clone(), master_key: party_two_master_key_rotated, }; + let addresses_derivation_map = HashMap::new(); let mut wallet_after_rotate = wallet::Wallet { id: wallet.id.clone(), network: wallet.network.clone(), - private_shares, + private_share, last_derived_pos: wallet.last_derived_pos.clone(), addresses_derivation_map, }; diff --git a/gotham-client/src/ecdsa/sign.rs b/gotham-client/src/ecdsa/sign.rs new file mode 100644 index 0000000..f5fc367 --- /dev/null +++ b/gotham-client/src/ecdsa/sign.rs @@ -0,0 +1,67 @@ +use bitcoin; +use curv::BigInt; +use kms::ecdsa::two_party::party2; +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 curv::arithmetic::traits::Converter; + +#[derive(Serialize, Deserialize)] +pub struct SignSecondMsgRequest { + pub message: BigInt, + pub party_two_sign_message: party2::SignMessage, + pub pos_child_key: u32, +} + +pub fn sign( + client_shim: &api::ClientShim, + message: bitcoin::util::hash::Sha256dHash, + mk: &MasterKey2, + pos: u32, + id: &String, +) -> party_one::Signature { + 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(); + + let party_two_sign_message = mk.sign_second_message( + &eph_ec_key_pair_party2, + eph_comm_witness.clone(), + &sign_party_one_first_message, + &BigInt::from_hex(&message.le_hex_string()), + ); + + let signature: party_one::Signature = + get_signature(client_shim, message, party_two_sign_message, pos, &id); + + signature +} + +fn get_signature( + client_shim: &api::ClientShim, + message: bitcoin::util::hash::Sha256dHash, + party_two_sign_message: party2::SignMessage, + pos_child_key: u32, + id: &String, +) -> party_one::Signature { + let request: SignSecondMsgRequest = SignSecondMsgRequest { + message: BigInt::from_hex(&message.le_hex_string()), + party_two_sign_message, + pos_child_key, + }; + + let res_body = + requests::postb(client_shim, &format!("/ecdsa/sign/{}/second", id), &request).unwrap(); + + let signature: party_one::Signature = serde_json::from_str(&res_body).unwrap(); + signature +} diff --git a/gotham-client/src/lib.rs b/gotham-client/src/lib.rs index 3471c26..da203d1 100644 --- a/gotham-client/src/lib.rs +++ b/gotham-client/src/lib.rs @@ -13,6 +13,7 @@ extern crate kms; extern crate multi_party_ecdsa; extern crate reqwest; extern crate zk_paillier; +extern crate config; #[macro_use] extern crate serde_derive; @@ -30,6 +31,7 @@ extern crate secp256k1; extern crate time; extern crate uuid; +pub mod api; pub mod ecdsa; pub mod escrow; pub mod utilities; diff --git a/gotham-client/src/main.rs b/gotham-client/src/main.rs index 8128981..fe446b5 100644 --- a/gotham-client/src/main.rs +++ b/gotham-client/src/main.rs @@ -11,21 +11,33 @@ extern crate clap; use clap::App; +use client_lib::api; use client_lib::escrow; use client_lib::wallet; -use reqwest; use time::PreciseTime; +use std::collections::HashMap; + fn main() { let yaml = load_yaml!("../cli.yml"); let matches = App::from_yaml(yaml).get_matches(); - let client = reqwest::Client::new(); + let mut settings = config::Config::default(); + settings + // Add in `./Settings.toml` + .merge(config::File::with_name("Settings")).unwrap() + // Add in settings from the environment (with prefix "APP") + // Eg.. `APP_DEBUG=1 ./target/app` would set the `debug` key + .merge(config::Environment::new()).unwrap(); + let hm = settings.try_into::>().unwrap(); + let endpoint = hm.get("endpoint").unwrap(); + let client_shim = api::ClientShim::new(endpoint.to_string()); + let network = "testnet".to_string(); if let Some(_matches) = matches.subcommand_matches("create-wallet") { println!("Network: [{}], Creating wallet", network); - let wallet = wallet::Wallet::new(&client, &network); + let wallet = wallet::Wallet::new(&client_shim, &network); wallet.save(); println!("Network: [{}], Wallet saved to disk", &network); @@ -79,7 +91,7 @@ fn main() { println!("backup recovery in process 📲 (it can take some time)..."); let start = PreciseTime::now(); - wallet::Wallet::recover_and_save_shares(escrow, &network, &client); + wallet::Wallet::recover_and_save_share(escrow, &network, &client_shim); let end = PreciseTime::now(); println!(" Backup recovered 💾(Took: {})", start.to(end)); @@ -87,7 +99,7 @@ fn main() { println!("Rotating secret shares"); let start = PreciseTime::now(); - let wallet = wallet.rotate(&client); + let wallet = wallet.rotate(&client_shim); wallet.save(); let end = PreciseTime::now(); @@ -97,9 +109,9 @@ fn main() { let to: &str = matches.value_of("to").unwrap(); let amount_btc: &str = matches.value_of("amount").unwrap(); let txid = wallet.send( - &client, to.to_string(), amount_btc.to_string().parse::().unwrap(), + &client_shim, ); wallet.save(); println!( diff --git a/gotham-client/src/utilities/requests.rs b/gotham-client/src/utilities/requests.rs index 0b1d30e..5224425 100644 --- a/gotham-client/src/utilities/requests.rs +++ b/gotham-client/src/utilities/requests.rs @@ -7,15 +7,16 @@ // version 3 of the License, or (at your option) any later version. // -use reqwest; +use super::super::api; use serde; use time::PreciseTime; -pub fn post(client: &reqwest::Client, path: &str) -> Option { +pub fn post(client_shim: &api::ClientShim, path: &str) -> Option { let start = PreciseTime::now(); - let res = client - .post(&format!("http://127.0.0.1:8000/{}", path)) + let res = client_shim + .client + .post(&format!("{}/{}", client_shim.endpoint, path)) .json("{}") .send(); @@ -26,14 +27,15 @@ pub fn post(client: &reqwest::Client, path: &str) -> Option { Some(res.unwrap().text().unwrap()) } -pub fn postb(client: &reqwest::Client, path: &str, body: T) -> Option +pub fn postb(client_shim: &api::ClientShim, path: &str, body: T) -> Option where T: serde::ser::Serialize, { let start = PreciseTime::now(); - let res = client - .post(&format!("http://127.0.0.1:8000/{}", path)) + let res = client_shim + .client + .post(&format!("{}/{}", client_shim.endpoint, path)) .json(&body) .send(); diff --git a/gotham-client/src/wallet/mod.rs b/gotham-client/src/wallet/mod.rs index 96b3f71..095d730 100644 --- a/gotham-client/src/wallet/mod.rs +++ b/gotham-client/src/wallet/mod.rs @@ -17,8 +17,6 @@ use curv::{BigInt, GE}; use electrumx_client::{electrumx_client::ElectrumxClient, interface::Electrumx}; use kms::ecdsa::two_party::MasterKey2; use kms::ecdsa::two_party::*; -use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::*; -use reqwest; use serde_json; use std::fs; use uuid::Uuid; @@ -27,7 +25,9 @@ use centipede::juggling::proof_system::{Helgamalsegmented, Proof}; use centipede::juggling::segmentation::Msegmentation; use kms::chain_code::two_party::party2::ChainCode2; -use super::ecdsa::{keygen, rotate}; +use super::api; +use super::api::PrivateShare; +use super::ecdsa::rotate; use super::escrow; use super::utilities::requests; use curv::arithmetic::traits::Converter; @@ -72,12 +72,6 @@ pub struct GetWalletBalanceResponse { pub unconfirmed: u64, } -#[derive(Serialize, Deserialize)] -pub struct PrivateShares { - pub id: String, - pub master_key: MasterKey2, -} - #[derive(Serialize, Deserialize)] pub struct AddressDerivation { pub pos: u32, @@ -88,15 +82,15 @@ pub struct AddressDerivation { pub struct Wallet { pub id: String, pub network: String, - pub private_shares: PrivateShares, + pub private_share: PrivateShare, pub last_derived_pos: u32, pub addresses_derivation_map: HashMap, } impl Wallet { - pub fn new(client: &reqwest::Client, net: &String) -> Wallet { + pub fn new(client_shim: &api::ClientShim, net: &String) -> Wallet { let id = Uuid::new_v4().to_string(); - let private_shares = keygen::get_master_key(client); + let private_share = api::get_master_key(client_shim); let last_derived_pos = 0; let addresses_derivation_map = HashMap::new(); let network = net.clone(); @@ -104,20 +98,20 @@ impl Wallet { Wallet { id, network, - private_shares, + private_share, last_derived_pos, addresses_derivation_map, } } - pub fn rotate(self, client: &reqwest::Client) -> Self { - rotate::rotate_master_key(self, client) + pub fn rotate(self, client_shim: &api::ClientShim) -> Self { + rotate::rotate_master_key(self, client_shim) } pub fn backup(&self, escrow_service: escrow::Escrow) { let g: GE = ECPoint::generator(); let y = escrow_service.get_public_key(); - let (segments, encryptions) = self.private_shares.master_key.private.to_encrypted_segment( + let (segments, encryptions) = self.private_share.master_key.private.to_encrypted_segment( &escrow::SEGMENT_SIZE, escrow::NUM_SEGMENTS, &y, @@ -129,9 +123,9 @@ impl Wallet { let client_backup_json = serde_json::to_string(&( encryptions, proof, - self.private_shares.master_key.public.clone(), - self.private_shares.master_key.chain_code.clone(), - self.private_shares.id.clone(), + self.private_share.master_key.public.clone(), + self.private_share.master_key.chain_code.clone(), + self.private_share.id.clone(), )) .unwrap(); @@ -165,10 +159,10 @@ impl Wallet { } } - pub fn recover_and_save_shares( + pub fn recover_and_save_share( escrow_service: escrow::Escrow, net: &String, - client: &reqwest::Client, + client_shim: &api::ClientShim, ) -> Wallet { let g: GE = ECPoint::generator(); let y_priv = escrow_service.get_private_key(); @@ -187,7 +181,7 @@ impl Wallet { let client_master_key_recovered = MasterKey2::recover_master_key(sk, public_data, chain_code2); - let res_body = requests::post(client, &format!("ecdsa/{}/recover", key_id)).unwrap(); + let res_body = 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 }; @@ -200,7 +194,7 @@ impl Wallet { let new_wallet = Wallet { id, network, - private_shares: PrivateShares { + private_share: PrivateShare { master_key: client_master_key_recovered, id: key_id, }, @@ -234,9 +228,9 @@ impl Wallet { pub fn send( &mut self, - client: &reqwest::Client, to_address: String, amount_btc: f32, + client_shim: &api::ClientShim, ) -> String { let selected = self.select_tx_in(amount_btc); if selected.is_empty() { @@ -306,17 +300,16 @@ impl Wallet { (selected[i].value as u32).into(), ); - let party_two_sign_message = self.sign(client, sig_hash, &mk); - - let signatures = self.get_signature( - client, + let signature = api::sign( + client_shim, sig_hash, - party_two_sign_message, + &mk, address_derivation.pos, + &self.private_share.id, ); - let mut v = BigInt::to_vec(&signatures.r); - v.extend(BigInt::to_vec(&signatures.s)); + 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(); @@ -336,63 +329,8 @@ impl Wallet { txid.unwrap() } - fn sign( - &self, - client: &reqwest::Client, - message: bitcoin::util::hash::Sha256dHash, - mk: &MasterKey2, - ) -> party2::SignMessage { - 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, - &format!("/ecdsa/sign/{}/first", self.private_shares.id), - &request, - ) - .unwrap(); - - let sign_party_one_first_message: party_one::EphKeyGenFirstMsg = - serde_json::from_str(&res_body).unwrap(); - - let party_two_sign_message = mk.sign_second_message( - &eph_ec_key_pair_party2, - eph_comm_witness.clone(), - &sign_party_one_first_message, - &BigInt::from_hex(&message.le_hex_string()), - ); - - party_two_sign_message - } - - fn get_signature( - &self, - client: &reqwest::Client, - message: bitcoin::util::hash::Sha256dHash, - party_two_sign_message: party2::SignMessage, - pos_child_key: u32, - ) -> party_one::Signature { - let request: SignSecondMsgRequest = SignSecondMsgRequest { - message: BigInt::from_hex(&message.le_hex_string()), - party_two_sign_message, - pos_child_key, - }; - - let res_body = requests::postb( - client, - &format!("/ecdsa/sign/{}/second", self.private_shares.id), - &request, - ) - .unwrap(); - - let signature: party_one::Signature = serde_json::from_str(&res_body).unwrap(); - - signature - } - pub fn get_new_bitcoin_address(&mut self) -> bitcoin::Address { - let (pos, mk) = Self::derive_new_key(&self.private_shares, self.last_derived_pos); + 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()); @@ -406,7 +344,7 @@ impl Wallet { pub fn derived(&mut self) { for i in 0..self.last_derived_pos { - let (pos, mk) = Self::derive_new_key(&self.private_shares, i); + 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()); @@ -516,7 +454,7 @@ impl Wallet { for n in init..=last_pos { let mk = self - .private_shares + .private_share .master_key .get_child(vec![BigInt::from(n)]); let bitcoin_address = Self::to_bitcoin_address(&mk, self.get_bitcoin_network()); @@ -527,10 +465,10 @@ impl Wallet { response } - fn derive_new_key(private_shares: &PrivateShares, pos: u32) -> (u32, MasterKey2) { + fn derive_new_key(private_share: &PrivateShare, pos: u32) -> (u32, MasterKey2) { let last_pos: u32 = pos + 1; - let last_child_master_key = private_shares + let last_child_master_key = private_share .master_key .get_child(vec![BigInt::from(last_pos)]); diff --git a/gotham-server/Cargo.toml b/gotham-server/Cargo.toml index 1be000e..8b06507 100644 --- a/gotham-server/Cargo.toml +++ b/gotham-server/Cargo.toml @@ -19,12 +19,20 @@ rocksdb = "0.10.1" serde = "1.0" serde_json = "1.0" serde_derive = "1.0" +rusoto_dynamodb = "0.36.0" +rusoto_core = "0.36.0" strum = "0.12.0" strum_macros = "0.12.0" time-test = "0.2.1" log = "0.4" time = "*" +config = "0.9.2" uuid = { version = "0.7", features = ["v4"] } +error-chain = "0.12.0" +failure = "0.1.5" +jsonwebtoken = "5" +rust-crypto = "0.2" +hex = "0.3.2" [dependencies.zk-paillier] git = "https://github.com/KZen-networks/zk-paillier" @@ -39,5 +47,8 @@ git = "https://github.com/KZen-networks/multi-party-ecdsa" git = "https://github.com/KZen-networks/curv" features = ["ec_secp256k1"] +[dependencies.serde_dynamodb] +git = "https://github.com/KZen-networks/serde_dynamodb" + [patch.crates-io] rust-gmp = { version = "0.5.0", features = ["serde_support"], git = "https://github.com/KZen-networks/rust-gmp" } diff --git a/gotham-server/README.md b/gotham-server/README.md index 030e2b3..d149efb 100644 --- a/gotham-server/README.md +++ b/gotham-server/README.md @@ -12,15 +12,21 @@ cd gotham-city/gotham-server cargo run --release ``` +* By default, the server will use a local [RocksDB](https://rocksdb.org/).
+Optionally, it can use a remote [AWS DynamoDB](https://aws.amazon.com/dynamodb/), +by setting the environment variable `DB` to the value `AWS`, and the AWS credentials `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY`. + + + ### Running tests #### Without timing output ```bash -cargo test --release +RUST_TEST_THREADS=1 cargo test --release ``` #### With timing output ```bash -cargo test --release -- --nocapture +RUST_TEST_THREADS=1 cargo test --release -- --nocapture ``` Example: @@ -60,3 +66,8 @@ PT0.000002242S Client: party2 fourth message ` * `#[post("/keygen//fourth", format = "json", data = "")] ` +* `#[post("/ecdsa/sign//first", + format = "json", + data = "")]` +* `#[post("/ecdsa/sign//second", format = "json", data = "")]` + diff --git a/gotham-server/Rocket.toml b/gotham-server/Rocket.toml index 5409c7c..a1a4b2f 100644 --- a/gotham-server/Rocket.toml +++ b/gotham-server/Rocket.toml @@ -1,5 +1,5 @@ [development] -address = "127.0.0.1" +address = "localhost" port = 8000 keep_alive = 5 log = "normal" \ No newline at end of file diff --git a/gotham-server/Settings.toml b/gotham-server/Settings.toml new file mode 100644 index 0000000..9a59859 --- /dev/null +++ b/gotham-server/Settings.toml @@ -0,0 +1,9 @@ +# "local" or "aws" +db = "local" +# if db = aws (also set environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY): +# aws_region = + +region = "" # Override with ENV variable! +pool_id = "" # Override with ENV variable! +issuer = "" # Override with ENV variable! +audience = "" # Override with ENV variable! diff --git a/gotham-server/src/auth/cognito.rs b/gotham-server/src/auth/cognito.rs new file mode 100644 index 0000000..cd7026a --- /dev/null +++ b/gotham-server/src/auth/cognito.rs @@ -0,0 +1,149 @@ +// 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::jwt::{decode_header_from_token, get_claims, Claims}; +use super::PublicKey; +use hex; +use jwt::Algorithm; +use rocket::http::Status; +use rocket::request::{self, FromRequest, Request}; +use rocket::Outcome; +use rocket::State; +use serde_json; +use std::collections::HashMap; +use std::process::Command; + +use super::super::server::AuthConfig; +use super::passthrough; + +const ALGORITHM: Algorithm = Algorithm::RS256; +const TOKEN_TYPE: &str = "Bearer"; + +pub fn verify( + issuer: &String, + audience: &String, + region: &String, + pool_id: &String, + authorization_header: &String, +) -> Result { + let mut header_parts = authorization_header.split_whitespace(); + let token_type = header_parts.next(); + assert_eq!(token_type, Some(TOKEN_TYPE)); + + let token = header_parts.next().unwrap(); + let header = match decode_header_from_token(token.to_string()) { + Ok(h) => h, + Err(_) => return Err(()), + }; + + let key_set_str: String = match get_jwt_to_pems(region, pool_id) { + Ok(k) => k, + Err(_) => return Err(()), + }; + + let key_set: HashMap = match serde_json::from_str(&key_set_str) { + Ok(k) => k, + Err(_) => return Err(()), + }; + + let header_kid = header.kid.unwrap(); + + if !key_set.contains_key(&header_kid) { + return Err(()); + } + + let key = key_set.get(&header_kid).unwrap(); + + let secret = hex::decode(&key.der).unwrap(); + let algorithms: Vec = vec![ALGORITHM]; + + get_claims(issuer, audience, &token.to_string(), &secret, algorithms) +} + +fn get_jwt_to_pems(region: &String, pool_id: &String) -> Result { + match Command::new("node") + .arg("jwt-to-pems.js") + .arg(format!("--region={}", region)) + .arg(format!("--poolid={}", pool_id)) + .current_dir("../gotham-utilities/server/cognito") + .output() + { + Ok(o) => return Ok(String::from_utf8_lossy(&o.stdout).to_string()), + Err(_) => return Err(()), + }; +} + +impl<'a, 'r> FromRequest<'a, 'r> for Claims { + type Error = (); + + fn from_request(request: &'a Request<'r>) -> request::Outcome { + let auths: Vec<_> = request.headers().get("Authorization").collect(); + let config = request.guard::>()?; + + if config.issuer.is_empty() + && config.audience.is_empty() + && config.region.is_empty() + && config.pool_id.is_empty() + { + info!("!!! Auth config empty, request in PASSTHROUGH mode !!! "); + if auths.is_empty() { + // No Authorization header + info!("!!! No Authorization header, request accepted !!! "); + return Outcome::Success(passthrough::get_empty_claim()); + } else { + error!("!!! Auth config empty but authorization header, rejecting requests !!!"); + return Outcome::Failure((Status::Unauthorized, ())); + } + } + + if auths.is_empty() { + return Outcome::Failure((Status::Unauthorized, ())); + } + + let claim = match verify( + &config.issuer, + &config.audience, + &config.region, + &config.pool_id, + &auths[0].to_string(), + ) { + Ok(claim) => claim, + Err(_) => { + error!("!!! Auth error: Unauthorized (401) !!!"); + return Outcome::Failure((Status::Unauthorized, ())); + } + }; + + Outcome::Success(claim) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const ISSUER: &str = "issuer"; + const AUDIENCE: &str = "audience"; + const REGION: &str = "region"; + const POOL_ID: &str = "pool_id"; + + #[test] + pub fn get_user_id_test() { + let authorization_header = "Bearer .a.b-c-d-e-f-g-h-i".to_string(); + + assert!(verify( + &ISSUER.to_string(), + &AUDIENCE.to_string(), + ®ION.to_string(), + &POOL_ID.to_string(), + &authorization_header + ) + .is_err()); + } +} diff --git a/gotham-server/src/auth/jwt.rs b/gotham-server/src/auth/jwt.rs new file mode 100644 index 0000000..17b3a7a --- /dev/null +++ b/gotham-server/src/auth/jwt.rs @@ -0,0 +1,119 @@ +// 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::jwt::{decode, decode_header, Algorithm, Header, Validation}; + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Claims { + pub sub: String, + pub exp: usize, +} + +pub fn get_claims( + issuer: &String, + audience: &String, + token: &String, + secret: &[u8], + algorithms: Vec, +) -> Result { + let mut validation = Validation { + iss: Some(issuer.to_string()), + ..Validation::default() + }; + validation.algorithms = algorithms; + + // Setting audience + validation.set_audience(audience); + + let token_data = match decode::(token, secret, &validation) { + Ok(c) => c, + Err(_) => return Err(()), + }; + + Ok(token_data.claims) +} + +pub fn decode_header_from_token(token: String) -> Result { + let header = match decode_header(&token) { + Ok(h) => h, + Err(_) => return Err(()), + }; + + Ok(header) +} + +#[cfg(test)] +mod tests { + use super::{decode_header_from_token, get_claims}; + use hex; + use jwt::{Algorithm, Header}; + use std::str; + + #[test] + #[should_panic] // Obviously hardcoded authorization_header become invalid + fn get_claims_test() { + let der_hex : &str = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100dd5a02e27e4d48e77fa7fba44de5963a4952850df2d89750408665ac9e814ca58d961348693c424cf884a5f44c377fced421ef3070eb974e7ec76fed861d9c4ff777aefcbb4a1c7396d35dde8feba2476dd42d3a38f73f2f4547d1b35e1cd9d3da9bf7341dc00bd543a97c890f5dfa2f2d800b5ecb44a2a679e8a5848123dd7a8087ec094c503b92dddd027e609e4f61caf5452344be6a401bf1b01a198967b526b13d9e9c2c0e6712e5b3359348135a48a936027d4c2a5c54b4d31eca1f94c00c02be82a91b4ef01498b58b652508110d06105c986750502e1b243fb69b05ad34e3eb1a86cc7cdaf69c4b29d3c00aa97a6055b293797017f1b59a998d2ade970203010001"; + + let token: String = "eyJraWQiOiJZeEdoUlhsTytZSWpjU2xWZFdVUFA1dHhWd\ + FRSTTNmTndNZTN4QzVnXC9YZz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjNDAz\ + ZTBlNy1jM2QwLTRhNDUtODI2Mi01MTM5OTIyZjc5NTgiLCJhdWQiOiI0cG1jaXUx\ + YWhyZjVzdm1nbTFobTVlbGJ1cCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJjdXN0\ + b206ZGV2aWNlUEsiOiJbXCItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxcbk1G\ + a3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUdDNmQ1SnV6OUNPUVVZ\ + K08rUUV5Z0xGaGxSOHpcXHJsVjRRTTV1ZUhsQjVOTVQ2dm04c1dFMWtpak5udnpP\ + WDl0cFRZUEVpTEIzbHZORWNuUmszTXRRZVNRPT1cXG4tLS0tLUVORCBQVUJMSUMg\ + S0VZLS0tLS1cIl0iLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTU0NjUz\ + MzM2NywiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6\ + b25hd3MuY29tXC91cy13ZXN0LTJfZzlqU2xFYUNHIiwiY29nbml0bzp1c2VybmFt\ + ZSI6ImM0MDNlMGU3LWMzZDAtNGE0NS04MjYyLTUxMzk5MjJmNzk1OCIsImV4cCI6\ + MTU0NzEwNzI0OSwiaWF0IjoxNTQ3MTAzNjQ5LCJlbWFpbCI6ImdhcnkrNzgyODJA\ + a3plbmNvcnAuY29tIn0.WLo9fiDiovRqC1RjR959aD8O1E3lqi5Iwnsq4zobqPU5\ + yZHW2FFIDwnEGf3UmQWMLgscKcuy0-NoupMUCbTvG52n5sPvOrCyeIpY5RkOk3mH\ + enH3H6jcNRA7UhDQwhMu_95du3I1YHOA173sPqQQvmWwYbA8TtyNAKOq9k0QEOuq\ + PWRBXldmmp9pxivbEYixWaIRtsJxpK02ODtOUR67o4RVeVLfthQMR4wiANO_hKLH\ + rt76DEkAntM0KIFODS6o6PBZw2IP4P7x21IgcDrTO3yotcc-RVEq0X1N3wI8clr8\ + DaVVZgolenGlERVMfD5i0YWIM1j7GgQ1fuQ8J_LYiQ" + .to_string(); + + let der = hex::decode(der_hex).unwrap(); + let issuer: String = "issuer".to_string(); + let audience: String = "audience".to_string(); + let algorithms = vec![Algorithm::RS256]; + assert!(get_claims(&issuer, &audience, &token, der.as_ref(), algorithms).is_ok()); + } + + #[test] + fn decode_token_test() { + let token: String = "eyJraWQiOiJZeEdoUlhsTytZSWpjU2xWZFdVUFA1dHhWd\ + FRSTTNmTndNZTN4QzVnXC9YZz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjNDAz\ + ZTBlNy1jM2QwLTRhNDUtODI2Mi01MTM5OTIyZjc5NTgiLCJhdWQiOiI0cG1jaXUx\ + YWhyZjVzdm1nbTFobTVlbGJ1cCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJjdXN0\ + b206ZGV2aWNlUEsiOiJbXCItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxcbk1G\ + a3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUdDNmQ1SnV6OUNPUVVZ\ + K08rUUV5Z0xGaGxSOHpcXHJsVjRRTTV1ZUhsQjVOTVQ2dm04c1dFMWtpak5udnpP\ + WDl0cFRZUEVpTEIzbHZORWNuUmszTXRRZVNRPT1cXG4tLS0tLUVORCBQVUJMSUMg\ + S0VZLS0tLS1cIl0iLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTU0NjUz\ + MzM2NywiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6\ + b25hd3MuY29tXC91cy13ZXN0LTJfZzlqU2xFYUNHIiwiY29nbml0bzp1c2VybmFt\ + ZSI6ImM0MDNlMGU3LWMzZDAtNGE0NS04MjYyLTUxMzk5MjJmNzk1OCIsImV4cCI6\ + MTU0NzEwNzI0OSwiaWF0IjoxNTQ3MTAzNjQ5LCJlbWFpbCI6ImdhcnkrNzgyODJA\ + a3plbmNvcnAuY29tIn0.WLo9fiDiovRqC1RjR959aD8O1E3lqi5Iwnsq4zobqPU5\ + yZHW2FFIDwnEGf3UmQWMLgscKcuy0-NoupMUCbTvG52n5sPvOrCyeIpY5RkOk3mH\ + enH3H6jcNRA7UhDQwhMu_95du3I1YHOA173sPqQQvmWwYbA8TtyNAKOq9k0QEOuq\ + PWRBXldmmp9pxivbEYixWaIRtsJxpK02ODtOUR67o4RVeVLfthQMR4wiANO_hKLH\ + rt76DEkAntM0KIFODS6o6PBZw2IP4P7x21IgcDrTO3yotcc-RVEq0X1N3wI8clr8\ + DaVVZgolenGlERVMfD5i0YWIM1j7GgQ1fuQ8J_LYiQ" + .to_string(); + + let header: Header = decode_header_from_token(token).unwrap(); + assert_eq!( + header.kid.unwrap(), + "YxGhRXlO+YIjcSlVdWUPP5txVtTRM3fNwMe3xC5g/Xg=" + ); + } +} diff --git a/gotham-server/src/auth/mod.rs b/gotham-server/src/auth/mod.rs new file mode 100644 index 0000000..96fca74 --- /dev/null +++ b/gotham-server/src/auth/mod.rs @@ -0,0 +1,21 @@ +// 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. +// + +#[derive(Debug, Serialize, Deserialize)] +pub struct PublicKey { + pub kid: String, + pub pem: String, + pub der: String, + pub alg: String, + pub kty: String, +} + +pub mod cognito; +pub mod jwt; +pub mod passthrough; diff --git a/gotham-server/src/auth/passthrough.rs b/gotham-server/src/auth/passthrough.rs new file mode 100644 index 0000000..57ab0a3 --- /dev/null +++ b/gotham-server/src/auth/passthrough.rs @@ -0,0 +1,17 @@ +// 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::jwt::Claims; + +pub fn get_empty_claim() -> Claims { + Claims { + sub: "pass_through_guest_user".to_string(), + exp: 0, + } +} diff --git a/gotham-server/src/lib.rs b/gotham-server/src/lib.rs index 0482e68..a94791e 100644 --- a/gotham-server/src/lib.rs +++ b/gotham-server/src/lib.rs @@ -6,11 +6,12 @@ // License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // - -#![feature(proc_macro_hygiene, decl_macro)] - +#![recursion_limit = "128"] +#![feature(proc_macro_hygiene)] +#![feature(decl_macro)] #[macro_use] extern crate rocket; +extern crate config; extern crate curv; extern crate kms; extern crate multi_party_ecdsa; @@ -18,6 +19,11 @@ extern crate rocket_contrib; extern crate rocksdb; extern crate uuid; extern crate zk_paillier; +#[macro_use] +extern crate failure; + +#[macro_use] +extern crate error_chain; #[macro_use] extern crate serde_derive; @@ -32,10 +38,22 @@ extern crate strum_macros; #[macro_use] extern crate log; +#[cfg(test)] +#[macro_use] extern crate time_test; extern crate time; +extern crate crypto; +extern crate jsonwebtoken as jwt; +extern crate rusoto_dynamodb; +extern crate serde_dynamodb; + +extern crate hex; + +pub mod auth; pub mod routes; pub mod server; +pub mod storage; pub mod tests; -pub mod utilities; + +type Result = std::result::Result; diff --git a/gotham-server/src/routes/ecdsa.rs b/gotham-server/src/routes/ecdsa.rs index aaf6656..2e5dd43 100644 --- a/gotham-server/src/routes/ecdsa.rs +++ b/gotham-server/src/routes/ecdsa.rs @@ -8,9 +8,13 @@ // version 3 of the License, or (at your option) any later version. // +use super::super::Result; use curv::cryptographic_primitives::proofs::sigma_dlog::*; use curv::cryptographic_primitives::twoparty::coin_flip_optimal_rounds; -use curv::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::*; +use curv::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::{ + CommWitness, EcKeyPair, Party1FirstMessage, Party1SecondMessage, +}; +use curv::elliptic::curves::secp256_k1::Secp256k1Scalar; use curv::{BigInt, GE}; use kms::chain_code::two_party as chain_code; use kms::ecdsa::two_party::*; @@ -18,12 +22,20 @@ use kms::rotation::two_party::party1::Rotation1; use multi_party_ecdsa::protocols::two_party_ecdsa::lindell_2017::*; use rocket::State; use rocket_contrib::json::Json; -use rocksdb::DB; -use serde_json; use std::string::ToString; use uuid::Uuid; -use super::super::utilities::db; +use super::super::auth::jwt::Claims; +use super::super::storage::db; +use super::super::storage::db::DB; + +use self::Share::*; +use std::slice::Iter; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +struct HDPos { + pos: u32, +} #[derive(ToString, Debug)] pub enum Share { @@ -60,53 +72,124 @@ pub enum Share { POS, } + +impl Share { + pub fn iterator() -> Iter<'static, Share> { + static FIELDS: [Share; 26] = [ + KeyGenFirstMsg, + CommWitness, + EcKeyPair, + PaillierKeyPair, + Party1Private, + Party2Public, + PDLProver, + PDLDecommit, + PDLFirstMessage, + Party2PDLFirstMsg, + CCKeyGenFirstMsg, + CCCommWitness, + CCEcKeyPair, + CC, + MasterKey, + EphEcKeyPair, + EphKeyGenFirstMsg, + RotateCommitMessage1M, + RotateCommitMessage1R, + RotateRandom1, + RotateFirstMsg, + RotatePrivateNew, + RotatePdlDecom, + RotateParty2First, + RotateParty1Second, + POS, + ]; + + FIELDS.iter() + } +} + pub struct Config { pub db: DB, } #[post("/ecdsa/keygen/first", format = "json")] -pub fn first_message(state: State) -> Json<(String, party_one::KeyGenFirstMsg)> { +pub fn first_message( + state: State, + claim: Claims, +) -> Result> { let id = Uuid::new_v4().to_string(); let (key_gen_first_msg, comm_witness, ec_key_pair) = MasterKey1::key_gen_first_message(); //save pos 0 - db::insert(&state.db, &id, &Share::POS, &0u32); + db::insert( + &state.db, + &claim.sub, + &id, + &Share::POS, + &HDPos { pos: 0u32 }, + )?; - db::insert(&state.db, &id, &Share::KeyGenFirstMsg, &key_gen_first_msg); - db::insert(&state.db, &id, &Share::CommWitness, &comm_witness); - db::insert(&state.db, &id, &Share::EcKeyPair, &ec_key_pair); + db::insert( + &state.db, + &claim.sub, + &id, + &Share::KeyGenFirstMsg, + &key_gen_first_msg, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Share::CommWitness, + &comm_witness, + )?; + db::insert(&state.db, &claim.sub, &id, &Share::EcKeyPair, &ec_key_pair)?; - Json((id, key_gen_first_msg)) + Ok(Json((id, key_gen_first_msg))) } #[post("/ecdsa/keygen//second", format = "json", data = "")] pub fn second_message( state: State, + claim: Claims, id: String, dlog_proof: Json, -) -> Json { +) -> Result> { let party2_public: GE = dlog_proof.0.pk.clone(); - db::insert(&state.db, &id, &Share::Party2Public, &party2_public); - let db_comm_witness = db::get(&state.db, &id, &Share::CommWitness); - let comm_witness: party_one::CommWitness = match db_comm_witness { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + db::insert( + &state.db, + &claim.sub, + &id, + &Share::Party2Public, + &party2_public, + )?; - let ec_key_pair = db::get(&state.db, &id, &Share::EcKeyPair); - let ec_key_pair: party_one::EcKeyPair = match ec_key_pair { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let comm_witness: party_one::CommWitness = + db::get(&state.db, &claim.sub, &id, &Share::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)? + .ok_or(format_err!("No data for such identifier {}", id))?; let (kg_party_one_second_message, paillier_key_pair, party_one_private) = MasterKey1::key_gen_second_message(comm_witness, &ec_key_pair, &dlog_proof.0); - db::insert(&state.db, &id, &Share::PaillierKeyPair, &paillier_key_pair); - db::insert(&state.db, &id, &Share::Party1Private, &party_one_private); + db::insert( + &state.db, + &claim.sub, + &id, + &Share::PaillierKeyPair, + &paillier_key_pair, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Share::Party1Private, + &party_one_private, + )?; - Json(kg_party_one_second_message) + Ok(Json(kg_party_one_second_message)) } #[post( @@ -116,34 +199,41 @@ pub fn second_message( )] pub fn third_message( state: State, + claim: Claims, id: String, party_2_pdl_first_message: Json, -) -> Json<(party_one::PDLFirstMessage)> { - let db_party_one_private = db::get(&state.db, &id, &Share::Party1Private); - let party_one_private: party_one::Party1Private = match db_party_one_private { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +) -> Result> { + let party_one_private: party_one::Party1Private = + db::get(&state.db, &claim.sub, &id, &Share::Party1Private)? + .ok_or(format_err!("No data for such identifier {}", id))?; let (party_one_third_message, party_one_pdl_decommit) = MasterKey1::key_gen_third_message(&party_2_pdl_first_message.0, &party_one_private); - db::insert(&state.db, &id, &Share::PDLDecommit, &party_one_pdl_decommit); db::insert( &state.db, + &claim.sub, + &id, + &Share::PDLDecommit, + &party_one_pdl_decommit, + )?; + db::insert( + &state.db, + &claim.sub, &id, &Share::Party2PDLFirstMsg, &party_2_pdl_first_message.0, - ); + )?; db::insert( &state.db, + &claim.sub, &id, &Share::PDLFirstMessage, &party_one_third_message, - ); + )?; - Json(party_one_third_message) + Ok(Json(party_one_third_message)) } #[post( @@ -153,33 +243,25 @@ pub fn third_message( )] pub fn fourth_message( state: State, + claim: Claims, id: String, party_two_pdl_second_message: Json, -) -> Json<(party_one::PDLSecondMessage)> { - let db_pdl_party_one_third_message = db::get(&state.db, &id, &Share::PDLFirstMessage); +) -> Result> { let pdl_party_one_third_message: party_one::PDLFirstMessage = - match db_pdl_party_one_third_message { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; - - let db_party_one_private = db::get(&state.db, &id, &Share::Party1Private); - let party_one_private: party_one::Party1Private = match db_party_one_private { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + db::get(&state.db, &claim.sub, &id, &Share::PDLFirstMessage)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let db_party_one_pdl_decommit = db::get(&state.db, &id, &Share::PDLDecommit); - let party_one_pdl_decommit: party_one::PDLdecommit = match db_party_one_pdl_decommit { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let party_one_private: party_one::Party1Private = + db::get(&state.db, &claim.sub, &id, &Share::Party1Private)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let db_party_2_pdl_first_message = db::get(&state.db, &id, &Share::Party2PDLFirstMsg); - let party_2_pdl_first_message: party_two::PDLFirstMessage = match db_party_2_pdl_first_message { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let party_one_pdl_decommit: party_one::PDLdecommit = + db::get(&state.db, &claim.sub, &id, &Share::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)? + .ok_or(format_err!("No data for such identifier {}", id))?; let res = MasterKey1::key_gen_fourth_message( &pdl_party_one_third_message, @@ -191,24 +273,41 @@ pub fn fourth_message( assert!(res.is_ok()); - Json(res.unwrap()) + Ok(Json(res.unwrap())) } #[post("/ecdsa/keygen//chaincode/first", format = "json")] -pub fn chain_code_first_message(state: State, id: String) -> Json<(Party1FirstMessage)> { +pub fn chain_code_first_message( + state: State, + claim: Claims, + id: String, +) -> Result> { let (cc_party_one_first_message, cc_comm_witness, cc_ec_key_pair1) = chain_code::party1::ChainCode1::chain_code_first_message(); db::insert( &state.db, + &claim.sub, &id, &Share::CCKeyGenFirstMsg, &cc_party_one_first_message, - ); - db::insert(&state.db, &id, &Share::CCCommWitness, &cc_comm_witness); - db::insert(&state.db, &id, &Share::CCEcKeyPair, &cc_ec_key_pair1); + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Share::CCCommWitness, + &cc_comm_witness, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Share::CCEcKeyPair, + &cc_ec_key_pair1, + )?; - Json(cc_party_one_first_message) + Ok(Json(cc_party_one_first_message)) } #[post( @@ -218,76 +317,61 @@ pub fn chain_code_first_message(state: State, id: String) -> Json<(Party )] pub fn chain_code_second_message( state: State, + claim: Claims, id: String, cc_party_two_first_message_d_log_proof: Json, -) -> Json<(Party1SecondMessage)> { - let db_cc_comm_witness = db::get(&state.db, &id, &Share::CCCommWitness); - let cc_comm_witness: CommWitness = match db_cc_comm_witness { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +) -> Result> { + let cc_comm_witness: CommWitness = db::get(&state.db, &claim.sub, &id, &Share::CCCommWitness)? + .ok_or(format_err!("No data for such identifier {}", id))?; let party1_cc = chain_code::party1::ChainCode1::chain_code_second_message( cc_comm_witness, &cc_party_two_first_message_d_log_proof.0, ); let party2_pub = &cc_party_two_first_message_d_log_proof.pk; - chain_code_compute_message(state, id, party2_pub); + chain_code_compute_message(state, claim, id, party2_pub)?; - Json(party1_cc) + Ok(Json(party1_cc)) } pub fn chain_code_compute_message( state: State, + claim: Claims, id: String, cc_party2_public: &GE, -) -> Json<()> { - let cc_ec_key_pair = db::get(&state.db, &id, &Share::CCEcKeyPair); - let cc_ec_key_pair_party1: EcKeyPair = match cc_ec_key_pair { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +) -> Result> { + let cc_ec_key_pair_party1: EcKeyPair = + db::get(&state.db, &claim.sub, &id, &Share::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, + &cc_party2_public, ); - db::insert(&state.db, &id, &Share::CC, &party1_cc); - master_key(state, id); - Json(()) + db::insert(&state.db, &claim.sub, &id, &Share::CC, &party1_cc)?; + master_key(state, claim, id)?; + Ok(Json(())) } -pub fn master_key(state: State, id: String) { - let db_party2_public = db::get(&state.db, &id, &Share::Party2Public); - let party2_public: GE = match db_party2_public { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +pub fn master_key(state: State, claim: Claims, id: String) -> Result<()> { + let party2_public: GE = db::get(&state.db, &claim.sub, &id, &Share::Party2Public)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let db_paillier_key_pair = db::get(&state.db, &id, &Share::PaillierKeyPair); - let paillier_key_pair: party_one::PaillierKeyPair = match db_paillier_key_pair { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; - - let db_party1_cc = db::get(&state.db, &id, &Share::CC); - let party1_cc: chain_code::party1::ChainCode1 = match db_party1_cc { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let paillier_key_pair: party_one::PaillierKeyPair = + db::get(&state.db, &claim.sub, &id, &Share::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)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let db_party_one_private = db::get(&state.db, &id, &Share::Party1Private); - let party_one_private: party_one::Party1Private = match db_party_one_private { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let party_one_private: party_one::Party1Private = + db::get(&state.db, &claim.sub, &id, &Share::Party1Private)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let db_comm_witness = db::get(&state.db, &id, &Share::CommWitness); - let comm_witness: party_one::CommWitness = match db_comm_witness { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let comm_witness: party_one::CommWitness = + db::get(&state.db, &claim.sub, &id, &Share::CommWitness)? + .ok_or(format_err!("No data for such identifier {}", id))?; let masterKey = MasterKey1::set_master_key( &party1_cc.chain_code, @@ -297,7 +381,7 @@ pub fn master_key(state: State, id: String) { paillier_key_pair, ); - db::insert(&state.db, &id, &Share::MasterKey, &masterKey); + db::insert(&state.db, &claim.sub, &id, &Share::MasterKey, &masterKey) } #[post( @@ -307,26 +391,29 @@ pub fn master_key(state: State, id: String) { )] pub fn sign_first( state: State, + claim: Claims, id: String, eph_key_gen_first_message_party_two: Json, -) -> Json<(party_one::EphKeyGenFirstMsg)> { +) -> Result> { let (sign_party_one_first_message, eph_ec_key_pair_party1) = MasterKey1::sign_first_message(); db::insert( &state.db, + &claim.sub, &id, &Share::EphKeyGenFirstMsg, &eph_key_gen_first_message_party_two.0, - ); + )?; db::insert( &state.db, + &claim.sub, &id, &Share::EphEcKeyPair, &eph_ec_key_pair_party1, - ); + )?; - Json(sign_party_one_first_message) + Ok(Json(sign_party_one_first_message)) } // Added here because the attribute data takes only a single struct @@ -340,30 +427,22 @@ pub struct SignSecondMsgRequest { #[post("/ecdsa/sign//second", format = "json", data = "")] pub fn sign_second( state: State, + claim: Claims, id: String, request: Json, -) -> Json<(party_one::Signature)> { - let db_master_key = db::get(&state.db, &id, &Share::MasterKey); - let master_key: MasterKey1 = match db_master_key { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +) -> Result> { + let master_key: MasterKey1 = db::get(&state.db, &claim.sub, &id, &Share::MasterKey)? + .ok_or(format_err!("No data for such identifier {}", id))?; let child_master_key = master_key.get_child(vec![BigInt::from(request.pos_child_key)]); - let db_eph_ec_key_pair_party1 = db::get(&state.db, &id, &Share::EphEcKeyPair); - let eph_ec_key_pair_party1: party_one::EphEcKeyPair = match db_eph_ec_key_pair_party1 { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let eph_ec_key_pair_party1: party_one::EphEcKeyPair = + db::get(&state.db, &claim.sub, &id, &Share::EphEcKeyPair)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let eph_key_gen_first_message_party_tw_option = - db::get(&state.db, &id, &Share::EphKeyGenFirstMsg); let eph_key_gen_first_message_party_two: party_two::EphKeyGenFirstMsg = - match eph_key_gen_first_message_party_tw_option { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + db::get(&state.db, &claim.sub, &id, &Share::EphKeyGenFirstMsg)? + .ok_or(format_err!("No data for such identifier {}", id))?; let signatures = child_master_key.sign_second_message( &request.party_two_sign_message, @@ -376,25 +455,36 @@ pub fn sign_second( panic!("validation failed") }; - Json(signatures.unwrap()) + Ok(Json(signatures.unwrap())) } -pub fn get_mk(state: &State, id: &String) -> MasterKey1 { - let db_master_key = db::get(&state.db, &id, &Share::MasterKey); - match db_master_key { - Some(v) => return serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +pub fn get_mk(state: &State, claim: Claims, id: &String) -> Result { + db::get(&state.db, &claim.sub, &id, &Share::MasterKey)? + .ok_or(format_err!("No data for such identifier {}", id)) } + #[post("/ecdsa/rotate//first", format = "json")] pub fn rotate_first( state: State, + claim: Claims, id: String, -) -> Json<(coin_flip_optimal_rounds::Party1FirstMessage)> { +) -> Result> { let (party1_coin_flip_first_message, m1, r1) = Rotation1::key_rotate_first_message(); - db::insert(&state.db, &id, &Share::RotateCommitMessage1M, &m1); - db::insert(&state.db, &id, &Share::RotateCommitMessage1R, &r1); - Json(party1_coin_flip_first_message) + db::insert( + &state.db, + &claim.sub, + &id, + &Share::RotateCommitMessage1M, + &m1, + )?; + db::insert( + &state.db, + &claim.sub, + &id, + &Share::RotateCommitMessage1R, + &r1, + )?; + Ok(Json(party1_coin_flip_first_message)) } #[post( @@ -405,44 +495,49 @@ pub fn rotate_first( pub fn rotate_second( state: State, id: String, + claim: Claims, party2_first_message: Json, -) -> Json< - (( - coin_flip_optimal_rounds::Party1SecondMessage, - party1::RotationParty1Message1, - )), +) -> Result< + Json< + (( + coin_flip_optimal_rounds::Party1SecondMessage, + party1::RotationParty1Message1, + )), + >, > { - let party_one_master_key = get_mk(&state, &id); + let party_one_master_key = get_mk(&state, claim.clone(), &id)?; + + let m1: Secp256k1Scalar = db::get(&state.db, &claim.sub, &id, &Share::RotateCommitMessage1M)? + .ok_or(format_err!("No data for such identifier {}", id))?; + + let r1: Secp256k1Scalar = db::get(&state.db, &claim.sub, &id, &Share::RotateCommitMessage1R)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let rotate_m = db::get(&state.db, &id, &Share::RotateCommitMessage1M); - let m1 = match rotate_m { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; - let rotate_r = db::get(&state.db, &id, &Share::RotateCommitMessage1R); - let r1 = match rotate_r { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("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, &id, &Share::RotateRandom1, &random1); + db::insert(&state.db, &claim.sub, &id, &Share::RotateRandom1, &random1)?; let (rotation_party_one_first_message, party_one_private_new) = party_one_master_key.rotation_first_message(&random1); + db::insert( &state.db, + &claim.sub, &id, &Share::RotateFirstMsg, &rotation_party_one_first_message, - ); + )?; db::insert( &state.db, + &claim.sub, &id, &Share::RotatePrivateNew, &party_one_private_new, - ); - Json((party1_second_message, rotation_party_one_first_message)) + )?; + Ok(Json(( + party1_second_message, + rotation_party_one_first_message, + ))) } #[post( @@ -452,14 +547,13 @@ pub fn rotate_second( )] pub fn rotate_third( state: State, + claim: Claims, id: String, rotation_party_two_first_message: Json, -) -> Json<(party_one::PDLFirstMessage)> { - let rotate_private = db::get(&state.db, &id, &Share::RotatePrivateNew); - let party_one_private_new = match rotate_private { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; +) -> Result> { + let party_one_private_new: party_one::Party1Private = + db::get(&state.db, &claim.sub, &id, &Share::RotatePrivateNew)? + .ok_or(format_err!("No data for such identifier {}", id))?; let (rotation_party_one_second_message, party_one_pdl_decommit) = MasterKey1::rotation_second_message( @@ -468,24 +562,27 @@ pub fn rotate_third( ); db::insert( &state.db, + &claim.sub, &id, &Share::RotatePdlDecom, &party_one_pdl_decommit, - ); + )?; db::insert( &state.db, + &claim.sub, &id, &Share::RotateParty2First, &rotation_party_two_first_message.0, - ); + )?; db::insert( &state.db, &id, + &claim.sub, &Share::RotateParty1Second, &rotation_party_one_second_message, - ); + )?; - Json(rotation_party_one_second_message) + Ok(Json(rotation_party_one_second_message)) } #[post( @@ -495,46 +592,35 @@ pub fn rotate_third( )] pub fn rotate_fourth( state: State, + claim: Claims, id: String, rotation_party_two_second_message: Json, -) -> Json<(party_one::PDLSecondMessage)> { - let party_one_master_key = get_mk(&state, &id); +) -> Result> { + let party_one_master_key = get_mk(&state, claim.clone(), &id)?; - let get_rotate_first_message = db::get(&state.db, &id, &Share::RotateFirstMsg); - let rotation_party_one_first_message = match get_rotate_first_message { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let rotation_party_one_first_message: party1::RotationParty1Message1 = + db::get(&state.db, &claim.sub, &id, &Share::RotateFirstMsg)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let rotate_private = db::get(&state.db, &id, &Share::RotatePrivateNew); - let party_one_private_new = match rotate_private { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let party_one_private_new: party_one::Party1Private = + db::get(&state.db, &claim.sub, &id, &Share::RotatePrivateNew)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let get_random = db::get(&state.db, &id, &Share::RotateRandom1); - let random1 = match get_random { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let random1: kms::rotation::two_party::Rotation = + db::get(&state.db, &claim.sub, &id, &Share::RotateRandom1)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let get_rotation_party_one_second = db::get(&state.db, &id, &Share::RotateParty1Second); - let rotation_party_one_second_message = match get_rotation_party_one_second { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let rotation_party_one_second_message: party_one::PDLFirstMessage = + db::get(&state.db, &claim.sub, &id, &Share::RotateParty1Second)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let get_rotation_party_two_first = db::get(&state.db, &id, &Share::RotateParty2First); - let rotation_party_two_first_message = match get_rotation_party_two_first { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let rotation_party_two_first_message: party_two::PDLFirstMessage = + db::get(&state.db, &claim.sub, &id, &Share::RotateParty2First)? + .ok_or(format_err!("No data for such identifier {}", id))?; - let get_party_one_pdl_decommit = db::get(&state.db, &id, &Share::RotatePdlDecom); - let party_one_pdl_decommit = match get_party_one_pdl_decommit { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; + let party_one_pdl_decommit: party_one::PDLdecommit = + db::get(&state.db, &claim.sub, &id, &Share::RotatePdlDecom)? + .ok_or(format_err!("No data for such identifier {}", id))?; let result_rotate_party_two_second_message = party_one_master_key.rotation_third_message( &rotation_party_one_first_message, @@ -553,20 +639,18 @@ pub fn rotate_fourth( db::insert( &state.db, + &claim.sub, &id, &Share::MasterKey, &party_one_master_key_rotated, - ); + )?; - Json(rotation_party_one_third_message) + Ok(Json(rotation_party_one_third_message)) } #[post("/ecdsa//recover", format = "json")] -pub fn recover(state: State, id: String) -> Json<(u32)> { - let pos_option = db::get(&state.db, &id, &Share::POS); - let pos_old: u32 = match pos_option { - Some(v) => serde_json::from_str(v.to_utf8().unwrap()).unwrap(), - None => panic!("No data for such identifier {}", id), - }; - Json(pos_old) +pub fn recover(state: State, claim: Claims, id: String) -> Result> { + let pos_old: u32 = db::get(&state.db, &claim.sub, &id, &Share::POS)? + .ok_or(format_err!("No data for such identifier {}", id))?; + Ok(Json(pos_old)) } diff --git a/gotham-server/src/server.rs b/gotham-server/src/server.rs index 76855e5..fdf87e9 100644 --- a/gotham-server/src/server.rs +++ b/gotham-server/src/server.rs @@ -7,11 +7,50 @@ // version 3 of the License, or (at your option) any later version. // +use config; use rocket; use rocket::{Request, Rocket}; -use rocksdb::DB; +use rocksdb; + +use rusoto_core::Region; +use rusoto_dynamodb::DynamoDbClient; use super::routes::ecdsa; +use super::storage::db; + +use std::collections::HashMap; +use std::path::Path; +use std::str::FromStr; + +#[derive(Deserialize)] +pub struct AuthConfig { + pub issuer: String, + pub audience: String, + pub region: String, + pub pool_id: String, +} + +impl AuthConfig { + pub fn load(settings: HashMap) -> AuthConfig { + let issuer = settings.get("issuer").unwrap_or(&"".to_string()).to_owned(); + let audience = settings.get("region").unwrap_or(&"".to_string()).to_owned(); + let region = settings + .get("pool_id") + .unwrap_or(&"".to_string()) + .to_owned(); + let pool_id = settings + .get("audience") + .unwrap_or(&"".to_string()) + .to_owned(); + + AuthConfig { + issuer, + audience, + region, + pool_id, + } + } +} #[catch(500)] fn internal_error() -> &'static str { @@ -29,10 +68,18 @@ fn not_found(req: &Request) -> String { } pub fn get_server() -> Rocket { - let config = ecdsa::Config { - db: DB::open_default("./db").unwrap(), + let settings = get_settings_as_map(); + let db_config = ecdsa::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() .register(catchers![internal_error, not_found, bad_request]) .mount( @@ -53,5 +100,48 @@ pub fn get_server() -> Rocket { ecdsa::recover, ], ) - .manage(config) + .manage(db_config) + .manage(auth_config) +} + +fn get_settings_as_map() -> HashMap { + assert!(Path::new("Settings.toml").exists()); + + let mut settings = config::Config::default(); + settings + .merge(config::File::with_name("Settings")) + .unwrap() + .merge(config::Environment::new()) + .unwrap(); + + settings.try_into::>().unwrap() +} + +fn get_db(settings: HashMap) -> db::DB { + let db_type_string = settings + .get("db") + .unwrap_or(&"local".to_string()) + .to_uppercase(); + let db_type = db_type_string.as_str(); + let env = settings + .get("env") + .unwrap_or(&"dev".to_string()) + .to_string(); + + match db_type { + "AWS" => { + let region_option = settings.get("aws_region"); + match region_option { + Some(s) => { + let region_res = Region::from_str(&s); + match region_res { + Ok(region) => db::DB::AWS(DynamoDbClient::new(region), env), + Err(_e) => panic!("Set 'DB = AWS' but 'region' is not a valid value"), + } + } + None => panic!("Set 'DB = AWS' but 'region' is empty"), + } + } + _ => db::DB::Local(rocksdb::DB::open_default("./db").unwrap()), + } } diff --git a/gotham-server/src/storage/aws/dynamodb.rs b/gotham-server/src/storage/aws/dynamodb.rs new file mode 100644 index 0000000..5d406ce --- /dev/null +++ b/gotham-server/src/storage/aws/dynamodb.rs @@ -0,0 +1,267 @@ +use super::error::*; +use super::*; +use failure; +use rusoto_dynamodb::*; +use serde; +use serde_json; +use std; +use std::collections::HashMap; +use std::default::Default; +use std::string::*; +use std::thread; +use std::time::Duration; + +const CUSTOMER_ID_IDENTIFIER: &str = "customerId"; +const ID_IDENTIFIER: &str = "id"; + +#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] +#[allow(non_snake_case)] +struct DBItemIdentifier { + customerId: String, + id: String, +} + +pub fn insert( + dynamodb_client: &DynamoDbClient, + user_id: &str, + id: &str, + table_name: &str, + v: T, +) -> std::result::Result +where + T: serde::ser::Serialize, +{ + let identifier = DBItemIdentifier { + customerId: user_id.to_string(), + id: id.to_string(), + }; + + let mut item = serde_dynamodb::to_hashmap(&identifier).unwrap(); + item.extend(serde_dynamodb::to_hashmap(&v).unwrap()); + + let put_item_input = PutItemInput { + item: item, + table_name: table_name.to_string(), + ..Default::default() + }; + + // Put item + dynamodb_client + .put_item(put_item_input) + .sync() + .map_err(|e| format_err!("DynamoDB error while inserting item: {}", e)) +} + +pub fn get<'a, T>( + dynamodb_client: &rusoto_dynamodb::DynamoDbClient, + _user_id: &str, + id: &str, + table_name: String, +) -> std::result::Result, failure::Error> +where + T: serde::de::Deserialize<'a>, +{ + let mut query_key: HashMap = HashMap::new(); + query_key.insert( + "id".to_string(), + AttributeValue { + s: Some(id.to_string()), + ..Default::default() + }, + ); + let query_item = GetItemInput { + key: query_key, + table_name: table_name.to_string(), + ..Default::default() + }; + + 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"); + 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? + attributes_map.remove(CUSTOMER_ID_IDENTIFIER); + attributes_map.remove(ID_IDENTIFIER); + + let raw_item: serde_dynamodb::error::Result = + serde_dynamodb::from_hashmap(attributes_map); + + match raw_item { + Ok(s) => Ok(Some(s)), + Err(_e) => Ok(None), + } + } + }, + Err(err) => { + info!("Error retrieving object: {:?}", err); + Err(failure::err_msg(format!("{:?}", err))) + } + } +} + +pub fn list_tables(client: &DynamoDbClient) -> Result> { + let list_tables_input: ListTablesInput = Default::default(); + + let result = client.list_tables(list_tables_input).sync(); + + if let Ok(output) = result { + Ok(output.table_names.unwrap()) + } else { + Ok(vec![]) + } +} + +pub fn wait_for_table(client: &DynamoDbClient, name: &str) -> Result { + loop { + let table_desc = describe_table(client, name)?; + + match table_desc.table_status.as_ref().map(|s| &s[..]) { + Some("ACTIVE") => { + info!("table {} state ACTIVE", name); + return Ok(table_desc); + } + Some(_) => { + info!("table {} state {}", name, table_desc.table_status.unwrap()); + } + None => { + info!("table {} no state available", name); + } + } + + thread::sleep(Duration::from_secs(1)); + } +} + +pub fn create_table_if_needed( + client: &DynamoDbClient, + name: &str, + read_capacity: i64, + write_capacity: i64, +) -> Result { + loop { + match describe_table(client, name).map_err(Error::from) { + Err(Error(ErrorKind::TableNotFound(_), _)) => { + info!("table {} not found. creating..", name); + } + Err(e) => { + bail!(e); + } + Ok(table) => { + return Ok(table); + } + } + + info!("Continuing to create..."); + match create_table(client, name, read_capacity, write_capacity) { + Err(Error(ErrorKind::TableAlreadyExists(_), _)) => { + info!("table {} already exists. getting info..", name); + } + Err(e) => { + bail!(e); + } + Ok(()) => { + // pass + } + } + } +} + +pub fn describe_table(client: &DynamoDbClient, name: &str) -> Result { + let describe_table_input = DescribeTableInput { + table_name: name.to_owned(), + ..Default::default() + }; + + match client.describe_table(describe_table_input).sync() { + Err(DescribeTableError::ResourceNotFound(s)) => { + if s.starts_with("Requested resource not found: Table:") { + bail!(ErrorKind::TableNotFound(name.to_string())) + } + + bail!(ErrorKind::DescribeTable( + DescribeTableError::ResourceNotFound(s) + )) + } + Err(e) => bail!(ErrorKind::DescribeTable(e)), + Ok(table) => { + if let Some(table_desc) = table.table { + info!("table created at {:?}", table_desc.creation_date_time); + Ok(table_desc) + } else { + bail!(ErrorKind::NoTableInfo) + } + } + } +} + +#[macro_export] +macro_rules! attributes { + ($($val:expr => $attr_type:expr),*) => { + { + let mut temp_vec = Vec::new(); + $( + temp_vec.push(AttributeDefinition { attribute_name: String::from($val), attribute_type: String::from($attr_type) }); + )* + temp_vec + } + } +} + +#[macro_export] +macro_rules! key_schema { + ($($name:expr => $key_type:expr),*) => { + { + let mut temp_vec = Vec::new(); + $( + temp_vec.push(KeySchemaElement { key_type: String::from($key_type), attribute_name: String::from($name) }); + )* + temp_vec + } + } +} + +pub fn create_table( + client: &DynamoDbClient, + name: &str, + read_capacity: i64, + write_capacity: i64, +) -> Result<()> { + let create_table_input = CreateTableInput { + table_name: name.to_string(), + attribute_definitions: attributes!("id" => "S"), + key_schema: key_schema!("id" => "HASH"), + provisioned_throughput: ProvisionedThroughput { + read_capacity_units: read_capacity, + write_capacity_units: write_capacity, + }, + ..Default::default() + }; + + match client.create_table(create_table_input).sync() { + Err(CreateTableError::ResourceInUse(s)) => { + let maybe_value = serde_json::from_str::(&s); + + if let Ok(value) = maybe_value { + if value.message.starts_with("Table already exists:") { + bail!(ErrorKind::TableAlreadyExists(name.to_string())) + } + } + + bail!(ErrorKind::CreateTable(CreateTableError::ResourceInUse(s))) + } + Err(e) => bail!(ErrorKind::CreateTable(e)), + Ok(table) => { + if let Some(table_desc) = table.table_description { + info!("table created at {:?}", table_desc.creation_date_time); + Ok(()) + } else { + bail!(ErrorKind::NoTableInfo) + } + } + } +} diff --git a/gotham-server/src/storage/aws/error.rs b/gotham-server/src/storage/aws/error.rs new file mode 100644 index 0000000..a98c41d --- /dev/null +++ b/gotham-server/src/storage/aws/error.rs @@ -0,0 +1,43 @@ +use rusoto_dynamodb; +use serde_json; +use std::num; + +error_chain! { + foreign_links { + DescribeTable(rusoto_dynamodb::DescribeTableError); + ListTables(rusoto_dynamodb::ListTablesError); + CreateTable(rusoto_dynamodb::CreateTableError); + GetItem(rusoto_dynamodb::GetItemError); + PutItem(rusoto_dynamodb::PutItemError); + DeleteItem(rusoto_dynamodb::DeleteItemError); + ParseError(num::ParseIntError); + Json(serde_json::Error); + } + + errors { + NoTableInfo { + description("no table info returned") + display("no table info returned") + } + + TableAlreadyExists(t: String) { + description("table already exists") + display("table already exists: {}", t) + } + + TableNotFound(t: String) { + description("table not found") + display("table not found: {}", t) + } + + ConditionalUpdateFailed { + description("conditional update failed") + display("conditional update failed") + } + + MissingAttribute { + description("missing attribute") + display("missing attribute") + } + } +} diff --git a/gotham-server/src/storage/aws/mod.rs b/gotham-server/src/storage/aws/mod.rs new file mode 100644 index 0000000..5b4a5fc --- /dev/null +++ b/gotham-server/src/storage/aws/mod.rs @@ -0,0 +1,10 @@ +pub mod dynamodb; +#[allow(deprecated)] +pub mod error; + +#[derive(Debug, Serialize, Deserialize)] +pub struct AWSError { + #[serde(rename = "__type")] + pub typ: String, + pub message: String, +} diff --git a/gotham-server/src/storage/db.rs b/gotham-server/src/storage/db.rs new file mode 100644 index 0000000..200c505 --- /dev/null +++ b/gotham-server/src/storage/db.rs @@ -0,0 +1,94 @@ +// 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::routes::ecdsa; +use super::super::Result; +use rocksdb; +use serde; + +use super::aws; + +pub enum DB { + Local(rocksdb::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 { + // Create tables + DB::AWS(dynamodb_client, env) => { + 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); + 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(()), + } +} + +pub fn insert(db: &DB, user_id: &str, id: &str, name: &ecdsa::Share, 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); + aws::dynamodb::insert(&dynamodb_client, user_id, id, &table_name, v)?; + Ok(()) + } + DB::Local(rocksdb_client) => { + let identifier = idify(user_id, id, name); + let v_string = serde_json::to_string(&v).unwrap(); + rocksdb_client.put(identifier.as_ref(), v_string.as_ref())?; + Ok(()) + } + } +} + +pub fn get(db: &DB, user_id: &str, id: &str, name: &ecdsa::Share) -> 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)?; + Ok(res) + } + DB::Local(rocksdb_client) => { + let identifier = idify(user_id, id, name); + info!("Getting from db ({})", identifier); + + let db_option = rocksdb_client.get(identifier.as_ref())?; + let vec_option: Option> = db_option.map(|v| v.to_vec()); + match vec_option { + Some(vec) => Ok(serde_json::from_slice(&vec).unwrap()), + None => Ok(None), + } + } + } +} + +fn calculate_table_name(name: &str, env: &str) -> String { + format!("{}-gotham-{}", env, name) +} diff --git a/gotham-server/src/utilities/mod.rs b/gotham-server/src/storage/mod.rs similarity index 96% rename from gotham-server/src/utilities/mod.rs rename to gotham-server/src/storage/mod.rs index 9df144e..785718e 100644 --- a/gotham-server/src/utilities/mod.rs +++ b/gotham-server/src/storage/mod.rs @@ -7,4 +7,5 @@ // version 3 of the License, or (at your option) any later version. // +pub mod aws; pub mod db; diff --git a/gotham-server/src/tests.rs b/gotham-server/src/tests.rs index 3fb2aca..96630d7 100644 --- a/gotham-server/src/tests.rs +++ b/gotham-server/src/tests.rs @@ -6,21 +6,22 @@ // License as published by the Free Software Foundation, either // version 3 of the License, or (at your option) any later version. // - #[cfg(test)] mod tests { - use super::routes::ecdsa; - use super::server; + use super::super::routes::ecdsa; + use super::super::server; use rocket; use rocket::http::ContentType; + use rocket::http::Header; use rocket::http::Status; use rocket::local::Client; use serde_json; + use std::env; use time::PreciseTime; use curv::arithmetic::traits::Converter; - use curv::cryptographic_primitives::twoparty::dh_key_exchange::*; + use curv::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::*; use curv::BigInt; use kms::chain_code::two_party as chain_code; use kms::ecdsa::two_party::*; @@ -47,7 +48,8 @@ mod tests { let start = PreciseTime::now(); - let (kg_party_two_first_message, kg_ec_key_pair_party2) = MasterKey2::key_gen_first_message(); + let (kg_party_two_first_message, kg_ec_key_pair_party2) = + MasterKey2::key_gen_first_message(); let end = PreciseTime::now(); println!("{} Client: party2 first message", start.to(end)); @@ -143,7 +145,7 @@ mod tests { &party_one_third_message, &party_one_pdl_second_message, ) - .expect("pdl error party1"); + .expect("pdl error party1"); let end = PreciseTime::now(); println!("{} Client: party2 fourth message", start.to(end)); @@ -165,7 +167,8 @@ mod tests { ); let res_body = response.body_string().unwrap(); - let cc_party_one_first_message: Party1FirstMessage = serde_json::from_str(&res_body).unwrap(); + let cc_party_one_first_message: Party1FirstMessage = + serde_json::from_str(&res_body).unwrap(); let start = PreciseTime::now(); let (cc_party_two_first_message, cc_ec_key_pair2) = @@ -193,13 +196,15 @@ mod tests { ); let res_body = response.body_string().unwrap(); - let cc_party_one_second_message: Party1SecondMessage = serde_json::from_str(&res_body).unwrap(); + let cc_party_one_second_message: Party1SecondMessage = + serde_json::from_str(&res_body).unwrap(); let start = PreciseTime::now(); - let _cc_party_two_second_message = chain_code::party2::ChainCode2::chain_code_second_message( - &cc_party_one_first_message, - &cc_party_one_second_message, - ); + let _cc_party_two_second_message = + chain_code::party2::ChainCode2::chain_code_second_message( + &cc_party_one_first_message, + &cc_party_one_second_message, + ); let end = PreciseTime::now(); println!("{} Client: party2 chain code second message", start.to(end)); @@ -236,22 +241,15 @@ mod tests { (id, party_two_master_key) } - pub fn sign( + fn sign( client: &Client, id: String, master_key_2: MasterKey2, message: BigInt, ) -> party_one::Signature { time_test!(); - - let start = PreciseTime::now(); let (eph_key_gen_first_message_party_two, eph_comm_witness, eph_ec_key_pair_party2) = MasterKey2::sign_first_message(); - let end = PreciseTime::now(); - println!( - "{} Client: party2 sign first message", - start.to(end) - ); let request: party_two::EphKeyGenFirstMsg = eph_key_gen_first_message_party_two; @@ -323,6 +321,12 @@ mod tests { #[test] fn key_gen_and_sign() { + // Passthrough mode + env::set_var("region", ""); + env::set_var("pool_id", ""); + env::set_var("issuer", ""); + env::set_var("audience", ""); + time_test!(); let client = Client::new(server::get_server()).expect("valid rocket instance"); @@ -339,4 +343,64 @@ mod tests { signatures.s.to_hex() ); } + + #[test] + fn authentication_test_invalid_token() { + env::set_var("region", "region"); + env::set_var("pool_id", "pool_id"); + env::set_var("issuer", "issuer"); + env::set_var("audience", "audience"); + + let client = Client::new(server::get_server()).expect("valid rocket instance"); + + let auth_header = Header::new("Authorization", "Bearer a"); + let response = client + .post("/ecdsa/keygen/first") + .header(ContentType::JSON) + .header(auth_header) + .dispatch(); + + assert_eq!(401, response.status().code); + } + + #[test] + fn authentication_test_expired_token() { + env::set_var("region", "region"); + env::set_var("pool_id", "pool_id"); + env::set_var("issuer", "issuer"); + env::set_var("audience", "audience"); + + let client = Client::new(server::get_server()).expect("valid rocket instance"); + + let token: String = "Bearer eyJraWQiOiJZeEdoUlhsTytZSWpjU2xWZFdVUFA1dHhWd\ + FRSTTNmTndNZTN4QzVnXC9YZz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJjNDAz\ + ZTBlNy1jM2QwLTRhNDUtODI2Mi01MTM5OTIyZjc5NTgiLCJhdWQiOiI0cG1jaXUx\ + YWhyZjVzdm1nbTFobTVlbGJ1cCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJjdXN0\ + b206ZGV2aWNlUEsiOiJbXCItLS0tLUJFR0lOIFBVQkxJQyBLRVktLS0tLVxcbk1G\ + a3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRUdDNmQ1SnV6OUNPUVVZ\ + K08rUUV5Z0xGaGxSOHpcXHJsVjRRTTV1ZUhsQjVOTVQ2dm04c1dFMWtpak5udnpP\ + WDl0cFRZUEVpTEIzbHZORWNuUmszTXRRZVNRPT1cXG4tLS0tLUVORCBQVUJMSUMg\ + S0VZLS0tLS1cIl0iLCJ0b2tlbl91c2UiOiJpZCIsImF1dGhfdGltZSI6MTU0NjUz\ + MzM2NywiaXNzIjoiaHR0cHM6XC9cL2NvZ25pdG8taWRwLnVzLXdlc3QtMi5hbWF6\ + b25hd3MuY29tXC91cy13ZXN0LTJfZzlqU2xFYUNHIiwiY29nbml0bzp1c2VybmFt\ + ZSI6ImM0MDNlMGU3LWMzZDAtNGE0NS04MjYyLTUxMzk5MjJmNzk1OCIsImV4cCI6\ + MTU0NzEwNzI0OSwiaWF0IjoxNTQ3MTAzNjQ5LCJlbWFpbCI6ImdhcnkrNzgyODJA\ + a3plbmNvcnAuY29tIn0.WLo9fiDiovRqC1RjR959aD8O1E3lqi5Iwnsq4zobqPU5\ + yZHW2FFIDwnEGf3UmQWMLgscKcuy0-NoupMUCbTvG52n5sPvOrCyeIpY5RkOk3mH\ + enH3H6jcNRA7UhDQwhMu_95du3I1YHOA173sPqQQvmWwYbA8TtyNAKOq9k0QEOuq\ + PWRBXldmmp9pxivbEYixWaIRtsJxpK02ODtOUR67o4RVeVLfthQMR4wiANO_hKLH\ + rt76DEkAntM0KIFODS6o6PBZw2IP4P7x21IgcDrTO3yotcc-RVEq0X1N3wI8clr8\ + DaVVZgolenGlERVMfD5i0YWIM1j7GgQ1fuQ8J_LYiQ" + .to_string(); + + let auth_header = Header::new("Authorization", token); + + let response = client + .post("/ecdsa/keygen/first") + .header(ContentType::JSON) + .header(auth_header) + .dispatch(); + + assert_eq!(401, response.status().code); + } } diff --git a/gotham-server/src/utilities/db.rs b/gotham-server/src/utilities/db.rs deleted file mode 100644 index 3898aec..0000000 --- a/gotham-server/src/utilities/db.rs +++ /dev/null @@ -1,43 +0,0 @@ -// 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::routes::ecdsa; -use rocksdb::DBVector; -use rocksdb::DB; -use serde; - -fn idify(id: &String, name: &ecdsa::Share) -> String { - format!("{}_{}", id, name.to_string()) -} - -pub fn insert(db: &DB, id: &String, name: &ecdsa::Share, v: &T) -where - T: serde::ser::Serialize, -{ - let identifier = idify(id, name); - let v: String = serde_json::to_string(&v).unwrap(); - debug!("Inserting into db ({}, {})", identifier, v); - - let r = db.put(identifier.as_ref(), v.as_ref()); - if r.is_err() { - panic!("Error while writing to db for identifier {}", identifier); - } -} - -pub fn get(db: &DB, id: &String, name: &ecdsa::Share) -> Option { - let identifier = idify(id, name); - debug!("Getting from db ({})", identifier); - - let r = db.get(identifier.as_ref()); - if r.is_err() { - panic!("Error while reading from db for identifier {}", identifier) - } - - r.unwrap() -} diff --git a/gotham-utilities/server/buildspec.yml b/gotham-utilities/server/buildspec.yml index f7f9f2d..6b61864 100644 --- a/gotham-utilities/server/buildspec.yml +++ b/gotham-utilities/server/buildspec.yml @@ -6,7 +6,8 @@ phases: - echo Logging in to Amazon ECR... - aws --version - $(aws ecr get-login --region us-west-2 --no-include-email) - - REPOSITORY_URI=542401451332.dkr.ecr.us-west-2.amazonaws.com/gothambuild + - ACCOUNT_ID=$(echo $CODEBUILD_BUILD_ARN | cut -d ":" -f 5) + - REPOSITORY_URI=${ACCOUNT_ID}.dkr.ecr.us-west-2.amazonaws.com/gothambuild - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) - IMAGE_TAG=${COMMIT_HASH:=latest} build: @@ -22,6 +23,6 @@ phases: - docker push $REPOSITORY_URI:latest - docker push $REPOSITORY_URI:$IMAGE_TAG - echo Writing image definitions file... - - printf '[{"name":"gotham-server-build","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json + - printf '{"AWSEBDockerrunVersion":"1","Ports":[{"ContainerPort":8000}],"Image":{"Name":"%s","Update":"true"}}' $REPOSITORY_URI:$IMAGE_TAG > Dockerrun.aws.json artifacts: - files: imagedefinitions.json + files: Dockerrun.aws.json diff --git a/gotham-utilities/server/cognito/.gitignore b/gotham-utilities/server/cognito/.gitignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/gotham-utilities/server/cognito/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/gotham-utilities/server/cognito/jwt-to-pems.js b/gotham-utilities/server/cognito/jwt-to-pems.js new file mode 100644 index 0000000..6086e93 --- /dev/null +++ b/gotham-utilities/server/cognito/jwt-to-pems.js @@ -0,0 +1,56 @@ +#!/usr/bin/env node +"use strict"; + +const RSAKey = require('rsa-key'); +const argv = require('yargs').argv; +const fetch = require('node-fetch'); +const _ = require('lodash'); +const jwkToPem = require('jwk-to-pem'); + +let JWT_TOKENS_AVAILABLE = false; + +function waitForTokens () { + if (!JWT_TOKENS_AVAILABLE) { + setTimeout(waitForTokens, 1000); + } +}; + +async function initJsonWebKeySet(url) { + const response = await fetch(url); + const jwk = await response.json(); + + return _(jwk.keys) + .map(k => { + const key = new RSAKey(jwkToPem(k)) + + return { + kid: k.kid, + pem: key.exportKey(), + der: key.exportKey('der', 'pkcs1', 'public').toString('hex'), + alg: k.alg, + kty: k.kty + }; + }) + .keyBy(k => k.kid) + .value(); +} + +if (!argv.region && !argv.poolid) { + console.error('Region and poolid are required!'); + process.exit(-1); +} + +const url = `https://cognito-idp.${argv.region}.amazonaws.com/${argv.poolid}/.well-known/jwks.json`; + +(async () => { + try { + const kidTojwk = await initJsonWebKeySet(url); + console.log(JSON.stringify(kidTojwk)); + } catch (e) { + console.error(e); + } + + JWT_TOKENS_AVAILABLE = true; +})(); + +waitForTokens(); diff --git a/gotham-utilities/server/cognito/package-lock.json b/gotham-utilities/server/cognito/package-lock.json new file mode 100644 index 0000000..7da3c8e --- /dev/null +++ b/gotham-utilities/server/cognito/package-lock.json @@ -0,0 +1,503 @@ +{ + "name": "cognito-jwt-utils", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + }, + "asn1": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" + }, + "asn1.js": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", + "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "camelcase": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.0.0.tgz", + "integrity": "sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA==" + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + }, + "elliptic": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", + "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==", + "requires": { + "once": "^1.4.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "requires": { + "locate-path": "^3.0.0" + } + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==" + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "jwk-to-pem": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwk-to-pem/-/jwk-to-pem-2.0.1.tgz", + "integrity": "sha512-KKu0WuDDjqw2FlRFp9/vk9TMO/KvgpZVKzdhhYcNyy5OwE8dw9lOK5OQTQHIJ7m+HioI/4P44sAtVuDrQ8KQfw==", + "requires": { + "asn1.js": "^4.5.2", + "elliptic": "^6.2.3", + "safe-buffer": "^5.0.1" + } + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==", + "requires": { + "invert-kv": "^2.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==", + "requires": { + "p-defer": "^1.0.0" + } + }, + "mem": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.1.0.tgz", + "integrity": "sha512-I5u6Q1x7wxO0kdOpYBB28xueHADYps5uty/zg936CiG8NTe5sJL8EjrCuLneuDW3PlMdZBGDIn8BirEVdovZvg==", + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^1.0.0", + "p-is-promise": "^2.0.0" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-fetch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz", + "integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA==" + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==", + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg==" + }, + "p-limit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz", + "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==", + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", + "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" + }, + "rsa-key": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/rsa-key/-/rsa-key-0.0.6.tgz", + "integrity": "sha512-dsBHKoQcQS/YijRzYI/idOm/UyNCCn9SPDUIo3nb4otiygImgdMSk29Ii2Gx7x0yaUaIhh6bFid7JgJkTOlXyA==", + "requires": { + "asn1": "0.2.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==", + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==", + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/gotham-utilities/server/cognito/package.json b/gotham-utilities/server/cognito/package.json new file mode 100644 index 0000000..694fce6 --- /dev/null +++ b/gotham-utilities/server/cognito/package.json @@ -0,0 +1,16 @@ +{ + "name": "cognito-jwt-utils", + "version": "1.0.0", + "description": "Simple utility to retrieve public keys from Cognito", + "main": "jwt-to-pems.js", + "scripts": {}, + "author": "", + "license": "ISC", + "dependencies": { + "jwk-to-pem": "^2.0.1", + "lodash": "^4.17.11", + "node-fetch": "^2.3.0", + "rsa-key": "0.0.6", + "yargs": "^12.0.5" + } +} diff --git a/gotham-utilities/server/docker-build-img/Dockerfile b/gotham-utilities/server/docker-build-img/Dockerfile index 9182044..6d72118 100644 --- a/gotham-utilities/server/docker-build-img/Dockerfile +++ b/gotham-utilities/server/docker-build-img/Dockerfile @@ -8,13 +8,21 @@ RUN apt-get update && apt-get install -y \ g++ \ curl \ clang \ - libgmp3-dev + libgmp3-dev \ + libssl-dev \ + pkg-config ARG CHANNEL="nightly" RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain ${CHANNEL} ENV rocket_address=0.0.0.0 -ENV rocket_port=8080 +ENV rocket_port=8000 +ENV db=AWS +EXPOSE 8000 ADD gotham-server /app WORKDIR /app +RUN ["/root/.cargo/bin/cargo", "build", "--release"] +CMD ["/root/.cargo/bin/cargo", "run", "--release"] + + diff --git a/integration-tests/.gitignore b/integration-tests/.gitignore new file mode 100644 index 0000000..0dedd1f --- /dev/null +++ b/integration-tests/.gitignore @@ -0,0 +1,2 @@ +db/ +target/ diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml new file mode 100644 index 0000000..bcb002c --- /dev/null +++ b/integration-tests/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "tests" +version = "0.1.0" +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" + +[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 diff --git a/integration-tests/README.md b/integration-tests/README.md new file mode 100644 index 0000000..0f9efdb --- /dev/null +++ b/integration-tests/README.md @@ -0,0 +1,7 @@ +# Integration tests + +Runs the Gotham-server and calls it using Gotham-client's API.
+Run it simply with: +```sh +$ cargo test +``` \ No newline at end of file diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs new file mode 100644 index 0000000..7b788c2 --- /dev/null +++ b/integration-tests/src/lib.rs @@ -0,0 +1 @@ +pub mod test; diff --git a/integration-tests/src/test.rs b/integration-tests/src/test.rs new file mode 100644 index 0000000..bb4bbea --- /dev/null +++ b/integration-tests/src/test.rs @@ -0,0 +1,43 @@ +#[cfg(test)] +mod tests { + extern crate server_lib; + extern crate client_lib; + extern crate bitcoin; + extern crate kms; + + use client_lib::api::{PrivateShare, ClientShim}; + use bitcoin::util::hash::Sha256dHash; + use curv::arithmetic::traits::Converter; + use curv::BigInt; + 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(); + }); + + let client_shim = ClientShim::new("http://localhost:8000".to_string()); + + let five_seconds = time::Duration::from_millis(5000); + thread::sleep(five_seconds); + + let ps: PrivateShare = client_lib::api::get_master_key(&client_shim); + + let pos = 0; + let child_master_key = ps + .master_key + .get_child(vec![BigInt::from(pos)]); + + let data : &[u8] = &[]; + let hash : Sha256dHash = Sha256dHash::from_data(data); + let signature = client_lib::api::sign(&client_shim, hash, &child_master_key, pos, &ps.id); + println!( + "signature = (r: {}, s: {})", + signature.r.to_hex(), + signature.s.to_hex() + ); + } +}