From 0767b2b7efed1edb520066cd34992675e2575318 Mon Sep 17 00:00:00 2001 From: Iraklis Leontiadis <120665504+leontiadZen@users.noreply.github.com> Date: Thu, 23 Nov 2023 14:53:12 +0200 Subject: [PATCH] feat!: gotham-city goes gotham-engine (#86) --- Cargo.toml | 5 +- demo-wallet/Cargo.toml | 1 - demo-wallet/src/bitcoin/mod.rs | 4 +- gotham-client/Cargo.toml | 4 +- gotham-client/src/ecdsa/keygen.rs | 4 +- gotham-client/src/ecdsa/recover.rs | 2 +- gotham-client/src/ecdsa/sign.rs | 2 +- gotham-client/src/ecdsa/types.rs | 2 +- gotham-server/Cargo.toml | 24 +- gotham-server/README.md | 4 - gotham-server/src/auth/cognito.rs | 142 ----- gotham-server/src/auth/jwt.rs | 116 ---- gotham-server/src/auth/mod.rs | 23 - gotham-server/src/auth/passthrough.rs | 17 - gotham-server/src/lib.rs | 20 +- gotham-server/src/main.rs | 30 +- gotham-server/src/mod.rs | 4 + gotham-server/src/public_gotham.rs | 99 ++++ gotham-server/src/routes/ecdsa.rs | 643 -------------------- gotham-server/src/routes/eddsa.rs | 56 -- gotham-server/src/routes/mod.rs | 12 - gotham-server/src/routes/ping.rs | 7 - gotham-server/src/server.rs | 124 +--- gotham-server/src/storage/aws/dynamodb.rs | 142 ----- gotham-server/src/storage/aws/mod.rs | 11 - gotham-server/src/storage/db.rs | 93 --- gotham-server/src/storage/mod.rs | 11 - gotham-server/src/tests.rs | 688 ++++++++-------------- integration-tests/tests/ecdsa.rs | 284 +++++---- 29 files changed, 532 insertions(+), 2042 deletions(-) delete mode 100644 gotham-server/src/auth/cognito.rs delete mode 100644 gotham-server/src/auth/jwt.rs delete mode 100644 gotham-server/src/auth/mod.rs delete mode 100644 gotham-server/src/auth/passthrough.rs create mode 100644 gotham-server/src/mod.rs create mode 100644 gotham-server/src/public_gotham.rs delete mode 100644 gotham-server/src/routes/ecdsa.rs delete mode 100644 gotham-server/src/routes/eddsa.rs delete mode 100644 gotham-server/src/routes/mod.rs delete mode 100644 gotham-server/src/routes/ping.rs delete mode 100644 gotham-server/src/storage/aws/dynamodb.rs delete mode 100644 gotham-server/src/storage/aws/mod.rs delete mode 100644 gotham-server/src/storage/db.rs delete mode 100644 gotham-server/src/storage/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 6c53b41..b0f24ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,5 @@ jsonwebtoken = "8" hex = "0.4" secp256k1 = {version = "0.21.0", features = ["global-context"]} rand = "0.8" - -two-party-ecdsa = { git = "https://github.com/ZenGo-X/two-party-ecdsa.git" } -kms = { git = "https://github.com/ZenGo-X/kms-secp256k1.git", branch = "2.0" } +two-party-ecdsa = { git = "https://github.com/ZenGo-X/two-party-ecdsa.git", branch="compatibility_gotham_engine" } +gotham-engine = { git = "https://github.com/ZenGo-X/gotham-engine.git" } diff --git a/demo-wallet/Cargo.toml b/demo-wallet/Cargo.toml index 4336af1..f52373f 100644 --- a/demo-wallet/Cargo.toml +++ b/demo-wallet/Cargo.toml @@ -23,7 +23,6 @@ uuid = { version = "0.7", features = ["v4"] } bitcoin = "0.27.1" itertools = "0.11.0" electrumx_client = { git = "https://github.com/ZenGo-X/rust-electrumx-client.git", tag = "bitcoin@0.26.0"} -kms.workspace = true two-party-ecdsa.workspace = true log.workspace = true hex.workspace = true diff --git a/demo-wallet/src/bitcoin/mod.rs b/demo-wallet/src/bitcoin/mod.rs index e7c287a..06be1b4 100644 --- a/demo-wallet/src/bitcoin/mod.rs +++ b/demo-wallet/src/bitcoin/mod.rs @@ -16,8 +16,6 @@ use bitcoin::util::bip143::SigHashCache; use bitcoin::{Address, SigHashType, TxIn, TxOut}; use electrumx_client::interface::Electrumx; use hex; -use kms::ecdsa::two_party::MasterKey2; -use kms::ecdsa::two_party::*; use serde::{Deserialize, Serialize}; use serde_json; use std::fs; @@ -25,6 +23,8 @@ use two_party_ecdsa::centipede::juggling::proof_system::{Helgamalsegmented, Proo use two_party_ecdsa::curv::elliptic::curves::secp256_k1::{GE, PK}; use two_party_ecdsa::curv::elliptic::curves::traits::ECPoint; use two_party_ecdsa::curv::BigInt; +use two_party_ecdsa::kms::ecdsa::two_party::MasterKey2; +use two_party_ecdsa::kms::ecdsa::two_party::*; use uuid::Uuid; use client_lib::ecdsa; diff --git a/gotham-client/Cargo.toml b/gotham-client/Cargo.toml index d726d3f..8fd73ce 100644 --- a/gotham-client/Cargo.toml +++ b/gotham-client/Cargo.toml @@ -2,8 +2,7 @@ name = "gotham-client" version = "0.1.0" authors = [ - "gbenattar ", - "Oded Leiba -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`. -* By default, the server will use no authentication (PASSTHROUGH).
-Optionally, it can use JWT with AWS Cognito as a service provider by setting proper environment variable (audience, region, pool_id, issuer). ### Running tests #### Without timing output diff --git a/gotham-server/src/auth/cognito.rs b/gotham-server/src/auth/cognito.rs deleted file mode 100644 index 5d7b171..0000000 --- a/gotham-server/src/auth/cognito.rs +++ /dev/null @@ -1,142 +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::jwt::{decode_header_from_token, get_claims, Claims}; -use super::PublicKey; -use jsonwebtoken::Algorithm; -use log::{debug, error}; -use rocket::request::{self, FromRequest, Request}; -use rocket::{http::Status, outcome::Outcome, State}; - -use std::{collections::HashMap, process::Command}; - -use super::super::server::AuthConfig; -use super::passthrough; - -const ALGORITHM: Algorithm = Algorithm::RS256; -const TOKEN_TYPE: &str = "Bearer"; - -fn verify( - issuer: &str, - audience: &str, - region: &str, - pool_id: &str, - authorization_header: &str, -) -> Option { - 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()) { - Some(h) => h, - None => return None, - }; - - let key_set_str: String = match get_jwt_to_pems(region, pool_id) { - Ok(k) => k, - Err(_) => return None, - }; - - let key_set: HashMap = match serde_json::from_str(&key_set_str) { - Ok(k) => k, - Err(_) => return None, - }; - - let header_kid = header.kid.unwrap(); - - if !key_set.contains_key(&header_kid) { - return None; - } - - 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, &secret, algorithms) -} - -fn get_jwt_to_pems(region: &str, pool_id: &str) -> 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) => Ok(String::from_utf8_lossy(&o.stdout).to_string()), - Err(_) => Err(()), - } -} - -#[rocket::async_trait] -impl<'a> FromRequest<'a> for Claims { - type Error = (); - - async fn from_request(request: &'a Request<'_>) -> request::Outcome { - let auths: Vec<_> = request.headers().get("Authorization").collect(); - let config = match request.guard::<&State>().await { - Outcome::Success(s) => s, - _ => return Outcome::Failure((Status::BadRequest, ())), - }; - - if config.issuer.is_empty() - && config.audience.is_empty() - && config.region.is_empty() - && config.pool_id.is_empty() - { - debug!("!!! Auth config empty, request in PASSTHROUGH mode !!! "); - if auths.is_empty() { - // No Authorization header - debug!("!!! 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], - ) { - Some(claim) => claim, - None => { - error!("!!! Auth error: Unauthorized (401) !!!"); - return Outcome::Failure((Status::Unauthorized, ())); - } - }; - - Outcome::Success(claim) - } -} - -#[cfg(test)] -mod tests { - use super::verify; - #[test] - pub fn get_user_id_test() { - let authorization_header = "Bearer .a.b-c-d-e-f-g-h-i"; - let issuer = "issuer"; - let audience = "audience"; - let region = "region"; - let pool_id = "pool_id"; - - assert!(verify(issuer, audience, region, pool_id, authorization_header).is_none()); - } -} diff --git a/gotham-server/src/auth/jwt.rs b/gotham-server/src/auth/jwt.rs deleted file mode 100644 index 5a9cd1b..0000000 --- a/gotham-server/src/auth/jwt.rs +++ /dev/null @@ -1,116 +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 jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, Header, Validation}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Claims { - pub sub: String, - pub exp: usize, -} - -pub fn get_claims( - issuer: &str, - audience: &str, - token: &str, - secret: &[u8], - algorithms: Vec, -) -> Option { - let mut validation = Validation::default(); - validation.set_issuer(&[issuer]); - validation.algorithms = algorithms; - - // Setting audience - validation.set_audience(&[audience]); - let key = DecodingKey::from_rsa_der(secret); - let token_data = match decode::(token, &key, &validation) { - Ok(c) => c, - Err(_) => return None, - }; - - Some(token_data.claims) -} - -pub fn decode_header_from_token(token: String) -> Option
{ - let header = match decode_header(&token) { - Ok(h) => h, - Err(_) => return None, - }; - - Some(header) -} - -#[cfg(test)] -mod tests { - use super::{decode_header_from_token, get_claims}; - use jsonwebtoken::{Algorithm, Header}; - - #[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_some()); - } - - #[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 deleted file mode 100644 index 8302d1e..0000000 --- a/gotham-server/src/auth/mod.rs +++ /dev/null @@ -1,23 +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 serde::{Deserialize, Serialize}; - -#[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 deleted file mode 100644 index 57ab0a3..0000000 --- a/gotham-server/src/auth/passthrough.rs +++ /dev/null @@ -1,17 +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::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 9a3cee4..683438d 100644 --- a/gotham-server/src/lib.rs +++ b/gotham-server/src/lib.rs @@ -1,21 +1,3 @@ -// 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. -// - -pub mod auth; -pub mod routes; +pub mod public_gotham; pub mod server; -pub mod storage; - pub mod tests; - -type Result = std::result::Result; - -pub struct Config { - pub db: storage::db::DB, -} diff --git a/gotham-server/src/main.rs b/gotham-server/src/main.rs index 138b9e7..fc9a63b 100644 --- a/gotham-server/src/main.rs +++ b/gotham-server/src/main.rs @@ -1,33 +1,9 @@ -// Gotham-city -// -// Copyright 2018 by Kzen Networks (kzencorp.com) -// Gotham city is free software: you can redistribute -// it and/or modify it under the terms of the GNU General Public -// License as published by the Free Software Foundation, either -// version 3 of the License, or (at your option) any later version. -// +mod public_gotham; +mod server; -use log::LevelFilter; -use server_lib::server; use std::collections::HashMap; #[rocket::launch] fn rocket() -> _ { - let settings = get_settings_as_map(); - server::get_server(settings) -} - -fn get_settings_as_map() -> HashMap { - let config_file = include_str!("../Settings.toml"); - let mut settings = config::Config::default(); - settings - .merge(config::File::from_str( - config_file, - config::FileFormat::Toml, - )) - .unwrap() - .merge(config::Environment::new()) - .unwrap(); - - settings.try_into::>().unwrap() + crate::server::get_server() } diff --git a/gotham-server/src/mod.rs b/gotham-server/src/mod.rs new file mode 100644 index 0000000..293a00f --- /dev/null +++ b/gotham-server/src/mod.rs @@ -0,0 +1,4 @@ +pub mod public_gotham; +pub mod server; +pub mod main; +pub mod tests; diff --git a/gotham-server/src/public_gotham.rs b/gotham-server/src/public_gotham.rs new file mode 100644 index 0000000..c50078d --- /dev/null +++ b/gotham-server/src/public_gotham.rs @@ -0,0 +1,99 @@ +//!Public gotham implementation + +use rocket::async_trait; +use std::collections::HashMap; +use std::string::String; + +use two_party_ecdsa::party_one::Value; + +use gotham_engine::keygen::KeyGen; +use gotham_engine::sign::Sign; +use gotham_engine::traits::*; +use gotham_engine::types::*; + +pub struct PublicGotham { + rocksdb_client: rocksdb::DB, +} +pub struct Authorizer {} + +fn get_settings_as_map() -> HashMap { + let config_file = include_str!("../Settings.toml"); + let mut settings = config::Config::default(); + settings + .merge(config::File::from_str( + config_file, + config::FileFormat::Toml, + )) + .unwrap() + .merge(config::Environment::new()) + .unwrap(); + + settings.try_into::>().unwrap() +} + +impl PublicGotham { + pub fn new() -> Self { + let settings = get_settings_as_map(); + let db_name = settings.get("db_name").unwrap_or(&"db".to_string()).clone(); + if !db_name.chars().all(|e| char::is_ascii_alphanumeric(&e)) { + panic!("DB name is illegal, may only contain alphanumeric characters"); + } + let rocksdb_client = rocksdb::DB::open_default(format!("./{}", db_name)).unwrap(); + + PublicGotham { rocksdb_client } + } +} + +impl KeyGen for PublicGotham {} + +impl Sign for PublicGotham {} + +#[inline(always)] +fn idify(user_id: String, id: String, name: &dyn MPCStruct) -> String { + format!("{}_{}_{}", user_id, id, name.to_string()) +} + +#[async_trait] +impl Db for PublicGotham { + async fn insert( + &self, + key: &DbIndex, + table_name: &dyn MPCStruct, + value: &dyn Value, + ) -> Result<(), DatabaseError> { + let identifier = idify(key.clone().customerId, key.clone().id, table_name); + let v_string = serde_json::to_string(&value).unwrap(); + let _ = self.rocksdb_client.put(identifier, v_string.clone()); + Ok(()) + } + + async fn get( + &self, + key: &DbIndex, + table_name: &dyn MPCStruct, + ) -> Result>, DatabaseError> { + let identifier = idify(key.clone().customerId, key.clone().id, table_name); + // debug!("Getting from db ({})", identifier); + let result = self.rocksdb_client.get(identifier.clone()).unwrap(); + let vec_option: Option> = result.map(|v| v.to_vec()); + match vec_option { + Some(vec) => { + let final_val: Box = serde_json::from_str( + String::from_utf8(vec.clone()) + .expect("Found invalid UTF-8") + .as_str(), + ) + .unwrap(); + Ok(Option::from(final_val)) + } + None => Ok(None), + } + } + /// the granted function implements the logic of tx authorization. If no tx authorization is needed the function returns always true + fn granted(&self, message: &str, customer_id: &str) -> Result { + Ok(true) + } + async fn has_active_share(&self, _user_id: &str) -> Result { + Ok(false) + } +} diff --git a/gotham-server/src/routes/ecdsa.rs b/gotham-server/src/routes/ecdsa.rs deleted file mode 100644 index 97479eb..0000000 --- a/gotham-server/src/routes/ecdsa.rs +++ /dev/null @@ -1,643 +0,0 @@ -#![allow(non_snake_case)] -// 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 two_party_ecdsa::curv::cryptographic_primitives::proofs::sigma_dlog::*; -use two_party_ecdsa::curv::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::{ - CommWitness, EcKeyPair, Party1FirstMessage, Party1SecondMessage, -}; -use two_party_ecdsa::curv::{elliptic::curves::secp256_k1::GE, BigInt}; -use two_party_ecdsa::{party_one, party_two}; -use kms::ecdsa::two_party::{party1, party2, MasterKey1}; -use kms::chain_code::two_party as chain_code; -use rocket::{State, post, serde::json::Json}; -use uuid::Uuid; -use serde::{Serialize, Deserialize}; -use failure::format_err; -use log::{warn, error}; -use std::collections::HashMap; -use std::process; -use std::panic; - -use crate::{auth::jwt::Claims, storage::db, Config}; - -use std::string::ToString; - -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -struct HDPos { - pos: u32, -} - -#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] -struct Alpha { - value: BigInt, -} - -#[derive(Debug)] -pub enum EcdsaStruct { - KeyGenFirstMsg, - CommWitness, - EcKeyPair, - PaillierKeyPair, - Party1Private, - Party2Public, - - PDLProver, - PDLDecommit, - Alpha, - Party2PDLFirstMsg, - - CCKeyGenFirstMsg, - CCCommWitness, - CCEcKeyPair, - CC, - - Party1MasterKey, - - EphEcKeyPair, - EphKeyGenFirstMsg, - - POS, - Abort, -} - -impl db::MPCStruct for EcdsaStruct { - fn to_string(&self) -> String { - format!("{:?}", self) - } - - // backward compatibility - fn to_table_name(&self, env: &str) -> String { - if self.to_string() == "Party1MasterKey" { - format!("{}_{}", env, self.to_string()) - } else { - format!("{}-gotham-{}", env, self.to_string()) - } - } - - fn require_customer_id(&self) -> bool { - self.to_string() == "Party1MasterKey" - } -} - -#[post("/ecdsa/keygen/first", format = "json")] -pub async fn first_message( - state: &State, - claim: Claims, -) -> Result, String> { - match has_active_share(&state.db, &claim.sub).await { - Err(e) => { - let msg = format!( - "Error when searching for active shares of customerId {}", - &claim.sub - ); - error!("{}: {:?}", msg, e); - return Err(format!("{}", msg)); - } - Ok(result) => { - if result { - let msg = format!("User {} already has an active share", &claim.sub); - warn!("{}", msg); - let should_fail_keygen = std::env::var("FAIL_KEYGEN_IF_ACTIVE_SHARE_EXISTS"); - if should_fail_keygen.is_ok() && should_fail_keygen.unwrap() == "true" { - warn!("Abort KeyGen"); - return Err(format!("{}", msg)); - } - } - } - } - - let (key_gen_first_msg, comm_witness, ec_key_pair) = MasterKey1::key_gen_first_message(); - - let id = Uuid::new_v4().to_string(); - //save pos 0 - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::POS, - &HDPos { pos: 0u32 }, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::KeyGenFirstMsg, - &key_gen_first_msg, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::CommWitness, - &comm_witness, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::EcKeyPair, - &ec_key_pair, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::Abort, "false") - .await - .or(Err("Failed to insert into db"))?; - - Ok(Json((id, key_gen_first_msg))) -} - -#[post("/ecdsa/keygen//second", format = "json", data = "")] -pub async fn second_message( - state: &State, - claim: Claims, - id: String, - dlog_proof: Json, -) -> Result, String> { - let party2_public: GE = dlog_proof.0.pk; - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::Party2Public, - &party2_public, - ) - .await - .or(Err("Failed to insert into db"))?; - - let comm_witness: party_one::CommWitness = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CommWitness) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - let ec_key_pair: party_one::EcKeyPair = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::EcKeyPair) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("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, - &claim.sub, - &id, - &EcdsaStruct::PaillierKeyPair, - &paillier_key_pair, - ) - .await - .or(Err("Failed to insert into db"))?; - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::Party1Private, - &party_one_private, - ) - .await - .or(Err("Failed to insert into db"))?; - - Ok(Json(kg_party_one_second_message)) -} - -#[post( - "/ecdsa/keygen//third", - format = "json", - data = "" -)] -pub async fn third_message( - state: &State, - claim: Claims, - id: String, - party_2_pdl_first_message: Json, -) -> Result, String> { - let party_one_private: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1Private) - .await - .or(Err(format!("Failed to get from DB, id: {}", id)))? - .ok_or(format!("No data for such identifier {}", id))?; - - let (party_one_third_message, party_one_pdl_decommit, alpha) = - MasterKey1::key_gen_third_message(&party_2_pdl_first_message.0, &party_one_private); - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::PDLDecommit, - &party_one_pdl_decommit, - ) - .await - .or(Err(format!( - "Failed to insert into DB PDLDecommit, id: {}", - id - )))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::Alpha, - &Alpha { value: alpha }, - ) - .await - .or(Err(format!("Failed to insert into DB Alpha, id: {}", id)))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::Party2PDLFirstMsg, - &party_2_pdl_first_message.0, - ) - .await - .or(Err(format!( - "Failed to insert into DB Party2PDLFirstMsg, id: {}", - id - )))?; - - Ok(Json(party_one_third_message)) -} - -#[post( - "/ecdsa/keygen//fourth", - format = "json", - data = "" -)] -pub async fn fourth_message( - state: &State, - claim: Claims, - id: String, - party_two_pdl_second_message: Json, -) -> Result, String> { - let party_one_private: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1Private) - .await - .or(Err(format!("Failed to get from DB, id:{}", id)))? - .ok_or(format!("No data for such identifier {}", id))?; - - let party_one_pdl_decommit: party_one::PDLdecommit = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::PDLDecommit) - .await - .or(Err(format!( - "Failed to get party one pdl decommit, id: {}", - id - )))? - .ok_or(format!("No data for such identifier {}", id))?; - - let party_2_pdl_first_message: party_two::PDLFirstMessage = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party2PDLFirstMsg) - .await - .or(Err(format!( - "Failed to get party 2 pdl first message from DB, id: {}", - id - )))? - .ok_or(format!("No data for such identifier {}", id))?; - - let alpha: Alpha = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Alpha) - .await - .or(Err(format!("Failed to get alpha from DB, id: {}", id)))? - .ok_or(format!("No data for such identifier {}", id))?; - - let res = MasterKey1::key_gen_fourth_message( - &party_2_pdl_first_message, - &party_two_pdl_second_message.0, - party_one_private, - party_one_pdl_decommit, - alpha.value, - ); - - assert!(res.is_ok()); - - Ok(Json(res.unwrap())) -} - -#[post("/ecdsa/keygen//chaincode/first", format = "json")] -pub async fn chain_code_first_message( - state: &State, - claim: Claims, - id: String, -) -> Result, String> { - 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, - &EcdsaStruct::CCKeyGenFirstMsg, - &cc_party_one_first_message, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::CCCommWitness, - &cc_comm_witness, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::CCEcKeyPair, - &cc_ec_key_pair1, - ) - .await - .or(Err("Failed to insert into db"))?; - - Ok(Json(cc_party_one_first_message)) -} - -#[post( - "/ecdsa/keygen//chaincode/second", - format = "json", - data = "" -)] -pub async fn chain_code_second_message( - state: &State, - claim: Claims, - id: String, - cc_party_two_first_message_d_log_proof: Json, -) -> Result, String> { - let cc_comm_witness: CommWitness = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CCCommWitness) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("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, claim, id, party2_pub).await?; - - Ok(Json(party1_cc)) -} - -pub async fn chain_code_compute_message( - state: &State, - claim: Claims, - id: String, - cc_party2_public: &GE, -) -> Result, String> { - let cc_ec_key_pair_party1: EcKeyPair = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CCEcKeyPair) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - let party1_cc = chain_code::party1::ChainCode1::compute_chain_code( - &cc_ec_key_pair_party1, - cc_party2_public, - ); - - db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::CC, &party1_cc) - .await - .or(Err("Failed to insert into db"))?; - master_key(state, claim, id) - .await - .map_err(|e| e.to_string())?; - Ok(Json(())) -} - -pub async fn master_key( - state: &State, - claim: Claims, - id: String, -) -> Result<(), failure::Error> { - let party2_public: GE = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party2Public) - .await? - .ok_or_else(|| format_err!("No data for such identifier {}", id))?; - - let paillier_key_pair: party_one::PaillierKeyPair = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::PaillierKeyPair) - .await? - .ok_or_else(|| format_err!("No data for such identifier {}", id))?; - - let party1_cc: chain_code::party1::ChainCode1 = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CC) - .await? - .ok_or_else(|| format_err!("No data for such identifier {}", id))?; - - let party_one_private: party_one::Party1Private = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1Private) - .await? - .ok_or_else(|| format_err!("No data for such identifier {}", id))?; - - let comm_witness: party_one::CommWitness = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::CommWitness) - .await? - .ok_or_else(|| format_err!("No data for such identifier {}", id))?; - - let masterKey = MasterKey1::set_master_key( - &party1_cc.chain_code, - party_one_private, - &comm_witness.public_share, - &party2_public, - paillier_key_pair, - ); - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::Party1MasterKey, - &masterKey, - ) - .await -} - -#[post( - "/ecdsa/sign//first", - format = "json", - data = "" -)] -pub async fn sign_first( - state: &State, - claim: Claims, - id: String, - eph_key_gen_first_message_party_two: Json, -) -> Result, String> { - let abort: String = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Abort) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - - if abort == "true" { - panic!("Tainted user"); - } - - let (sign_party_one_first_message, eph_ec_key_pair_party1) = MasterKey1::sign_first_message(); - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::EphKeyGenFirstMsg, - &eph_key_gen_first_message_party_two.0, - ) - .await - .or(Err("Failed to insert into db"))?; - - db::insert( - &state.db, - &claim.sub, - &id, - &EcdsaStruct::EphEcKeyPair, - &eph_ec_key_pair_party1, - ) - .await - .or(Err("Failed to insert into db"))?; - - Ok(Json(sign_party_one_first_message)) -} - -// Added here because the attribute data takes only a single struct -#[derive(Serialize, Deserialize)] -pub struct SignSecondMsgRequest { - pub message: BigInt, - pub party_two_sign_message: party2::SignMessage, - pub x_pos_child_key: BigInt, - pub y_pos_child_key: BigInt, -} - -#[post("/ecdsa/sign//second", format = "json", data = "")] -pub async fn sign_second( - state: &State, - claim: Claims, - id: String, - request: Json, -) -> Result, String> { - let master_key: MasterKey1 = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::Party1MasterKey) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - - let x: BigInt = request.x_pos_child_key.clone(); - let y: BigInt = request.y_pos_child_key.clone(); - - let child_master_key = master_key.get_child(vec![x, y]); - - let eph_ec_key_pair_party1: party_one::EphEcKeyPair = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::EphEcKeyPair) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - - let eph_key_gen_first_message_party_two: party_two::EphKeyGenFirstMsg = - db::get(&state.db, &claim.sub, &id, &EcdsaStruct::EphKeyGenFirstMsg) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - - let signature_with_recid = child_master_key.sign_second_message( - &request.party_two_sign_message, - &eph_key_gen_first_message_party_two, - &eph_ec_key_pair_party1, - &request.message, - ); - - if signature_with_recid.is_err() { - println!("signature failed, user tainted[{:?}]", id); - db::insert(&state.db, &claim.sub, &id, &EcdsaStruct::Abort, "true") - .await - .or(Err("Failed to insert into db"))?; - panic!("Server sign_second: validation of signature failed. Potential adversary") - }; - - Ok(Json(signature_with_recid.unwrap())) -} - -pub async fn get_mk( - state: &State, - claim: Claims, - id: &str, -) -> Result { - db::get(&state.db, &claim.sub, id, &EcdsaStruct::Party1MasterKey) - .await? - .ok_or_else(|| format_err!("No data for such identifier {}", id)) -} - -#[post("/ecdsa//recover", format = "json")] -pub async fn recover( - state: &State, - claim: Claims, - id: String, -) -> Result, String> { - let pos_old: u32 = db::get(&state.db, &claim.sub, &id, &EcdsaStruct::POS) - .await - .or(Err("Failed to get from db"))? - .ok_or(format!("No data for such identifier {}", id))?; - Ok(Json(pos_old)) -} - -async fn has_active_share(db: &db::DB, user_id: &str) -> Result { - match db { - #[cfg(feature = "local")] - db::DB::Local(_) => Ok(false), - #[cfg(feature = "aws")] - db::DB::AWS(dynamodb_client, env) => { - use rusoto_dynamodb::{AttributeValue, DynamoDb, QueryInput}; - let mut expression_attribute_values: HashMap = HashMap::new(); - expression_attribute_values.insert( - ":customerId".into(), - AttributeValue { - s: Some(user_id.to_string()), - ..AttributeValue::default() - }, - ); - expression_attribute_values.insert( - ":deleted".into(), - AttributeValue { - bool: Some(true), - ..AttributeValue::default() - }, - ); - - let query_input = QueryInput { - table_name: format!("{}_Party1MasterKey", env), - projection_expression: Some("id".into()), - key_condition_expression: Some("customerId = :customerId".into()), - filter_expression: Some("isDeleted <> :deleted".into()), - expression_attribute_values: Some(expression_attribute_values), - consistent_read: Some(true), - ..QueryInput::default() - }; - let result = dynamodb_client.query(query_input).await; - match result { - Ok(query_output) => query_output - .items - .map_or(Ok(false), |items| Ok(items.len() > 0)), - Err(e) => Err(format!( - "Error retrieving Party1MasterKey for customerId {}: {:?}", - user_id, e - )), - } - } - } -} diff --git a/gotham-server/src/routes/eddsa.rs b/gotham-server/src/routes/eddsa.rs deleted file mode 100644 index ba5c055..0000000 --- a/gotham-server/src/routes/eddsa.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::str::FromStr; - -use rocket::State; -use uuid::Uuid; - -use super::super::auth::jwt::Claims; -use super::super::storage::db; -use super::super::Config; -use rocket::{post, serde::json::Json}; - -use self::EddsaStruct::*; - -const PARTY1_INDEX: usize = 0; - -#[derive(Debug)] -pub enum EddsaStruct { - Party2PublicKey, - Party1KeyPair, - AggregatedPublicKey, - Party2SignFirstMsg, - Message, - Party1EphemeralKey, - Party1SignFirstMsg, - Party1SignSecondMsg, -} -#[post("/eddsa/keygen", format = "json", data = "")] -pub async fn keygen( - state: &State, - claim: Claims, - party2_public_key_json: String, -) -> Result { - // let id = Uuid::new_v4().to_string(); - // let party1_key_pair: KeyPair = KeyPair::create(); - // let eight: FE = ECScalar::from(&BigInt::from(8)); - // let eight_inverse: FE = eight.invert(); - // let party2_public_key = party2_public_key_json.0 * &eight_inverse; - // db::insert( - // &state.db, - // &claim.sub, - // &id, - // &Party2PublicKey, - // &party2_public_key, - // )?; - - // // compute apk: - // let mut pks: Vec = Vec::new(); - // pks.push(party1_key_pair.public_key.clone()); - // pks.push(party2_public_key.clone()); - // let key_agg = KeyPair::key_aggregation_n(&pks, &PARTY1_INDEX); - // db::insert(&state.db, &claim.sub, &id, &Party1KeyPair, &party1_key_pair)?; - // db::insert(&state.db, &claim.sub, &id, &AggregatedPublicKey, &key_agg)?; - - // Ok(Json((id, party1_key_pair.public_key))) - let default_id = Uuid::from_bytes([0u8; 16]); - Ok("[\"d9876a6a-d135-40f0-bde7-e3c21ffb06aa\",{\"bytes_str\":\"52306ad4863fb676f9d9534629c3b89e5315114e76f719325a1172bc668de3bd\"}]".to_string()) -} diff --git a/gotham-server/src/routes/mod.rs b/gotham-server/src/routes/mod.rs deleted file mode 100644 index 374e2df..0000000 --- a/gotham-server/src/routes/mod.rs +++ /dev/null @@ -1,12 +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. -// - -pub mod ecdsa; -pub mod eddsa; -pub mod ping; diff --git a/gotham-server/src/routes/ping.rs b/gotham-server/src/routes/ping.rs deleted file mode 100644 index 63a9490..0000000 --- a/gotham-server/src/routes/ping.rs +++ /dev/null @@ -1,7 +0,0 @@ -use rocket::{get, http::Status}; - -#[get("/ping")] -pub fn ping() -> Status { - // TODO: Add logic for health check - Status::Ok -} diff --git a/gotham-server/src/server.rs b/gotham-server/src/server.rs index 4bd55a2..ff03249 100644 --- a/gotham-server/src/server.rs +++ b/gotham-server/src/server.rs @@ -1,50 +1,7 @@ -// 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 log::info; +use crate::public_gotham::PublicGotham; use rocket::{self, catch, catchers, routes, Build, Request, Rocket}; -use serde::Deserialize; - -use super::{storage::db, Config}; - use std::collections::HashMap; -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("audience") - .unwrap_or(&"".to_string()) - .to_owned(); - let region = settings.get("region").unwrap_or(&"".to_string()).to_owned(); - let pool_id = settings - .get("pool_id") - .unwrap_or(&"".to_string()) - .to_owned(); - - AuthConfig { - issuer, - audience, - region, - pool_id, - } - } -} +use tokio::sync::Mutex; #[catch(500)] fn internal_error() -> &'static str { @@ -61,77 +18,22 @@ fn not_found(req: &Request) -> String { format!("Unknown route '{}'.", req.uri()) } -pub fn get_server(settings: HashMap) -> Rocket { - // let settings = get_settings_as_map(); - let db_config = Config { - db: get_db(settings.clone()), - }; - - let auth_config = AuthConfig::load(settings); - +pub fn get_server() -> Rocket { + let x = PublicGotham::new(); rocket::Rocket::build() .register("/", catchers![internal_error, not_found, bad_request]) .mount( "/", routes![ - crate::routes::ping::ping, - crate::routes::ecdsa::first_message, - crate::routes::ecdsa::second_message, - crate::routes::ecdsa::third_message, - crate::routes::ecdsa::fourth_message, - crate::routes::ecdsa::chain_code_first_message, - crate::routes::ecdsa::chain_code_second_message, - crate::routes::ecdsa::sign_first, - crate::routes::ecdsa::sign_second, - crate::routes::ecdsa::recover, - crate::routes::eddsa::keygen + gotham_engine::routes::wrap_keygen_first, + gotham_engine::routes::wrap_keygen_second, + gotham_engine::routes::wrap_keygen_third, + gotham_engine::routes::wrap_keygen_fourth, + gotham_engine::routes::wrap_chain_code_first_message, + gotham_engine::routes::wrap_chain_code_second_message, + gotham_engine::routes::wrap_sign_first, + gotham_engine::routes::wrap_sign_second, ], ) - .manage(db_config) - .manage(auth_config) -} - -fn get_db(settings: HashMap) -> db::DB { - let db_type_string = settings - .get("db") - .unwrap_or(&"local".to_string()) - .to_uppercase(); - let db_name = settings.get("db_name").unwrap_or(&"db".to_string()).clone(); - if !db_name.chars().all(|e| char::is_ascii_alphanumeric(&e)) { - panic!("DB name is illegal, may only contain alphanumeric characters"); - } - let db_type = db_type_string.as_str(); - let env = settings - .get("env") - .unwrap_or(&"dev".to_string()) - .to_string(); - - match db_type { - #[cfg(feature = "aws")] - "AWS" => { - use rusoto_core::Region; - use rusoto_dynamodb::DynamoDbClient; - 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"), - } - } - _ => { - #[cfg(feature = "local")] - { - db::DB::Local(rocksdb::DB::open_default(format!("./{}", db_name)).unwrap()) - } - #[cfg(not(feature = "local"))] - { - unimplemented!("DB type not supported") - } - } - } + .manage(Mutex::new(Box::new(x) as Box)) } diff --git a/gotham-server/src/storage/aws/dynamodb.rs b/gotham-server/src/storage/aws/dynamodb.rs deleted file mode 100644 index 8ca465a..0000000 --- a/gotham-server/src/storage/aws/dynamodb.rs +++ /dev/null @@ -1,142 +0,0 @@ -// use rusoto_dynamodb::*; -use failure::format_err; -use rusoto_dynamodb::{ - AttributeValue, DynamoDb, DynamoDbClient, GetItemInput, PutItemInput, PutItemOutput, -}; -use serde::{self, Deserialize, Serialize}; - -use log::{debug, error}; -use std::collections::HashMap; - -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 async fn insert( - dynamodb_client: &DynamoDbClient, - user_id: &str, - id: &str, - table_name: &str, - v: T, -) -> Result -where - T: serde::ser::Serialize, -{ - let identifier = DBItemIdentifier { - customerId: user_id.to_string(), - id: id.to_string(), - }; - debug!("identifier = {:?}", identifier); - - let mut item = serde_dynamodb::to_hashmap(&identifier).unwrap(); - item.extend(serde_dynamodb::to_hashmap(&v).unwrap()); - - let put_item_input = PutItemInput { - item, - table_name: table_name.to_string(), - ..Default::default() - }; - - // Put item - dynamodb_client - .put_item(put_item_input) - .await - .map_err(|e| format_err!("DynamoDB error while inserting item: {}", e)) -} - -pub async fn get<'a, T>( - dynamodb_client: &DynamoDbClient, - user_id: &str, - id: &str, - table_name: String, - require_customer_id: bool, -) -> Result, failure::Error> -where - T: serde::de::Deserialize<'a>, -{ - let mut query_key: HashMap = HashMap::new(); - - if require_customer_id { - query_key.insert( - "customerId".to_string(), - AttributeValue { - s: Some(user_id.to_string()), - ..Default::default() - }, - ); - } - - query_key.insert( - "id".to_string(), - AttributeValue { - s: Some(id.to_string()), - ..Default::default() - }, - ); - - debug!("Querying table {}, key: {:?}", table_name, query_key); - - let query_item = GetItemInput { - key: query_key, - table_name: table_name.to_string(), - ..Default::default() - }; - - match dynamodb_client.get_item(query_item).await { - Ok(item_from_dynamo) => match item_from_dynamo.item { - None => { - debug!("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) => { - error!("Error retrieving object: {:?}", err); - Err(failure::err_msg(format!("{:?}", err))) - } - } -} - -#[macro_export] -macro_rules! attributes { - ($($val:expr => $attr_type:expr),*) => { - { - $( - let temp_vec = vec![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 temp_vec = vec![(KeySchemaElement { key_type: String::from($key_type), attribute_name: String::from($name) })]; - )* - temp_vec - } - } -} diff --git a/gotham-server/src/storage/aws/mod.rs b/gotham-server/src/storage/aws/mod.rs deleted file mode 100644 index 53cb2aa..0000000 --- a/gotham-server/src/storage/aws/mod.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![cfg(feature = "aws")] - -use serde::{Deserialize, Serialize}; -pub mod dynamodb; - -#[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 deleted file mode 100644 index 8bfd039..0000000 --- a/gotham-server/src/storage/db.rs +++ /dev/null @@ -1,93 +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 crate::Result; -use log::debug; - -pub enum DB { - #[cfg(feature = "local")] - Local(rocksdb::DB), - #[cfg(feature = "aws")] - AWS(rusoto_dynamodb::DynamoDbClient, String), -} - -pub trait MPCStruct: Sync { - fn to_string(&self) -> String; - - fn to_table_name(&self, env: &str) -> String { - format!("{}_{}", env, self.to_string()) - } - - fn require_customer_id(&self) -> bool { - true - } -} - -fn idify(user_id: &str, id: &str, name: &dyn MPCStruct) -> String { - format!("{}_{}_{}", user_id, id, name.to_string()) -} - -pub async fn insert(db: &DB, user_id: &str, id: &str, name: &dyn MPCStruct, v: T) -> Result<()> -where - T: serde::ser::Serialize, -{ - match db { - #[cfg(feature = "aws")] - DB::AWS(dynamodb_client, env) => { - let table_name = name.to_table_name(env); - super::aws::dynamodb::insert(dynamodb_client, user_id, id, &table_name, v).await?; - Ok(()) - } - #[cfg(feature = "local")] - DB::Local(rocksdb_client) => { - let identifier = idify(user_id, id, name); - let v_string = serde_json::to_string(&v).unwrap(); - rocksdb_client.put(identifier, v_string)?; - Ok(()) - } - } -} - -pub async fn get(db: &DB, user_id: &str, id: &str, name: &dyn MPCStruct) -> Result> -where - T: serde::de::DeserializeOwned, -{ - match db { - #[cfg(feature = "aws")] - DB::AWS(dynamodb_client, env) => { - let table_name = name.to_table_name(env); - debug!("table_name = {}", table_name); - let require_customer_id = name.require_customer_id(); - println!("require_customer_id = {}", require_customer_id); - println!("user_id = {}", user_id); - println!("id = {}", id); - let res: Option = super::aws::dynamodb::get( - dynamodb_client, - user_id, - id, - table_name, - require_customer_id, - ) - .await?; - println!("res.is_none() = {}", res.is_none()); - Ok(res) - } - #[cfg(feature = "local")] - DB::Local(rocksdb_client) => { - let identifier = idify(user_id, id, name); - debug!("Getting from db ({})", identifier); - - let db_option = rocksdb_client.get(identifier)?; - 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), - } - } - } -} diff --git a/gotham-server/src/storage/mod.rs b/gotham-server/src/storage/mod.rs deleted file mode 100644 index 785718e..0000000 --- a/gotham-server/src/storage/mod.rs +++ /dev/null @@ -1,11 +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. -// - -pub mod aws; -pub mod db; diff --git a/gotham-server/src/tests.rs b/gotham-server/src/tests.rs index a058bfe..e081858 100644 --- a/gotham-server/src/tests.rs +++ b/gotham-server/src/tests.rs @@ -1,441 +1,251 @@ -// 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. -// -#![cfg(test)] - -use crate::{routes::ecdsa, server}; -use std::collections::HashMap; -use std::env; -use std::time::Instant; -use time_test::time_test; - -use rocket::{http::ContentType, http::{Header, Status}, local::blocking::Client}; - use two_party_ecdsa::curv::arithmetic::traits::Converter; - use two_party_ecdsa::curv::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::*; - use two_party_ecdsa::{party_one, party_two, curv::BigInt}; +#[cfg(test)] +mod tests { + use std::collections::HashMap; + use std::env; + use std::time::Instant; use floating_duration::TimeFormat; - use kms::chain_code::two_party as chain_code; - use kms::ecdsa::two_party::{MasterKey2, party1}; - -fn key_gen(client: &Client) -> (String, MasterKey2) { - time_test!(); - - /*************** START: FIRST MESSAGE ***************/ - let start = Instant::now(); - - let response = client - .post("/ecdsa/keygen/first") - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 first message", - TimeFormat(start.elapsed()) - ); - let res_body = response.into_string().unwrap(); - - let (id, kg_party_one_first_message): (String, party_one::KeyGenFirstMsg) = - serde_json::from_str(&res_body).unwrap(); - - let start = Instant::now(); - - let (kg_party_two_first_message, kg_ec_key_pair_party2) = MasterKey2::key_gen_first_message(); - - println!( - "{} Client: party2 first message", - TimeFormat(start.elapsed()) - ); - /*************** END: FIRST MESSAGE ***************/ - - /*************** START: SECOND MESSAGE ***************/ - let body = serde_json::to_string(&kg_party_two_first_message.d_log_proof).unwrap(); - - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/keygen/{}/second", id)) - .body(body) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 second message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let kg_party_one_second_message: party1::KeyGenParty1Message2 = - serde_json::from_str(&res_body).unwrap(); - - let start = Instant::now(); - - let key_gen_second_message = MasterKey2::key_gen_second_message( - &kg_party_one_first_message, - &kg_party_one_second_message, - ); - assert!(key_gen_second_message.is_ok()); - - println!( - "{} Client: party2 second message", - TimeFormat(start.elapsed()) - ); - - let (party_two_second_message, party_two_paillier, party_two_pdl_chal) = - key_gen_second_message.unwrap(); - - /*************** END: SECOND MESSAGE ***************/ - - /*************** START: THIRD MESSAGE ***************/ - let body = serde_json::to_string(&party_two_second_message.pdl_first_message).unwrap(); - - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/keygen/{}/third", id)) - .body(body) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 third message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let party_one_third_message: party_one::PDLFirstMessage = - serde_json::from_str(&res_body).unwrap(); - - let start = Instant::now(); - - let pdl_decom_party2 = MasterKey2::key_gen_third_message(&party_two_pdl_chal); - - println!( - "{} Client: party2 third message", - TimeFormat(start.elapsed()) - ); - /*************** END: THIRD MESSAGE ***************/ - - /*************** START: FOURTH MESSAGE ***************/ - - let party_2_pdl_second_message = pdl_decom_party2; - let request = party_2_pdl_second_message; - let body = serde_json::to_string(&request).unwrap(); - - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/keygen/{}/fourth", id)) - .body(body) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 fourth message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let party_one_pdl_second_message: party_one::PDLSecondMessage = - serde_json::from_str(&res_body).unwrap(); - - let start = Instant::now(); - - MasterKey2::key_gen_fourth_message( - &party_two_pdl_chal, - &party_one_third_message, - &party_one_pdl_second_message, - ) - .expect("pdl error party1"); - - println!( - "{} Client: party2 fourth message", - TimeFormat(start.elapsed()) - ); - /*************** END: FOURTH MESSAGE ***************/ - - /*************** START: CHAINCODE FIRST MESSAGE ***************/ - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/keygen/{}/chaincode/first", id)) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 chain code first message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let cc_party_one_first_message: Party1FirstMessage = serde_json::from_str(&res_body).unwrap(); - - let start = Instant::now(); - let (cc_party_two_first_message, cc_ec_key_pair2) = - chain_code::party2::ChainCode2::chain_code_first_message(); - - println!( - "{} Client: party2 chain code first message", - TimeFormat(start.elapsed()) - ); - /*************** END: CHAINCODE FIRST MESSAGE ***************/ - - /*************** START: CHAINCODE SECOND MESSAGE ***************/ - let body = serde_json::to_string(&cc_party_two_first_message.d_log_proof).unwrap(); - - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/keygen/{}/chaincode/second", id)) - .body(body) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 chain code second message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let cc_party_one_second_message: Party1SecondMessage = serde_json::from_str(&res_body).unwrap(); - - let start = Instant::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, - ); - - println!( - "{} Client: party2 chain code second message", - TimeFormat(start.elapsed()) - ); - /*************** END: CHAINCODE SECOND MESSAGE ***************/ - - let start = Instant::now(); - let party2_cc = chain_code::party2::ChainCode2::compute_chain_code( - &cc_ec_key_pair2, - &cc_party_one_second_message.comm_witness.public_share, - ) - .chain_code; - - println!( - "{} Client: party2 chain code second message", - TimeFormat(start.elapsed()) - ); - /*************** END: CHAINCODE COMPUTE MESSAGE ***************/ - - println!( - "{} Network/Server: party1 master key", - TimeFormat(start.elapsed()) - ); - - let start = Instant::now(); - let party_two_master_key = MasterKey2::set_master_key( - &party2_cc, - &kg_ec_key_pair_party2, - &kg_party_one_second_message - .ecdh_second_message - .comm_witness - .public_share, - &party_two_paillier, - ); - - println!("{} Client: party2 master_key", TimeFormat(start.elapsed())); - /*************** END: MASTER KEYS MESSAGE ***************/ - - (id, party_two_master_key) -} - -fn sign( - client: &Client, - id: String, - master_key_2: MasterKey2, - message: BigInt, -) -> party_one::SignatureRecid { - time_test!(); - 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 body = serde_json::to_string(&request).unwrap(); - - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/sign/{}/first", id)) - .body(body) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 sign first message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let sign_party_one_first_message: party_one::EphKeyGenFirstMsg = - serde_json::from_str(&res_body).unwrap(); - - let x_pos = BigInt::from(0u32); - let y_pos = BigInt::from(21u32); - - let child_party_two_master_key = master_key_2.get_child(vec![x_pos.clone(), y_pos.clone()]); - - let start = Instant::now(); - - let party_two_sign_message = child_party_two_master_key.sign_second_message( - &eph_ec_key_pair_party2, - eph_comm_witness.clone(), - &sign_party_one_first_message, - &message, - ); - - println!( - "{} Client: party2 sign second message", - TimeFormat(start.elapsed()) - ); - - let request: ecdsa::SignSecondMsgRequest = ecdsa::SignSecondMsgRequest { - message, - party_two_sign_message, - x_pos_child_key: x_pos, - y_pos_child_key: y_pos, - }; - - let body = serde_json::to_string(&request).unwrap(); - - let start = Instant::now(); - - let response = client - .post(format!("/ecdsa/sign/{}/second", id)) - .body(body) - .header(ContentType::JSON) - .dispatch(); - assert_eq!(response.status(), Status::Ok); - - println!( - "{} Network/Server: party1 sign second message", - TimeFormat(start.elapsed()) - ); - - let res_body = response.into_string().unwrap(); - let signature_recid: party_one::SignatureRecid = serde_json::from_str(&res_body).unwrap(); - - signature_recid -} - -#[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 settings = HashMap::::from([ - ("db".to_string(), "local".to_string()), - ("db_name".to_string(), "KeyGenAndSign".to_string()), - ]); - let client = Client::tracked(server::get_server(settings)).expect("valid rocket instance"); - - let (id, master_key_2): (String, MasterKey2) = key_gen(&client); - - let message = BigInt::from(1234u32); - - let signature: party_one::SignatureRecid = sign(&client, id, master_key_2, message); - - println!( - "s = (r: {}, s: {}, recid: {})", - signature.r.to_hex(), - signature.s.to_hex(), - signature.recid - ); -} - -#[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 settings = HashMap::::from([ - ("db".to_string(), "local".to_string()), - ( - "db_name".to_string(), - "AuthenticationTestInvalidToken".to_string(), - ), - ]); - let client = Client::tracked(server::get_server(settings)).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 settings = HashMap::::from([ - ("db".to_string(), "local".to_string()), - ( - "db_name".to_string(), - "AuthenticationTestExpiredToken".to_string(), - ), - ]); - let client = Client::tracked(server::get_server(settings)).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); + use crate::server; + use rocket::{http::ContentType, http::{Status}, local::blocking::Client}; + use two_party_ecdsa::{BigInt, party_one, party_two}; + use two_party_ecdsa::curv::cryptographic_primitives::twoparty::dh_key_exchange_variant_with_pok_comm::{Party1FirstMessage, Party1SecondMessage}; + use two_party_ecdsa::kms::ecdsa::two_party::{MasterKey2, party1}; + use two_party_ecdsa::kms::chain_code::two_party::party2::ChainCode2; + use two_party_ecdsa::kms::ecdsa; + use gotham_engine::types::SignSecondMsgRequest; + use two_party_ecdsa::party_one::Converter; + + fn key_gen(client: &Client) -> (String, MasterKey2) { + let response = client + .post("/ecdsa/keygen/first") + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + let res_body = response.into_string().unwrap(); + + let (id, kg_party_one_first_message): (String, party_one::KeyGenFirstMsg) = + serde_json::from_str(&res_body).unwrap(); + + let (kg_party_two_first_message, kg_ec_key_pair_party2) = + MasterKey2::key_gen_first_message(); + + /*************** END: FIRST MESSAGE ***************/ + + /*************** START: SECOND MESSAGE ***************/ + let body = serde_json::to_string(&kg_party_two_first_message.d_log_proof).unwrap(); + let response = client + .post(format!("/ecdsa/keygen/{}/second", id)) + .body(body) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let kg_party_one_second_message: party1::KeyGenParty1Message2 = + serde_json::from_str(&res_body).unwrap(); + + let key_gen_second_message = MasterKey2::key_gen_second_message( + &kg_party_one_first_message, + &kg_party_one_second_message, + ); + assert!(key_gen_second_message.is_ok()); + + let (party_two_second_message, party_two_paillier, party_two_pdl_chal) = + key_gen_second_message.unwrap(); + + /*************** END: SECOND MESSAGE ***************/ + + /*************** START: THIRD MESSAGE ***************/ + let body = serde_json::to_string(&party_two_second_message.pdl_first_message).unwrap(); + + let response = client + .post(format!("/ecdsa/keygen/{}/third", id)) + .body(body) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let party_one_third_message: party_one::PDLFirstMessage = + serde_json::from_str(&res_body).unwrap(); + + let start = Instant::now(); + + let pdl_decom_party2 = MasterKey2::key_gen_third_message(&party_two_pdl_chal); + + /*************** END: THIRD MESSAGE ***************/ + + /*************** START: FOURTH MESSAGE ***************/ + + let party_2_pdl_second_message = pdl_decom_party2; + let request = party_2_pdl_second_message; + let body = serde_json::to_string(&request).unwrap(); + + let response = client + .post(format!("/ecdsa/keygen/{}/fourth", id)) + .body(body) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let party_one_pdl_second_message: party_one::PDLSecondMessage = + serde_json::from_str(&res_body).unwrap(); + + MasterKey2::key_gen_fourth_message( + &party_two_pdl_chal, + &party_one_third_message, + &party_one_pdl_second_message, + ) + .expect("pdl error party1"); + + /*************** START: CHAINCODE FIRST MESSAGE ***************/ + + let response = client + .post(format!("/ecdsa/keygen/{}/chaincode/first", id)) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let cc_party_one_first_message: Party1FirstMessage = + serde_json::from_str(&res_body).unwrap(); + + let (cc_party_two_first_message, cc_ec_key_pair2) = ChainCode2::chain_code_first_message(); + + /*************** END: CHAINCODE FIRST MESSAGE ***************/ + + /*************** START: CHAINCODE SECOND MESSAGE ***************/ + let body = serde_json::to_string(&cc_party_two_first_message.d_log_proof).unwrap(); + + let response = client + .post(format!("/ecdsa/keygen/{}/chaincode/second", id)) + .body(body) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let cc_party_one_second_message: Party1SecondMessage = + serde_json::from_str(&res_body).unwrap(); + + let _cc_party_two_second_message = ChainCode2::chain_code_second_message( + &cc_party_one_first_message, + &cc_party_one_second_message, + ); + + /*************** END: CHAINCODE SECOND MESSAGE ***************/ + + let party2_cc = ChainCode2::compute_chain_code( + &cc_ec_key_pair2, + &cc_party_one_second_message.comm_witness.public_share, + ) + .chain_code; + + /*************** END: CHAINCODE COMPUTE MESSAGE ***************/ + + let party_two_master_key = MasterKey2::set_master_key( + &party2_cc, + &kg_ec_key_pair_party2, + &kg_party_one_second_message + .ecdh_second_message + .comm_witness + .public_share, + &party_two_paillier, + ); + + /*************** END: MASTER KEYS MESSAGE ***************/ + + (id, party_two_master_key) + } + + fn sign( + client: &Client, + id: String, + master_key_2: MasterKey2, + message: BigInt, + ) -> party_one::SignatureRecid { + 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 body = serde_json::to_string(&request).unwrap(); + + let response = client + .post(format!("/ecdsa/sign/{}/first", id)) + .body(body) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let sign_party_one_first_message: party_one::EphKeyGenFirstMsg = + serde_json::from_str(&res_body).unwrap(); + + let x_pos = BigInt::from(0u32); + let y_pos = BigInt::from(21u32); + + let child_party_two_master_key = master_key_2.get_child(vec![x_pos.clone(), y_pos.clone()]); + + let start = Instant::now(); + + let party_two_sign_message = child_party_two_master_key.sign_second_message( + &eph_ec_key_pair_party2, + eph_comm_witness.clone(), + &sign_party_one_first_message, + &message, + ); + + let request: SignSecondMsgRequest = SignSecondMsgRequest { + message, + party_two_sign_message, + x_pos_child_key: x_pos, + y_pos_child_key: y_pos, + }; + + let body = serde_json::to_string(&request).unwrap(); + + let response = client + .post(format!("/ecdsa/sign/{}/second", id)) + .body(body) + .header(ContentType::JSON) + .dispatch(); + assert_eq!(response.status(), Status::Ok); + + let res_body = response.into_string().unwrap(); + let signature_recid: party_one::SignatureRecid = serde_json::from_str(&res_body).unwrap(); + + signature_recid + } + + #[test] + fn unit_test_key_gen_and_sign() { + // Passthrough mode + env::set_var("region", ""); + env::set_var("pool_id", ""); + env::set_var("issuer", ""); + env::set_var("audience", ""); + // env::set_var("ELASTICACHE_URL", "127.0.0.1"); + + let settings = HashMap::::from([ + ("db".to_string(), "local".to_string()), + ("db_name".to_string(), "KeyGenAndSign".to_string()), + ]); + let server = server::get_server(); + let client = Client::tracked(server).expect("valid rocket instance"); + let (id, master_key_2) = key_gen(&client); + + let message = BigInt::from(1234u32); + + let signature: party_one::SignatureRecid = + sign(&client, id.clone(), master_key_2, message.clone()); + + println!( + "s = (r: {}, s: {}, recid: {})", + signature.r.to_hex(), + signature.s.to_hex(), + signature.recid + ); + //test v2 sign interface with session id enabled + } } diff --git a/integration-tests/tests/ecdsa.rs b/integration-tests/tests/ecdsa.rs index b495337..3cc259c 100644 --- a/integration-tests/tests/ecdsa.rs +++ b/integration-tests/tests/ecdsa.rs @@ -2,121 +2,118 @@ use client_lib::{ecdsa, ClientShim}; use rand::rngs::mock::StepRng; use rand::Rng; use rocket::serde::{DeserializeOwned, Serialize}; -use rocket::{Config, Ignite, Rocket}; +use rocket::Rocket; use secp256k1::{ecdsa::Signature, Message, SECP256K1}; use server_lib::server; use std::collections::HashMap; -use std::{thread, time}; use two_party_ecdsa::curv::arithmetic::big_gmp::BigInt; use two_party_ecdsa::curv::arithmetic::traits::Converter; use two_party_ecdsa::curv::elliptic::curves::traits::ECPoint; -#[rocket::async_test] -async fn test_ecdsa_network() { - let mut rng = StepRng::new(0, 1); - rocket::tokio::spawn(spawn_server(8000, "ecdsa")); - - let client_shim = ClientShim::new("http://localhost:8000".to_string(), None); - - let two_seconds = time::Duration::from_millis(2000); - thread::sleep(two_seconds); - - let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); - - for y in 0..50i32 { - let x_pos = BigInt::from(y * 2 + 1); - let y_pos = BigInt::from(y); - - let child_master_key = ps.master_key.get_child(vec![x_pos.clone(), y_pos.clone()]); - let pk = child_master_key.public.q.get_element(); - - let mut msg_buf = [0u8; 32]; - rng.fill(&mut msg_buf); - let msg: BigInt = BigInt::from(&msg_buf[..]); - - let signature = ecdsa::sign( - &client_shim, - msg.clone(), - &child_master_key, - x_pos, - y_pos, - &ps.id, - ) - .expect("ECDSA signature failed"); - - let r = BigInt::to_vec(&signature.r); - let s = BigInt::to_vec(&signature.s); - let msg = Message::from_slice(&msg_buf).unwrap(); - - let mut sig = [0u8; 64]; - sig[32 - r.len()..32].copy_from_slice(&r); - sig[32 + 32 - s.len()..].copy_from_slice(&s); - - let sig = Signature::from_compact(&sig).unwrap(); - - SECP256K1.verify_ecdsa(&msg, &sig, &pk).unwrap(); - } -} - -async fn spawn_server(port: u32, db_name: &str) -> Rocket { - let settings = HashMap::::from([ - ("db".to_string(), "local".to_string()), - ("db_name".to_string(), db_name.to_string()), - ]); - let rocket = server::get_server(settings); - let figment = rocket.figment().clone().merge((Config::PORT, port)); - rocket.configure(figment).launch().await.unwrap() -} - -#[test] -fn test_ecdsa_keygen() { - let settings = HashMap::::from([ - ("db".into(), "local".into()), - ("db_name".into(), "testEcdsaKeygen".into()), - ]); - let rocket = server::get_server(settings); - let client = RocketClient::new(rocket); - - let client_shim = - ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); - for _ in 0..10 { - let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); - let _ = ps.master_key.public.q.get_element(); - } -} - -#[test] -fn test_ecdsa_key_derivation() { - let settings = HashMap::::from([ - ("db".into(), "local".into()), - ("db_name".into(), "testEcdsaDerivation".into()), - ]); - let rocket = server::get_server(settings); - let client = RocketClient::new(rocket); - - let client_shim = - ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); - let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); - for y in 0..10 { - let x_pos = BigInt::from(y * 2 + 1); - let y_pos = BigInt::from(y); - let child_master_key = ps.master_key.get_child(vec![x_pos.clone(), y_pos.clone()]); - let _ = child_master_key.public.q.get_element(); - } -} +// #[rocket::async_test] +// async fn test_ecdsa_network() { +// let mut rng = StepRng::new(0, 1); +// rocket::tokio::spawn(spawn_server(8000, "ecdsa")); +// +// let client_shim = ClientShim::new("http://localhost:8000".to_string(), None); +// +// let two_seconds = time::Duration::from_millis(2000); +// thread::sleep(two_seconds); +// +// let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); +// +// for y in 0..50i32 { +// let x_pos = BigInt::from(y * 2 + 1); +// let y_pos = BigInt::from(y); +// +// let child_master_key = ps.master_key.get_child(vec![x_pos.clone(), y_pos.clone()]); +// let pk = child_master_key.public.q.get_element(); +// +// let mut msg_buf = [0u8; 32]; +// rng.fill(&mut msg_buf); +// let msg: BigInt = BigInt::from(&msg_buf[..]); +// +// let signature = ecdsa::sign( +// &client_shim, +// msg.clone(), +// &child_master_key, +// x_pos, +// y_pos, +// &ps.id, +// ) +// .expect("ECDSA signature failed"); +// +// let r = BigInt::to_vec(&signature.r); +// let s = BigInt::to_vec(&signature.s); +// let msg = Message::from_slice(&msg_buf).unwrap(); +// +// let mut sig = [0u8; 64]; +// sig[32 - r.len()..32].copy_from_slice(&r); +// sig[32 + 32 - s.len()..].copy_from_slice(&s); +// +// let sig = Signature::from_compact(&sig).unwrap(); +// +// SECP256K1.verify_ecdsa(&msg, &sig, &pk).unwrap(); +// } +// } + +// async fn spawn_server(port: u32, db_name: &str) -> Rocket { +// let settings = HashMap::::from([ +// ("db".to_string(), "local".to_string()), +// ("db_name".to_string(), db_name.to_string()), +// ]); +// let rocket = server::get_server(settings); +// let figment = rocket.figment().clone().merge((Config::PORT, port)); +// rocket.configure(figment).launch().await.unwrap() +// } + +// #[test] +// fn test_ecdsa_keygen() { +// let settings = HashMap::::from([ +// ("db".into(), "local".into()), +// ("db_name".into(), "testEcdsaKeygen".into()), +// ]); +// let rocket = server::get_server(settings); +// let client = RocketClient::new(rocket); +// +// let client_shim = +// ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); +// for _ in 0..10 { +// let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); +// let _ = ps.master_key.public.q.get_element(); +// } +// } + +// #[test] +// fn test_ecdsa_key_derivation() { +// let settings = HashMap::::from([ +// ("db".into(), "local".into()), +// ("db_name".into(), "testEcdsaDerivation".into()), +// ]); +// let rocket = server::get_server(settings); +// let client = RocketClient::new(rocket); +// +// let client_shim = +// ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); +// let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); +// for y in 0..1 { +// let x_pos = BigInt::from(y * 2 + 1); +// let y_pos = BigInt::from(y); +// let child_master_key = ps.master_key.get_child(vec![x_pos.clone(), y_pos.clone()]); +// let _ = child_master_key.public.q.get_element(); +// } +// } #[test] -fn test_ecdsa_key_signing() { +// #[rocket::async_test] +fn integration_test_ecdsa_key_signing() { let mut rng = StepRng::new(0, 1); - let settings = HashMap::::from([ - ("db".into(), "local".into()), - ("db_name".into(), "testEcdsaSigning".into()), - ]); - let rocket = server::get_server(settings); + let settings = HashMap::::from([("db_name".into(), "testEcdsaSigning".into())]); + let rocket = server::get_server(); let client = RocketClient::new(rocket); let client_shim = - ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); + ClientShim::new_with_client("http://localhost:8008".to_string(), None, client); let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); let x_pos = BigInt::from(1); let y_pos = BigInt::from(2); @@ -153,48 +150,47 @@ fn test_ecdsa_key_signing() { } } -#[test] -fn test_ecdsa_long() { - let mut rng = StepRng::new(0, 1); - let settings = HashMap::::from([ - ("db".into(), "local".into()), - ("db_name".into(), "ecdsaLong".into()), - ]); - let rocket = server::get_server(settings); - let client = RocketClient::new(rocket); - - let client_shim = - ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); - - let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); - - for y in 0..20 { - let x_pos = BigInt::from(y * 2 + 1); - let y_pos = BigInt::from(y); - - let child_master_key = ps.master_key.get_child(vec![x_pos.clone(), y_pos.clone()]); - let pk = child_master_key.public.q.get_element(); - - let mut msg_buf = [0u8; 32]; - rng.fill(&mut msg_buf); - let msg: BigInt = BigInt::from(&msg_buf[..]); - - let signature = ecdsa::sign(&client_shim, msg, &child_master_key, x_pos, y_pos, &ps.id) - .expect("ECDSA signature failed"); - - let r = BigInt::to_vec(&signature.r); - let s = BigInt::to_vec(&signature.s); - let msg = Message::from_slice(&msg_buf).unwrap(); - - let mut sig = [0u8; 64]; - sig[32 - r.len()..32].copy_from_slice(&r); - sig[32 + 32 - s.len()..].copy_from_slice(&s); - - let sig = Signature::from_compact(&sig).unwrap(); - - SECP256K1.verify_ecdsa(&msg, &sig, &pk).unwrap(); - } -} +// #[test] +// fn integration_test_ecdsa_long() { +// let mut rng = StepRng::new(0, 1); +// let settings = HashMap::::from([ +// ("db_name".into(), "testEcdsaLong".into()), +// ]); +// let rocket = server::get_server(settings); +// let client = RocketClient::new(rocket); +// +// let client_shim = +// ClientShim::new_with_client("http://localhost:8009".to_string(), None, client); +// +// let ps: ecdsa::PrivateShare = ecdsa::get_master_key(&client_shim); +// +// for y in 0..1 { +// let x_pos = BigInt::from(y * 2 + 1); +// let y_pos = BigInt::from(y); +// +// let child_master_key = ps.master_key.get_child(vec![x_pos.clone(), y_pos.clone()]); +// let pk = child_master_key.public.q.get_element(); +// +// let mut msg_buf = [0u8; 32]; +// rng.fill(&mut msg_buf); +// let msg: BigInt = BigInt::from(&msg_buf[..]); +// +// let signature = ecdsa::sign(&client_shim, msg, &child_master_key, x_pos, y_pos, &ps.id) +// .expect("ECDSA signature failed"); +// +// let r = BigInt::to_vec(&signature.r); +// let s = BigInt::to_vec(&signature.s); +// let msg = Message::from_slice(&msg_buf).unwrap(); +// +// let mut sig = [0u8; 64]; +// sig[32 - r.len()..32].copy_from_slice(&r); +// sig[32 + 32 - s.len()..].copy_from_slice(&s); +// +// let sig = Signature::from_compact(&sig).unwrap(); +// +// SECP256K1.verify_ecdsa(&msg, &sig, &pk).unwrap(); +// } +// } struct RocketClient(pub rocket::local::blocking::Client);