diff --git a/Cargo.lock b/Cargo.lock index 004562f93..5b02c3546 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1582,43 +1582,6 @@ dependencies = [ "vm", ] -[[package]] -name = "ethcore-secretstore" -version = "1.0.0" -dependencies = [ - "byteorder", - "env_logger 0.5.13", - "ethabi", - "ethabi-contract", - "ethabi-derive", - "ethereum-types", - "ethkey", - "futures", - "hyper", - "jsonrpc-server-utils", - "keccak-hash", - "kvdb", - "kvdb-rocksdb", - "lazy_static", - "libsecp256k1", - "log", - "parity-bytes", - "parity-crypto", - "parity-runtime", - "parking_lot 0.10.0", - "percent-encoding 2.1.0", - "rustc-hex 1.0.0", - "serde", - "serde_derive", - "serde_json", - "tempdir", - "tiny-keccak 1.5.0", - "tokio", - "tokio-io", - "tokio-service", - "url 2.1.0", -] - [[package]] name = "ethcore-service" version = "0.1.0" @@ -3303,7 +3266,6 @@ dependencies = [ "ethcore-miner", "ethcore-network", "ethcore-private-tx", - "ethcore-secretstore", "ethcore-service", "ethcore-sync", "ethereum-types", @@ -3333,6 +3295,7 @@ dependencies = [ "parity-path", "parity-rpc", "parity-runtime", + "parity-secretstore", "parity-updater", "parity-util-mem", "parity-version", @@ -3533,6 +3496,41 @@ dependencies = [ "serde", ] +[[package]] +name = "parity-secretstore" +version = "1.0.0" +source = "git+https://github.com/paritytech/secret-store?rev=ebe751d#ebe751db6af07425d2e1823ac05a84d0fafe3dad" +dependencies = [ + "byteorder", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethereum-types", + "futures", + "hyper", + "jsonrpc-server-utils", + "keccak-hash", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "libsecp256k1", + "log", + "parity-bytes", + "parity-crypto", + "parity-runtime", + "parking_lot 0.10.0", + "percent-encoding 2.1.0", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "tiny-keccak 1.5.0", + "tokio", + "tokio-io", + "tokio-service", + "url 2.1.0", +] + [[package]] name = "parity-snappy" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 7bc7d6d68..66e90b816 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ ethcore-logger = { path = "parity/logger" } ethcore-miner = { path = "miner" } ethcore-network = { path = "util/network" } ethcore-private-tx = { path = "ethcore/private-tx" } -ethcore-secretstore = { path = "secret-store", optional = true } ethcore-service = { path = "ethcore/service" } ethcore-sync = { path = "ethcore/sync" } ethereum-types = "0.8.0" @@ -58,6 +57,7 @@ parity-local-store = { path = "miner/local-store" } parity-path = "0.1" parity-rpc = { path = "rpc" } parity-runtime = "0.1.1" +parity-secretstore = { git = "https://github.com/paritytech/secret-store", rev = "ebe751d", optional = true } parity-updater = { path = "updater" } parity-util-mem = { version = "0.5.1", features = ["jemalloc-global"] } parity-version = { path = "util/version" } @@ -99,7 +99,7 @@ test-heavy = ["ethcore/test-heavy"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"] +secretstore = ["parity-secretstore", "accounts", "ethabi", "ethcore-call-contract"] final = ["parity-version/final"] deadlock_detection = ["parking_lot/deadlock_detection"] # to create a memory profile (requires nightly rust), use e.g. diff --git a/parity/lib.rs b/parity/lib.rs index c3876fa9b..e676a1619 100644 --- a/parity/lib.rs +++ b/parity/lib.rs @@ -84,7 +84,7 @@ extern crate log as rlog; extern crate ethcore_accounts as accounts; #[cfg(feature = "secretstore")] -extern crate ethcore_secretstore; +extern crate parity_secretstore; #[cfg(feature = "secretstore")] extern crate ethabi; diff --git a/parity/secretstore/blockchain.rs b/parity/secretstore/blockchain.rs index 4d628de19..18187b513 100644 --- a/parity/secretstore/blockchain.rs +++ b/parity/secretstore/blockchain.rs @@ -37,7 +37,7 @@ use ethcore::miner::{Miner, MinerService}; use parity_crypto::publickey::Error as EthKeyError; use sync::SyncProvider; use registrar::RegistrarClient; -use ethcore_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter}; +use parity_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter}; // TODO: Instead of a constant, make this based on consensus finality. /// Number of confirmations required before request can be processed. diff --git a/parity/secretstore/nodekeypair.rs b/parity/secretstore/nodekeypair.rs index ef1b3cce6..932e56b59 100644 --- a/parity/secretstore/nodekeypair.rs +++ b/parity/secretstore/nodekeypair.rs @@ -22,7 +22,7 @@ use ethkey::Password; use parity_crypto::publickey::public_to_address; use ethereum_types::{H256, Address, Public}; use parity_crypto::publickey::{Signature, Error as EthKeyError}; -use ethcore_secretstore::SigningKeyPair; +use parity_secretstore::SigningKeyPair; pub struct KeyStoreNodeKeyPair { account_provider: Arc, diff --git a/parity/secretstore/server.rs b/parity/secretstore/server.rs index 450a9d787..7f624a1e8 100644 --- a/parity/secretstore/server.rs +++ b/parity/secretstore/server.rs @@ -123,7 +123,7 @@ mod server { #[cfg(feature = "secretstore")] mod server { use std::sync::Arc; - use ethcore_secretstore; + use parity_secretstore; use parity_crypto::publickey::KeyPair; use ansi_term::Colour::{Red, White}; use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor}; @@ -131,23 +131,23 @@ mod server { #[cfg(feature = "accounts")] use super::super::KeyStoreNodeKeyPair; - fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { + fn into_service_contract_address(address: ContractAddress) -> parity_secretstore::ContractAddress { match address { - ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry, - ContractAddress::Address(address) => ethcore_secretstore::ContractAddress::Address(address), + ContractAddress::Registry => parity_secretstore::ContractAddress::Registry, + ContractAddress::Address(address) => parity_secretstore::ContractAddress::Address(address), } } /// Key server pub struct KeyServer { - _key_server: Box, + _key_server: Box, } impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result { - let self_secret: Arc = match conf.self_secret.take() { - Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( + let self_secret: Arc = match conf.self_secret.take() { + Some(NodeSecretKey::Plain(secret)) => Arc::new(parity_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), #[cfg(feature = "accounts")] Some(NodeSecretKey::KeyStore(account)) => { @@ -177,8 +177,8 @@ mod server { } let key_server_name = format!("{}:{}", conf.interface, conf.port); - let mut cconf = ethcore_secretstore::ServiceConfiguration { - listener_address: if conf.http_enabled { Some(ethcore_secretstore::NodeAddress { + let mut cconf = parity_secretstore::ServiceConfiguration { + listener_address: if conf.http_enabled { Some(parity_secretstore::NodeAddress { address: conf.http_interface.clone(), port: conf.http_port, }) } else { None }, @@ -188,12 +188,12 @@ mod server { service_contract_doc_store_address: conf.service_contract_doc_store_address.map(into_service_contract_address), service_contract_doc_sretr_address: conf.service_contract_doc_sretr_address.map(into_service_contract_address), acl_check_contract_address: conf.acl_check_contract_address.map(into_service_contract_address), - cluster_config: ethcore_secretstore::ClusterConfiguration { - listener_address: ethcore_secretstore::NodeAddress { + cluster_config: parity_secretstore::ClusterConfiguration { + listener_address: parity_secretstore::NodeAddress { address: conf.interface.clone(), port: conf.port, }, - nodes: conf.nodes.into_iter().map(|(p, (ip, port))| (p, ethcore_secretstore::NodeAddress { + nodes: conf.nodes.into_iter().map(|(p, (ip, port))| (p, parity_secretstore::NodeAddress { address: ip, port: port, })).collect(), @@ -207,9 +207,9 @@ mod server { cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - let db = ethcore_secretstore::open_secretstore_db(&conf.data_path)?; + let db = parity_secretstore::open_secretstore_db(&conf.data_path)?; let trusted_client = TrustedClient::new(self_secret.clone(), deps.client, deps.sync, deps.miner); - let key_server = ethcore_secretstore::start(trusted_client, self_secret, cconf, db, executor) + let key_server = parity_secretstore::start(trusted_client, self_secret, cconf, db, executor) .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { diff --git a/secret-store/Cargo.toml b/secret-store/Cargo.toml deleted file mode 100644 index 42322c804..000000000 --- a/secret-store/Cargo.toml +++ /dev/null @@ -1,43 +0,0 @@ -[package] -description = "Parity Ethereum (EthCore) Secret Store" -name = "ethcore-secretstore" -version = "1.0.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[dependencies] -byteorder = "1.0" -ethabi = "9.0.1" -ethabi-contract = "9.0.0" -ethabi-derive = "9.0.1" -ethereum-types = "0.8.0" -ethkey = { path = "../accounts/ethkey", optional = true } -futures = "0.1" -hyper = { version = "0.12", default-features = false } -keccak-hash = "0.4.0" -kvdb = "0.4.0" -kvdb-rocksdb = "0.5.0" -lazy_static = "1.0" -libsecp256k1 = { version = "0.3.5", default-features = false } -log = "0.4" -parity-bytes = "0.1" -parity-crypto = { version = "0.5.0", features = ["publickey"] } -parity-runtime = "0.1.1" -parking_lot = "0.10.0" -percent-encoding = "2.1.0" -rustc-hex = "1.0" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" -tiny-keccak = "1.4" -tokio = "0.1.22" -tokio-io = "0.1" -tokio-service = "0.1" -url = "2.1.0" -jsonrpc-server-utils = "14.0.3" - -[dev-dependencies] -env_logger = "0.5" -tempdir = "0.3" -kvdb-rocksdb = "0.5.0" -parity-runtime = { version = "0.1.1", features = ["test-helpers"] } diff --git a/secret-store/res/acl_storage.json b/secret-store/res/acl_storage.json deleted file mode 100644 index cfdefd9c7..000000000 --- a/secret-store/res/acl_storage.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - {"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"document","type":"bytes32"}],"name":"checkPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"} -] diff --git a/secret-store/res/key_server_set.json b/secret-store/res/key_server_set.json deleted file mode 100644 index 28530e353..000000000 --- a/secret-store/res/key_server_set.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - {"constant":true,"inputs":[],"name":"getMigrationMaster","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"startMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerIndex","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getMigrationId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getNewKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"confirmMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[],"name":"getMigrationKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"isMigrationConfirmed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getCurrentKeyServersCount","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getCurrentKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getCurrentLastChange","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint8"}],"name":"getCurrentKeyServer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerAdded","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerRemoved","type":"event"}, - {"anonymous":false,"inputs":[],"name":"MigrationStarted","type":"event"}, - {"anonymous":false,"inputs":[],"name":"MigrationCompleted","type":"event"} -] \ No newline at end of file diff --git a/secret-store/res/service.json b/secret-store/res/service.json deleted file mode 100644 index d79c38e7a..000000000 --- a/secret-store/res/service.json +++ /dev/null @@ -1,33 +0,0 @@ -[ - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"requireKeyServer","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, - - {"constant":true,"inputs":[],"name":"serverKeyGenerationRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"serverKeyGenerationError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyGenerationRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"}],"name":"serverKeyGenerated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isServerKeyGenerationResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"threshold","type":"uint8"}],"name":"ServerKeyGenerationRequested","type":"event"}, - - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"serverKeyRetrievalError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[],"name":"serverKeyRetrievalRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isServerKeyRetrievalResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyRetrievalRequest","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"},{"name":"threshold","type":"uint8"}],"name":"serverKeyRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"}],"name":"ServerKeyRetrievalRequested","type":"event"}, - - {"constant":true,"inputs":[],"name":"documentKeyStoreRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"documentKeyStoreError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"documentKeyStored","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isDocumentKeyStoreResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getDocumentKeyStoreRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"bytes"},{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"commonPoint","type":"bytes"},{"indexed":false,"name":"encryptedPoint","type":"bytes"}],"name":"DocumentKeyStoreRequested","type":"event"}, - - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"},{"name":"commonPoint","type":"bytes"},{"name":"threshold","type":"uint8"}],"name":"documentKeyCommonRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"},{"name":"requester","type":"address"}],"name":"isDocumentKeyShadowRetrievalResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"},{"name":"participants","type":"uint256"},{"name":"decryptedSecret","type":"bytes"},{"name":"shadow","type":"bytes"}],"name":"documentKeyPersonalRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"}],"name":"documentKeyShadowRetrievalError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[],"name":"documentKeyShadowRetrievalRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getDocumentKeyShadowRetrievalRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"requester","type":"address"}],"name":"DocumentKeyCommonRetrievalRequested","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"requesterPublic","type":"bytes"}],"name":"DocumentKeyPersonalRetrievalRequested","type":"event"} -] \ No newline at end of file diff --git a/secret-store/src/acl_storage.rs b/secret-store/src/acl_storage.rs deleted file mode 100644 index 2c9dbf2d2..000000000 --- a/secret-store/src/acl_storage.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{HashMap, HashSet}; -use parking_lot::{Mutex, RwLock}; -use ethereum_types::Address; -use ethabi::FunctionOutputDecoder; -use blockchain::{SecretStoreChain, NewBlocksNotify, ContractAddress, BlockId}; -use types::{Error, ServerKeyId}; - -use_contract!(acl_storage, "res/acl_storage.json"); - -const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; - -/// ACL storage of Secret Store -pub trait AclStorage: Send + Sync { - /// Check if requester can access document with hash `document` - fn check(&self, requester: Address, document: &ServerKeyId) -> Result; -} - -/// On-chain ACL storage implementation. -pub struct OnChainAclStorage { - /// Cached on-chain contract. - contract: Mutex, -} - -/// Cached on-chain ACL storage contract. -struct CachedContract { - /// Blockchain client. - client: Arc, - /// Contract address source. - address_source: ContractAddress, - /// Current contract address. - contract_address: Option
, -} - -/// Dummy ACL storage implementation (check always passed). -#[derive(Default, Debug)] -pub struct DummyAclStorage { - prohibited: RwLock>>, -} - -impl OnChainAclStorage { - pub fn new(trusted_client: Arc, address_source: ContractAddress) -> Result, Error> { - let acl_storage = Arc::new(OnChainAclStorage { - contract: Mutex::new(CachedContract::new(trusted_client.clone(), address_source)), - }); - trusted_client.add_listener(acl_storage.clone()); - Ok(acl_storage) - } -} - -impl AclStorage for OnChainAclStorage { - fn check(&self, requester: Address, document: &ServerKeyId) -> Result { - self.contract.lock().check(requester, document) - } -} - -impl NewBlocksNotify for OnChainAclStorage { - fn new_blocks(&self, _new_enacted_len: usize) { - self.contract.lock().update_contract_address() - } -} - -impl CachedContract { - pub fn new(client: Arc, address_source: ContractAddress) -> Self { - let mut contract = CachedContract { - client, - address_source, - contract_address: None, - }; - contract.update_contract_address(); - contract - } - - pub fn update_contract_address(&mut self) { - let contract_address = self.client.read_contract_address( - ACL_CHECKER_CONTRACT_REGISTRY_NAME, - &self.address_source - ); - if contract_address != self.contract_address { - trace!(target: "secretstore", "Configuring for ACL checker contract from address {:?}", - contract_address); - - self.contract_address = contract_address; - } - } - - pub fn check(&mut self, requester: Address, document: &ServerKeyId) -> Result { - if self.client.is_trusted() { - // call contract to check accesss - match self.contract_address { - Some(contract_address) => { - let (encoded, decoder) = acl_storage::functions::check_permissions::call(requester, document.clone()); - let d = self.client.call_contract(BlockId::Latest, contract_address, encoded) - .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string())))?; - decoder.decode(&d) - .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string()))) - }, - None => Err(Error::Internal("ACL checker contract is not configured".to_owned())), - } - } else { - Err(Error::Internal("Calling ACL contract without trusted blockchain client".into())) - } - } -} - -impl DummyAclStorage { - /// Prohibit given requester access to given documents - #[cfg(test)] - pub fn prohibit(&self, requester: Address, document: ServerKeyId) { - self.prohibited.write() - .entry(requester) - .or_insert_with(Default::default) - .insert(document); - } -} - -impl AclStorage for DummyAclStorage { - fn check(&self, requester: Address, document: &ServerKeyId) -> Result { - Ok(self.prohibited.read() - .get(&requester) - .map(|docs| !docs.contains(document)) - .unwrap_or(true)) - } -} diff --git a/secret-store/src/blockchain.rs b/secret-store/src/blockchain.rs deleted file mode 100644 index bed3eb1a7..000000000 --- a/secret-store/src/blockchain.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use bytes::Bytes; -use ethereum_types::{H256, Address, Public}; -use ethabi::RawLog; -use crypto::publickey::{Signature, Error as EthKeyError}; - -/// Type for block number. -/// Duplicated from ethcore types -pub type BlockNumber = u64; - -/// Uniquely identifies block. -/// Duplicated from ethcore types -#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] -pub enum BlockId { - /// Block's sha3. - /// Querying by hash is always faster. - Hash(H256), - /// Block number within canon blockchain. - Number(BlockNumber), - /// Earliest block (genesis). - Earliest, - /// Latest mined block. - Latest, -} - -/// Contract address. -#[derive(Debug, Clone)] -pub enum ContractAddress { - /// Address is read from registry. - Registry, - /// Address is specified. - Address(ethereum_types::Address), -} - -/// Key pair with signing ability. -pub trait SigningKeyPair: Send + Sync { - /// Public portion of key. - fn public(&self) -> &Public; - /// Address of key owner. - fn address(&self) -> Address; - /// Sign data with the key. - fn sign(&self, data: &H256) -> Result; -} - -/// Wrapps client ChainNotify in order to send signal about new blocks -pub trait NewBlocksNotify: Send + Sync { - /// Fires when chain has new blocks. - /// Sends this signal only, if contracts' update required - fn new_blocks(&self, _new_enacted_len: usize) { - // does nothing by default - } -} - -/// Blockchain logs Filter. -#[derive(Debug, PartialEq)] -pub struct Filter { - /// Blockchain will be searched from this block. - pub from_block: BlockId, - - /// Search addresses. - /// - /// If None, match all. - /// If specified, log must be produced by one of these addresses. - pub address: Option>, - - /// Search topics. - /// - /// If None, match all. - /// If specified, log must contain one of these topics. - pub topics: Vec>>, -} - -/// Blockchain representation for Secret Store -pub trait SecretStoreChain: Send + Sync + 'static { - /// Adds listener for chain's NewBlocks event - fn add_listener(&self, target: Arc); - - /// Check if the underlying chain is in the trusted state - fn is_trusted(&self) -> bool; - - /// Transact contract. - fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError>; - - /// Read contract address. If address source is registry, address only returned if current client state is - /// trusted. Address from registry is read from registry from block latest block with - /// REQUEST_CONFIRMATIONS_REQUIRED confirmations. - fn read_contract_address(&self, registry_name: &str, address: &ContractAddress) -> Option
; - - /// Call contract in the blockchain - fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result; - - /// Returns blockhash for block id - fn block_hash(&self, id: BlockId) -> Option; - - /// Returns block number for block id - fn block_number(&self, id: BlockId) -> Option; - - /// Retrieve last blockchain logs for the filter - fn retrieve_last_logs(&self, filter: Filter) -> Option>; - - /// Get hash of the last block with predefined number of confirmations (depends on the chain). - fn get_confirmed_block_hash(&self) -> Option; -} diff --git a/secret-store/src/key_server.rs b/secret-store/src/key_server.rs deleted file mode 100644 index 3e5efb267..000000000 --- a/secret-store/src/key_server.rs +++ /dev/null @@ -1,714 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use std::sync::Arc; -use futures::{future::{err, result}, Future}; -use parking_lot::Mutex; -use crypto::DEFAULT_MAC; -use crypto::publickey::public_to_address; -use parity_runtime::Executor; -use super::acl_storage::AclStorage; -use super::key_storage::KeyStorage; -use super::key_server_set::KeyServerSet; -use blockchain::SigningKeyPair; -use key_server_cluster::{math, new_network_cluster, ClusterSession, WaitableSession}; -use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; -use types::{Error, Public, RequestSignature, Requester, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, - ClusterConfiguration, MessageHash, EncryptedMessageSignature, NodeId}; -use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration, NetConnectionsManagerConfig}; - -/// Secret store key server implementation -pub struct KeyServerImpl { - data: Arc>, -} - -/// Secret store key server data. -pub struct KeyServerCore { - cluster: Arc, -} - -impl KeyServerImpl { - /// Create new key server instance - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, - acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result - { - Ok(KeyServerImpl { - data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, self_key_pair, acl_storage, key_storage, executor)?)), - }) - } - - /// Get cluster client reference. - pub fn cluster(&self) -> Arc { - self.data.lock().cluster.clone() - } -} - -impl KeyServer for KeyServerImpl {} - -impl AdminSessionsServer for KeyServerImpl { - fn change_servers_set( - &self, - old_set_signature: RequestSignature, - new_set_signature: RequestSignature, - new_servers_set: BTreeSet, - ) -> Box + Send> { - return_session(self.data.lock().cluster - .new_servers_set_change_session(None, None, new_servers_set, old_set_signature, new_set_signature)) - } -} - -impl ServerKeyGenerator for KeyServerImpl { - fn generate_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - // recover requestor' address key from signature - let address = author.address(&key_id).map_err(Error::InsufficientRequesterData); - - // generate server key - return_session(address.and_then(|address| self.data.lock().cluster - .new_generation_session(key_id, None, address, threshold))) - } - - fn restore_key_public( - &self, - key_id: ServerKeyId, - author: Requester, - ) -> Box + Send> { - // recover requestor' public key from signature - let session_and_address = author - .address(&key_id) - .map_err(Error::InsufficientRequesterData) - .and_then(|address| self.data.lock().cluster.new_key_version_negotiation_session(key_id) - .map(|session| (session, address))); - let (session, address) = match session_and_address { - Ok((session, address)) => (session, address), - Err(error) => return Box::new(err(error)), - }; - - // negotiate key version && retrieve common key data - let core_session = session.session.clone(); - Box::new(session.into_wait_future() - .and_then(move |_| core_session.common_key_data() - .map(|key_share| (key_share, address))) - .and_then(|(key_share, address)| if key_share.author == address { - Ok(key_share.public) - } else { - Err(Error::AccessDenied) - })) - } -} - -impl DocumentKeyServer for KeyServerImpl { - fn store_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - common_point: Public, - encrypted_document_key: Public, - ) -> Box + Send> { - // store encrypted key - return_session(self.data.lock().cluster.new_encryption_session(key_id, - author.clone(), common_point, encrypted_document_key)) - } - - fn generate_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(author.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // generate server key - let data = self.data.clone(); - let server_key = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_generation_session(key_id, None, public_to_address(&public), threshold); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |server_key| (public, server_key))); - - // generate random document key - let document_key = server_key.and_then(|(public, server_key)| - result(math::generate_random_point() - .and_then(|document_key| math::encrypt_secret(&document_key, &server_key) - .map(|encrypted_document_key| (public, document_key, encrypted_document_key)))) - ); - - // store document key in the storage - let data = self.data.clone(); - let stored_document_key = document_key.and_then(move |(public, document_key, encrypted_document_key)| { - let data = data.lock(); - let session = data.cluster.new_encryption_session(key_id, - author.clone(), encrypted_document_key.common_point, encrypted_document_key.encrypted_point); - result(session.map(|session| (public, document_key, session))) - }) - .and_then(|(public, document_key, session)| session.into_wait_future().map(move |_| (public, document_key))); - - // encrypt document key with requestor public key - let encrypted_document_key = stored_document_key - .and_then(|(public, document_key)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, document_key.as_bytes()) - .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))); - - Box::new(encrypted_document_key) - } - - fn restore_document_key( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // decrypt document key - let data = self.data.clone(); - let stored_document_key = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_decryption_session(key_id, None, requester.clone(), None, false, false); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |document_key| (public, document_key))); - - // encrypt document key with requestor public key - let encrypted_document_key = stored_document_key - .and_then(|(public, document_key)| - crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, document_key.decrypted_secret.as_bytes()) - .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))); - - Box::new(encrypted_document_key) - } - - fn restore_document_key_shadow( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - return_session(self.data.lock().cluster.new_decryption_session(key_id, - None, requester.clone(), None, true, false)) - } -} - -impl MessageSigner for KeyServerImpl { - fn sign_message_schnorr( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // sign message - let data = self.data.clone(); - let signature = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_schnorr_signing_session(key_id, requester.clone().into(), None, message); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |signature| (public, signature))); - - // compose two message signature components into single one - let combined_signature = signature.map(|(public, signature)| { - let mut combined_signature = [0; 64]; - combined_signature[..32].clone_from_slice(signature.0.as_bytes()); - combined_signature[32..].clone_from_slice(signature.1.as_bytes()); - (public, combined_signature) - }); - - // encrypt signature with requestor public key - let encrypted_signature = combined_signature - .and_then(|(public, combined_signature)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, &combined_signature) - .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))); - - Box::new(encrypted_signature) - } - - fn sign_message_ecdsa( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // sign message - let data = self.data.clone(); - let signature = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_ecdsa_signing_session(key_id, requester.clone().into(), None, message); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |signature| (public, signature))); - - // encrypt combined signature with requestor public key - let encrypted_signature = signature - .and_then(|(public, signature)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, &*signature) - .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))); - - Box::new(encrypted_signature) - } -} - -impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, - acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result - { - let cconfig = NetClusterConfiguration { - self_key_pair: self_key_pair.clone(), - key_server_set: key_server_set, - acl_storage: acl_storage, - key_storage: key_storage, - admin_public: config.admin_public, - preserve_sessions: false, - }; - let net_config = NetConnectionsManagerConfig { - listen_address: (config.listener_address.address.clone(), config.listener_address.port), - allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes, - auto_migrate_enabled: config.auto_migrate_enabled, - }; - - let core = new_network_cluster(executor, cconfig, net_config)?; - let cluster = core.client(); - core.run()?; - - Ok(KeyServerCore { - cluster, - }) - } -} - -fn return_session( - session: Result, Error>, -) -> Box + Send> { - match session { - Ok(session) => Box::new(session.into_wait_future()), - Err(error) => Box::new(err(error)) - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::BTreeSet; - use std::time; - use std::sync::Arc; - use std::net::SocketAddr; - use std::collections::BTreeMap; - use futures::Future; - use crypto::DEFAULT_MAC; - use crypto::publickey::{Secret, Random, Generator, verify_public}; - use acl_storage::DummyAclStorage; - use key_storage::KeyStorage; - use key_storage::tests::DummyKeyStorage; - use node_key_pair::PlainNodeKeyPair; - use key_server_set::tests::MapKeyServerSet; - use key_server_cluster::math; - use ethereum_types::{H256, H520}; - use parity_runtime::Runtime; - use types::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature, - Requester, NodeId}; - use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; - use super::KeyServerImpl; - - #[derive(Default)] - pub struct DummyKeyServer; - - impl KeyServer for DummyKeyServer {} - - impl AdminSessionsServer for DummyKeyServer { - fn change_servers_set( - &self, - _old_set_signature: RequestSignature, - _new_set_signature: RequestSignature, - _new_servers_set: BTreeSet, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - impl ServerKeyGenerator for DummyKeyServer { - fn generate_key( - &self, - _key_id: ServerKeyId, - _author: Requester, - _threshold: usize, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn restore_key_public( - &self, - _key_id: ServerKeyId, - _author: Requester, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - impl DocumentKeyServer for DummyKeyServer { - fn store_document_key( - &self, - _key_id: ServerKeyId, - _author: Requester, - _common_point: Public, - _encrypted_document_key: Public, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn generate_document_key( - &self, - _key_id: ServerKeyId, - _author: Requester, - _threshold: usize, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn restore_document_key( - &self, - _key_id: ServerKeyId, - _requester: Requester, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn restore_document_key_shadow( - &self, - _key_id: ServerKeyId, - _requester: Requester, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - impl MessageSigner for DummyKeyServer { - fn sign_message_schnorr( - &self, - _key_id: ServerKeyId, - _requester: Requester, - _message: MessageHash, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn sign_message_ecdsa( - &self, - _key_id: ServerKeyId, - _requester: Requester, - _message: MessageHash, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - fn make_key_servers(start_port: u16, num_nodes: usize) -> (Vec, Vec>, Runtime) { - let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate()).collect(); - let configs: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { - listener_address: NodeAddress { - address: "127.0.0.1".into(), - port: start_port + (i as u16), - }, - nodes: key_pairs.iter().enumerate().map(|(j, kp)| (kp.public().clone(), - NodeAddress { - address: "127.0.0.1".into(), - port: start_port + (j as u16), - })).collect(), - key_server_set_contract_address: None, - allow_connecting_to_higher_nodes: false, - admin_public: None, - auto_migrate_enabled: false, - }).collect(); - let key_servers_set: BTreeMap = configs[0].nodes.iter() - .map(|(k, a)| (k.clone(), format!("{}:{}", a.address, a.port).parse().unwrap())) - .collect(); - let key_storages = (0..num_nodes).map(|_| Arc::new(DummyKeyStorage::default())).collect::>(); - let runtime = Runtime::with_thread_count(4); - let key_servers: Vec<_> = configs.into_iter().enumerate().map(|(i, cfg)| - KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(false, key_servers_set.clone())), - Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), - Arc::new(DummyAclStorage::default()), - key_storages[i].clone(), runtime.executor()).unwrap() - ).collect(); - - // wait until connections are established. It is fast => do not bother with events here - let start = time::Instant::now(); - let mut tried_reconnections = false; - loop { - if key_servers.iter().all(|ks| ks.cluster().is_fully_connected()) { - break; - } - - let old_tried_reconnections = tried_reconnections; - let mut fully_connected = true; - for key_server in &key_servers { - if !key_server.cluster().is_fully_connected() { - fully_connected = false; - if !old_tried_reconnections { - tried_reconnections = true; - key_server.cluster().connect(); - } - } - } - if fully_connected { - break; - } - if time::Instant::now() - start > time::Duration::from_millis(3000) { - panic!("connections are not established in 3000ms"); - } - } - - (key_servers, key_storages, runtime) - } - - #[test] - fn document_key_generation_and_retrievement_works_over_network_with_single_node() { - let _ = ::env_logger::try_init(); - let (key_servers, _, runtime) = make_key_servers(6070, 1); - - // generate document key - let threshold = 0; - let document = Random.generate().secret().clone(); - let secret = Random.generate().secret().clone(); - let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); - let generated_key = key_servers[0].generate_document_key( - *document, - signature.clone(), - threshold, - ).wait().unwrap(); - let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); - - // now let's try to retrieve key back - for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key( - *document, - signature.clone(), - ).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - assert_eq!(retrieved_key, generated_key); - } - drop(runtime); - } - - #[test] - fn document_key_generation_and_retrievement_works_over_network_with_3_nodes() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6080, 3); - - let test_cases = [0, 1, 2]; - for threshold in &test_cases { - // generate document key - let document = Random.generate().secret().clone(); - let secret = Random.generate().secret().clone(); - let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); - let generated_key = key_servers[0].generate_document_key( - *document, - signature.clone(), - *threshold, - ).wait().unwrap(); - let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); - - // now let's try to retrieve key back - for (i, key_server) in key_servers.iter().enumerate() { - let retrieved_key = key_server.restore_document_key( - *document, - signature.clone(), - ).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - assert_eq!(retrieved_key, generated_key); - - let key_share = key_storages[i].get(&document).unwrap().unwrap(); - assert!(key_share.common_point.is_some()); - assert!(key_share.encrypted_point.is_some()); - } - } - drop(runtime); - } - - #[test] - fn server_key_generation_and_storing_document_key_works_over_network_with_3_nodes() { - let _ = ::env_logger::try_init(); - let (key_servers, _, runtime) = make_key_servers(6090, 3); - - let test_cases = [0, 1, 2]; - for threshold in &test_cases { - // generate server key - let server_key_id = Random.generate().secret().clone(); - let requestor_secret = Random.generate().secret().clone(); - let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); - let server_public = key_servers[0].generate_key( - *server_key_id, - signature.clone(), - *threshold, - ).wait().unwrap(); - - // generate document key (this is done by KS client so that document key is unknown to any KS) - let generated_key = Random.generate().public().clone(); - let encrypted_document_key = math::encrypt_secret(&generated_key, &server_public).unwrap(); - - // store document key - key_servers[0].store_document_key(*server_key_id, signature.clone(), - encrypted_document_key.common_point, encrypted_document_key.encrypted_point).wait().unwrap(); - - // now let's try to retrieve key back - for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key(*server_key_id, signature.clone()).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - let retrieved_key = Public::from_slice(&retrieved_key); - assert_eq!(retrieved_key, generated_key); - } - } - drop(runtime); - } - - #[test] - fn server_key_generation_and_message_signing_works_over_network_with_3_nodes() { - let _ = ::env_logger::try_init(); - let (key_servers, _, runtime) = make_key_servers(6100, 3); - - let test_cases = [0, 1, 2]; - for threshold in &test_cases { - // generate server key - let server_key_id = Random.generate().secret().clone(); - let requestor_secret = Random.generate().secret().clone(); - let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); - let server_public = key_servers[0].generate_key( - *server_key_id, - signature.clone(), - *threshold, - ).wait().unwrap(); - - // sign message - let message_hash = H256::from_low_u64_be(42); - let combined_signature = key_servers[0].sign_message_schnorr( - *server_key_id, - signature, - message_hash, - ).wait().unwrap(); - let combined_signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); - let signature_c = Secret::copy_from_slice(&combined_signature[..32]).unwrap(); - let signature_s = Secret::copy_from_slice(&combined_signature[32..]).unwrap(); - - // check signature - assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); - } - drop(runtime); - } - - #[test] - fn decryption_session_is_delegated_when_node_does_not_have_key_share() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6110, 3); - - // generate document key - let threshold = 0; - let document = Random.generate().secret().clone(); - let secret = Random.generate().secret().clone(); - let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); - let generated_key = key_servers[0].generate_document_key( - *document, - signature.clone(), - threshold, - ).wait().unwrap(); - let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); - - // remove key from node0 - key_storages[0].remove(&document).unwrap(); - - // now let's try to retrieve key back by requesting it from node0, so that session must be delegated - let retrieved_key = key_servers[0].restore_document_key(*document, signature).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - assert_eq!(retrieved_key, generated_key); - drop(runtime); - } - - #[test] - fn schnorr_signing_session_is_delegated_when_node_does_not_have_key_share() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6114, 3); - let threshold = 1; - - // generate server key - let server_key_id = Random.generate().secret().clone(); - let requestor_secret = Random.generate().secret().clone(); - let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); - let server_public = key_servers[0].generate_key(*server_key_id, signature.clone(), threshold).wait().unwrap(); - - // remove key from node0 - key_storages[0].remove(&server_key_id).unwrap(); - - // sign message - let message_hash = H256::from_low_u64_be(42); - let combined_signature = key_servers[0].sign_message_schnorr( - *server_key_id, - signature, - message_hash, - ).wait().unwrap(); - let combined_signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); - let signature_c = Secret::copy_from_slice(&combined_signature[..32]).unwrap(); - let signature_s = Secret::copy_from_slice(&combined_signature[32..]).unwrap(); - - // check signature - assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); - drop(runtime); - } - - #[test] - fn ecdsa_signing_session_is_delegated_when_node_does_not_have_key_share() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6117, 4); - let threshold = 1; - - // generate server key - let server_key_id = Random.generate().secret().clone(); - let requestor_secret = Random.generate().secret().clone(); - let signature = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap(); - let server_public = key_servers[0].generate_key( - *server_key_id, - signature.clone().into(), - threshold, - ).wait().unwrap(); - - // remove key from node0 - key_storages[0].remove(&server_key_id).unwrap(); - - // sign message - let message_hash = H256::random(); - let signature = key_servers[0].sign_message_ecdsa( - *server_key_id, - signature.clone().into(), - message_hash, - ).wait().unwrap(); - let signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &signature).unwrap(); - let signature = H520::from_slice(&signature[0..65]); - - // check signature - assert!(verify_public(&server_public, &signature.into(), &message_hash).unwrap()); - drop(runtime); - } - - #[test] - fn servers_set_change_session_works_over_network() { - // TODO [Test] - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs b/secret-store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs deleted file mode 100644 index b90d850ff..000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::{Address, H256}; -use crypto::publickey::Secret; -use futures::Oneshot; -use parking_lot::Mutex; -use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; -use key_server_cluster::signing_session_ecdsa::SessionImpl as EcdsaSigningSession; -use key_server_cluster::signing_session_schnorr::SessionImpl as SchnorrSigningSession; -use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, - KeyVersions, KeyVersionsError, FailedKeyVersionContinueAction, CommonKeyData}; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -// TODO [Opt]: change sessions so that versions are sent by chunks. -/// Number of versions sent in single message. -const VERSIONS_PER_MESSAGE: usize = 32; - -/// Key version negotiation transport. -pub trait SessionTransport { - /// Broadcast message to all nodes. - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error>; - /// Send message to given node. - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error>; -} - -/// Key version negotiation result computer. -pub trait SessionResultComputer: Send + Sync { - /// Compute result of session, if possible. - fn compute_result(&self, threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option>; -} - -/// Key discovery session API. -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Action after key version is negotiated. -#[derive(Clone)] -pub enum ContinueAction { - /// Decryption session + origin + is_shadow_decryption + is_broadcast_decryption. - Decrypt(Arc, Option
, bool, bool), - /// Schnorr signing session + message hash. - SchnorrSign(Arc, H256), - /// ECDSA signing session + message hash. - EcdsaSign(Arc, H256), -} - -/// Failed action after key version is negotiated. -#[derive(Clone, Debug, PartialEq)] -pub enum FailedContinueAction { - /// Decryption origin + requester. - Decrypt(Option
, Address), -} - -/// Immutable session data. -struct SessionCore { - /// Session meta. - pub meta: ShareChangeSessionMeta, - /// Sub-session id. - pub sub_session: Secret, - /// Key share. - pub key_share: Option, - /// Session result computer. - pub result_computer: Arc, - /// Session transport. - pub transport: T, - /// Session nonce. - pub nonce: u64, - /// Session completion signal. - pub completed: CompletionSignal>, -} - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Initialization confirmations. - pub confirmations: Option>, - /// Common key data that nodes have agreed upon. - pub key_share: Option, - /// { Version => Nodes } - pub versions: Option>>, - /// Session result. - pub result: Option, Error>>, - /// Continue action. - pub continue_with: Option, - /// Failed continue action (reported in error message by master node). - pub failed_continue_with: Option, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session meta. - pub meta: ShareChangeSessionMeta, - /// Sub-session id. - pub sub_session: Secret, - /// Key share. - pub key_share: Option, - /// Session result computer. - pub result_computer: Arc, - /// Session transport to communicate to other cluster nodes. - pub transport: T, - /// Session nonce. - pub nonce: u64, -} - -/// Signing session state. -#[derive(Debug, PartialEq)] -enum SessionState { - /// Waiting for initialization. - WaitingForInitialization, - /// Waiting for responses. - WaitingForResponses, - /// Session is completed. - Finished, -} - -/// Isolated session transport. -pub struct IsolatedSessionTransport { - /// Cluster. - pub cluster: Arc, - /// Key id. - pub key_id: SessionId, - /// Sub session id. - pub sub_session: Secret, - /// Session-level nonce. - pub nonce: u64, -} - -/// Fastest session result computer. Computes first possible version that can be recovered on this node. -/// If there's no such version, selects version with the most support. -pub struct FastestResultComputer { - /// This node id. - self_node_id: NodeId, - /// Threshold (if known). - threshold: Option, - /// Count of all configured key server nodes. - configured_nodes_count: usize, - /// Count of all connected key server nodes. - connected_nodes_count: usize, -} - -/// Selects version with most support, waiting for responses from all nodes. -pub struct LargestSupportResultComputer; - -impl SessionImpl where T: SessionTransport { - /// Create new session. - pub fn new(params: SessionParams) -> (Self, Oneshot, Error>>) { - let (completed, oneshot) = CompletionSignal::new(); - (SessionImpl { - core: SessionCore { - meta: params.meta, - sub_session: params.sub_session, - key_share: params.key_share.clone(), - result_computer: params.result_computer, - transport: params.transport, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - confirmations: None, - key_share: params.key_share.map(|key_share| DocumentKeyShare { - threshold: key_share.threshold, - author: key_share.author, - public: key_share.public, - ..Default::default() - }), - versions: None, - result: None, - continue_with: None, - failed_continue_with: None, - }) - }, oneshot) - } - - /// Return session meta. - pub fn meta(&self) -> &ShareChangeSessionMeta { - &self.core.meta - } - - /// Return result computer reference. - pub fn version_holders(&self, version: &H256) -> Result, Error> { - Ok(self.data.lock().versions.as_ref().ok_or(Error::InvalidStateForRequest)? - .get(version).ok_or(Error::ServerKeyIsNotFound)? - .clone()) - } - - /// Set continue action. - pub fn set_continue_action(&self, action: ContinueAction) { - self.data.lock().continue_with = Some(action); - } - - /// Take continue action. - pub fn take_continue_action(&self) -> Option { - self.data.lock().continue_with.take() - } - - /// Take failed continue action. - pub fn take_failed_continue_action(&self) -> Option { - self.data.lock().failed_continue_with.take() - } - - /// Return session completion result (if available). - pub fn result(&self) -> Option, Error>> { - self.data.lock().result.clone() - } - - /// Retrieve common key data (author, threshold, public), if available. - pub fn common_key_data(&self) -> Result { - self.data.lock().key_share.clone() - .ok_or(Error::InvalidStateForRequest) - } - - /// Initialize session. - pub fn initialize(&self, connected_nodes: BTreeSet) -> Result<(), Error> { - // check state - let mut data = self.data.lock(); - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update state - let mut confirmations = connected_nodes; - let mut versions: BTreeMap> = BTreeMap::new(); - let received_own_confirmation = confirmations.remove(&self.core.meta.self_node_id); - if received_own_confirmation { - if let Some(key_share) = self.core.key_share.as_ref() { - for version in &key_share.versions { - versions.entry(version.hash.clone()) - .or_insert_with(Default::default) - .insert(self.core.meta.self_node_id.clone()); - } - } - } - - // update state - let no_confirmations_required = confirmations.is_empty(); - data.state = SessionState::WaitingForResponses; - data.confirmations = Some(confirmations); - data.versions = Some(versions); - - // try to complete session - Self::try_complete(&self.core, &mut *data); - if no_confirmations_required && data.state != SessionState::Finished { - return Err(Error::ServerKeyIsNotFound); - } else if data.state == SessionState::Finished { - return Ok(()); - } - - // send requests - let confirmations = data.confirmations.as_ref().expect("dilled couple of lines above; qed"); - for connected_node in confirmations { - self.core.transport.send(connected_node, KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: self.core.meta.id.clone().into(), - sub_session: self.core.sub_session.clone().into(), - session_nonce: self.core.nonce, - }))?; - } - - Ok(()) - } - - /// Process single message. - pub fn process_message(&self, sender: &NodeId, message: &KeyVersionNegotiationMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &KeyVersionNegotiationMessage::RequestKeyVersions(ref message) => - self.on_key_versions_request(sender, message), - &KeyVersionNegotiationMessage::KeyVersions(ref message) => - self.on_key_versions(sender, message), - &KeyVersionNegotiationMessage::KeyVersionsError(ref message) => { - // remember failed continue action - if let Some(FailedKeyVersionContinueAction::Decrypt(Some(ref origin), ref requester)) = message.continue_with { - self.data.lock().failed_continue_with = - Some(FailedContinueAction::Decrypt(Some(origin.clone().into()), requester.clone().into())); - } - - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - } - } - - /// Process key versions request. - pub fn on_key_versions_request(&self, sender: &NodeId, _message: &RequestKeyVersions) -> Result<(), Error> { - debug_assert!(sender != &self.core.meta.self_node_id); - - // check message - if *sender != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // send response - self.core.transport.send(sender, KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: self.core.meta.id.clone().into(), - sub_session: self.core.sub_session.clone().into(), - session_nonce: self.core.nonce, - key_common: self.core.key_share.as_ref().map(|key_share| CommonKeyData { - threshold: key_share.threshold, - author: key_share.author.into(), - public: key_share.public.into(), - }), - versions: self.core.key_share.as_ref().map(|key_share| - key_share.versions.iter().rev() - .filter(|v| v.id_numbers.contains_key(sender)) - .chain(key_share.versions.iter().rev().filter(|v| !v.id_numbers.contains_key(sender))) - .map(|v| v.hash.clone().into()) - .take(VERSIONS_PER_MESSAGE) - .collect()) - .unwrap_or_else(|| Default::default()) - }))?; - - // update state - data.state = SessionState::Finished; - data.result = Some(Ok(None)); - self.core.completed.send(Ok(None)); - - Ok(()) - } - - /// Process key versions response. - pub fn on_key_versions(&self, sender: &NodeId, message: &KeyVersions) -> Result<(), Error> { - debug_assert!(sender != &self.core.meta.self_node_id); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::WaitingForResponses && data.state != SessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - let reason = "this field is filled on master node when initializing; this is initialized master node; qed"; - if !data.confirmations.as_mut().expect(reason).remove(sender) { - return Err(Error::InvalidMessage); - } - - // remember versions that sender have - { - match message.key_common.as_ref() { - Some(key_common) if data.key_share.is_none() => { - data.key_share = Some(DocumentKeyShare { - threshold: key_common.threshold, - author: key_common.author.clone().into(), - public: key_common.public.clone().into(), - ..Default::default() - }); - }, - Some(key_common) => { - let prev_key_share = data.key_share.as_ref() - .expect("data.key_share.is_none() is matched by previous branch; qed"); - if prev_key_share.threshold != key_common.threshold || - prev_key_share.author.as_bytes() != key_common.author.as_bytes() || - prev_key_share.public.as_bytes() != key_common.public.as_bytes() - { - return Err(Error::InvalidMessage); - } - }, - None if message.versions.is_empty() => (), - None => return Err(Error::InvalidMessage), - } - - let versions = data.versions.as_mut().expect(reason); - for version in &message.versions { - versions.entry(version.clone().into()) - .or_insert_with(Default::default) - .insert(sender.clone()); - } - } - - // try to compute result - if data.state != SessionState::Finished { - Self::try_complete(&self.core, &mut *data); - } - - Ok(()) - } - - /// Try to complete result && finish session. - fn try_complete(core: &SessionCore, data: &mut SessionData) { - let reason = "this field is filled on master node when initializing; try_complete is only called on initialized master node; qed"; - let confirmations = data.confirmations.as_ref().expect(reason); - let versions = data.versions.as_ref().expect(reason); - let threshold = data.key_share.as_ref().map(|key_share| key_share.threshold); - if let Some(result) = core.result_computer.compute_result(threshold, confirmations, versions) { - // when the master node processing decryption service request, it starts with a key version negotiation session - // if the negotiation fails, only master node knows about it - // => if the error is fatal, only the master will know about it and report it to the contract && the request will never be rejected - // => let's broadcast fatal error so that every other node know about it, and, if it trusts to master node - // will report error to the contract - if let (Some(continue_with), Err(error)) = (data.continue_with.as_ref(), result.as_ref()) { - let origin = match *continue_with { - ContinueAction::Decrypt(_, origin, _, _) => origin.clone(), - _ => None, - }; - - let requester = match *continue_with { - ContinueAction::Decrypt(ref session, _, _, _) => session.requester().and_then(|r| r.address(&core.meta.id).ok()), - _ => None, - }; - - if origin.is_some() && requester.is_some() && !error.is_non_fatal() { - let requester = requester.expect("checked in above condition; qed"); - data.failed_continue_with = - Some(FailedContinueAction::Decrypt(origin.clone(), requester.clone())); - - let send_result = core.transport.broadcast(KeyVersionNegotiationMessage::KeyVersionsError(KeyVersionsError { - session: core.meta.id.clone().into(), - sub_session: core.sub_session.clone().into(), - session_nonce: core.nonce, - error: error.clone(), - continue_with: Some(FailedKeyVersionContinueAction::Decrypt( - origin.map(Into::into), - requester.into(), - )), - })); - - if let Err(send_error) = send_result { - warn!(target: "secretstore_net", "{}: failed to broadcast key version negotiation error {}: {}", - core.meta.self_node_id, error, send_error); - } - } - } - - let result = result.map(Some); - data.state = SessionState::Finished; - data.result = Some(result.clone()); - core.completed.send(result); - } - } -} - -impl ClusterSession for SessionImpl where T: SessionTransport { - type Id = SessionIdWithSubSession; - type CreationData = (); - type SuccessfulResult = Option<(H256, NodeId)>; - - fn type_name() -> &'static str { - "version negotiation" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.sub_session.clone()) - } - - fn is_finished(&self) -> bool { - self.data.lock().state == SessionState::Finished - } - - fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - if data.confirmations.is_some() { - data.confirmations.as_mut().expect("checked a line above; qed").clear(); - Self::try_complete(&self.core, &mut *data); - if data.state != SessionState::Finished { - warn!(target: "secretstore_net", "{}: key version negotiation session failed with timeout", self.core.meta.self_node_id); - - data.result = Some(Err(Error::ConsensusTemporaryUnreachable)); - self.core.completed.send(Err(Error::ConsensusTemporaryUnreachable)); - } - } - } - - fn on_node_timeout(&self, node: &NodeId) { - self.on_session_error(node, Error::NodeDisconnected) - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let mut data = self.data.lock(); - - if data.confirmations.is_some() { - let is_waiting_for_confirmation = data.confirmations.as_mut().expect("checked a line above; qed").remove(node); - if !is_waiting_for_confirmation { - return; - } - - Self::try_complete(&self.core, &mut *data); - if data.state == SessionState::Finished { - return; - } - } - - warn!(target: "secretstore_net", "{}: key version negotiation session failed because of {} from {}", - self.core.meta.self_node_id, error, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error.clone())); - self.core.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::KeyVersionNegotiation(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl SessionTransport for IsolatedSessionTransport { - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.broadcast(Message::KeyVersionNegotiation(message)) - } - - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.send(node, Message::KeyVersionNegotiation(message)) - } -} - -impl FastestResultComputer { - pub fn new(self_node_id: NodeId, key_share: Option<&DocumentKeyShare>, configured_nodes_count: usize, connected_nodes_count: usize) -> Self { - let threshold = key_share.map(|ks| ks.threshold); - FastestResultComputer { - self_node_id, - threshold, - configured_nodes_count, - connected_nodes_count, - } - }} - -impl SessionResultComputer for FastestResultComputer { - fn compute_result(&self, threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option> { - match self.threshold.or(threshold) { - // if there's no versions at all && we're not waiting for confirmations anymore - _ if confirmations.is_empty() && versions.is_empty() => Some(Err(Error::ServerKeyIsNotFound)), - // if we have key share on this node - Some(threshold) => { - // select version this node have, with enough participants - let has_key_share = self.threshold.is_some(); - let version = versions.iter().find(|&(_, ref n)| !has_key_share || n.contains(&self.self_node_id) && n.len() >= threshold + 1); - // if there's no such version, wait for more confirmations - match version { - Some((version, nodes)) => Some(Ok((version.clone(), if has_key_share { self.self_node_id.clone() } else { nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed") }))), - None if !confirmations.is_empty() => None, - // otherwise - try to find any version - None => Some(versions.iter() - .find(|&(_, ref n)| n.len() >= threshold + 1) - .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed")))) - // if there's no version consensus among all connected nodes - // AND we're connected to ALL configured nodes - // OR there are less than required nodes for key restore - // => this means that we can't restore key with CURRENT configuration => respond with fatal error - // otherwise we could try later, after all nodes are connected - .unwrap_or_else(|| Err(if self.configured_nodes_count == self.connected_nodes_count - || self.configured_nodes_count < threshold + 1 { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }))), - } - }, - // if we do not have share, then wait for all confirmations - None if !confirmations.is_empty() => None, - // ...and select version with largest support - None => Some(versions.iter() - .max_by_key(|&(_, ref n)| n.len()) - .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed")))) - .unwrap_or_else(|| Err(if self.configured_nodes_count == self.connected_nodes_count { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }))), - } - } -} - -impl SessionResultComputer for LargestSupportResultComputer { - fn compute_result(&self, _threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option> { - if !confirmations.is_empty() { - return None; - } - if versions.is_empty() { - return Some(Err(Error::ServerKeyIsNotFound)); - } - - versions.iter() - .max_by_key(|&(_, ref n)| n.len()) - .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed")))) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use ethereum_types::{H512, H160, Address}; - use crypto::publickey::public_to_address; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage, - DocumentKeyShare, DocumentKeyShareVersion}; - use key_server_cluster::math; - use key_server_cluster::cluster::Cluster; - use key_server_cluster::cluster::tests::DummyCluster; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - use key_server_cluster::decryption_session::create_default_decryption_session; - use key_server_cluster::message::{ - Message, KeyVersionNegotiationMessage, RequestKeyVersions, - CommonKeyData, KeyVersions, - }; - use super::{ - SessionImpl, SessionTransport, SessionParams, FastestResultComputer, LargestSupportResultComputer, - SessionResultComputer, SessionState, ContinueAction, FailedContinueAction, - }; - - struct DummyTransport { - cluster: Arc, - } - - impl SessionTransport for DummyTransport { - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.broadcast(Message::KeyVersionNegotiation(message)) - } - - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.send(node, Message::KeyVersionNegotiation(message)) - } - } - - struct Node { - pub cluster: Arc, - pub key_storage: Arc, - pub session: SessionImpl, - } - - struct MessageLoop { - pub session_id: SessionId, - pub nodes: BTreeMap, - pub queue: VecDeque<(NodeId, NodeId, Message)>, - } - - impl MessageLoop { - pub fn prepare_nodes(nodes_num: usize) -> BTreeMap> { - (0..nodes_num).map(|_| (math::generate_random_point().unwrap(), - Arc::new(DummyKeyStorage::default()))).collect() - } - - pub fn empty(nodes_num: usize) -> Self { - Self::new(Self::prepare_nodes(nodes_num)) - } - - pub fn new(nodes: BTreeMap>) -> Self { - let master_node_id = nodes.keys().cloned().nth(0).unwrap(); - let sub_sesion = math::generate_random_scalar().unwrap(); - let all_nodes_ids: BTreeSet<_> = nodes.keys().cloned().collect(); - MessageLoop { - session_id: Default::default(), - nodes: nodes.iter().map(|(node_id, key_storage)| { - let cluster = Arc::new(DummyCluster::new(node_id.clone())); - cluster.add_nodes(all_nodes_ids.iter().cloned()); - (node_id.clone(), Node { - cluster: cluster.clone(), - key_storage: key_storage.clone(), - session: SessionImpl::new(SessionParams { - meta: ShareChangeSessionMeta { - id: Default::default(), - self_node_id: node_id.clone(), - master_node_id: master_node_id.clone(), - configured_nodes_count: nodes.len(), - connected_nodes_count: nodes.len(), - }, - sub_session: sub_sesion.clone(), - key_share: key_storage.get(&Default::default()).unwrap(), - result_computer: Arc::new(FastestResultComputer::new( - node_id.clone(), - key_storage.get(&Default::default()).unwrap().as_ref(), - nodes.len(), nodes.len() - )), - transport: DummyTransport { - cluster: cluster, - }, - nonce: 0, - }).0, - }) - }).collect(), - queue: VecDeque::new(), - } - } - - pub fn node_id(&self, idx: usize) -> &NodeId { - self.nodes.keys().nth(idx).unwrap() - } - - pub fn session(&self, idx: usize) -> &SessionImpl { - &self.nodes.values().nth(idx).unwrap().session - } - - pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { - self.nodes.values() - .filter_map(|n| n.cluster.take_message().map(|m| (n.session.meta().self_node_id.clone(), m.0, m.1))) - .nth(0) - .or_else(|| self.queue.pop_front()) - } - - pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { - match msg.2 { - Message::KeyVersionNegotiation(message) => - self.nodes[&msg.1].session.process_message(&msg.0, &message), - _ => panic!("unexpected"), - } - } - - pub fn run(&mut self) { - while let Some((from, to, message)) = self.take_message() { - self.process_message((from, to, message)).unwrap(); - } - } - } - - #[test] - fn negotiation_fails_if_initialized_twice() { - let ml = MessageLoop::empty(1); - assert_eq!(ml.session(0).initialize(BTreeSet::new()), Ok(())); - assert_eq!(ml.session(0).initialize(BTreeSet::new()), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn negotiation_fails_if_message_contains_wrong_nonce() { - let ml = MessageLoop::empty(2); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 100, - })), Err(Error::ReplayProtection)); - } - - #[test] - fn negotiation_fails_if_versions_request_received_from_non_master() { - let ml = MessageLoop::empty(3); - assert_eq!(ml.session(2).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - })), Err(Error::InvalidMessage)); - } - - #[test] - fn negotiation_fails_if_versions_request_received_twice() { - let ml = MessageLoop::empty(2); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - })), Ok(())); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - })), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn negotiation_fails_if_versions_received_before_initialization() { - let ml = MessageLoop::empty(2); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 10, - author: Default::default(), - public: Default::default(), - }), - versions: Vec::new(), - })), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn negotiation_does_not_fails_if_versions_received_after_completion() { - let ml = MessageLoop::empty(3); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - assert_eq!(ml.session(0).data.lock().state, SessionState::WaitingForResponses); - - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 0, - author: Default::default(), - public: Default::default(), - }), - - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).data.lock().state, SessionState::Finished); - - assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 0, - author: Default::default(), - public: Default::default(), - }), - - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).data.lock().state, SessionState::Finished); - } - - #[test] - fn negotiation_fails_if_wrong_common_data_sent() { - fn run_test(key_common: CommonKeyData) { - let ml = MessageLoop::empty(3); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 1, - author: Default::default(), - public: Default::default(), - }), - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(key_common), - versions: vec![version_id.clone().into()] - })), Err(Error::InvalidMessage)); - } - - run_test(CommonKeyData { - threshold: 2, - author: Default::default(), - public: Default::default(), - }); - - run_test(CommonKeyData { - threshold: 1, - author: H160::from_low_u64_be(1).into(), - public: Default::default(), - }); - - run_test(CommonKeyData { - threshold: 1, - author: H160::from_low_u64_be(2).into(), - public: Default::default(), - }); - } - - #[test] - fn negotiation_fails_if_threshold_empty_when_versions_are_not_empty() { - let ml = MessageLoop::empty(2); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: None, - versions: vec![version_id.clone().into()] - })), Err(Error::InvalidMessage)); - } - - #[test] - fn fast_negotiation_does_not_completes_instantly_when_enough_share_owners_are_connected() { - let nodes = MessageLoop::prepare_nodes(2); - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - nodes.values().nth(0).unwrap().insert(Default::default(), DocumentKeyShare { - author: H160::from_low_u64_be(2), - threshold: 1, - public: H512::from_low_u64_be(3), - common_point: None, - encrypted_point: None, - versions: vec![DocumentKeyShareVersion { - hash: version_id, - id_numbers: vec![(nodes.keys().cloned().nth(0).unwrap(), math::generate_random_scalar().unwrap())].into_iter().collect(), - secret_share: math::generate_random_scalar().unwrap(), - }], - }).unwrap(); - let ml = MessageLoop::new(nodes); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - // we can't be sure that node has given key version because previous ShareAdd session could fail - assert!(ml.session(0).data.lock().state != SessionState::Finished); - - // check that upon completion, commmon key data is known - assert_eq!(ml.session(0).common_key_data(), Ok(DocumentKeyShare { - author: H160::from_low_u64_be(2), - threshold: 1, - public: H512::from_low_u64_be(3), - ..Default::default() - })); - } - - #[test] - fn fastest_computer_returns_missing_share_if_no_versions_returned() { - let computer = FastestResultComputer { - self_node_id: Default::default(), - threshold: None, - configured_nodes_count: 1, - connected_nodes_count: 1, - }; - assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::ServerKeyIsNotFound))); - } - - #[test] - fn largest_computer_returns_missing_share_if_no_versions_returned() { - let computer = LargestSupportResultComputer; - assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::ServerKeyIsNotFound))); - } - - #[test] - fn fatal_error_is_not_broadcasted_if_started_without_origin() { - let mut ml = MessageLoop::empty(3); - ml.session(0).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), None, false, false)); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - ml.run(); - - assert!(ml.nodes.values().all(|n| n.session.is_finished() && - n.session.take_failed_continue_action().is_none())); - } - - #[test] - fn fatal_error_is_broadcasted_if_started_with_origin() { - let mut ml = MessageLoop::empty(3); - ml.session(0).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), Some(Address::from_low_u64_be(1)), true, true)); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - ml.run(); - - // on all nodes session is completed - assert!(ml.nodes.values().all(|n| n.session.is_finished())); - - // slave nodes have non-empty failed continue action - assert!(ml.nodes.values().skip(1).all(|n| n.session.take_failed_continue_action() - == Some(FailedContinueAction::Decrypt(Some(Address::from_low_u64_be(1)), public_to_address(&H512::from_low_u64_be(2)))))); - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/mod.rs b/secret-store/src/key_server_cluster/admin_sessions/mod.rs deleted file mode 100644 index c253f4436..000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod key_version_negotiation_session; -pub mod servers_set_change_session; -pub mod share_add_session; -pub mod share_change_session; - -mod sessions_queue; - -use key_server_cluster::{SessionId, NodeId, SessionMeta, Error}; - -/// Share change session metadata. -#[derive(Debug, Clone)] -pub struct ShareChangeSessionMeta { - /// Key id. - pub id: SessionId, - /// Id of node, which has started this session. - pub master_node_id: NodeId, - /// Id of node, on which this session is running. - pub self_node_id: NodeId, - /// Count of all configured key server nodes. - pub configured_nodes_count: usize, - /// Count of all connected key server nodes. - pub connected_nodes_count: usize, -} - -impl ShareChangeSessionMeta { - /// Convert to consensus session meta. `all_nodes_set` is the union of `old_nodes_set` && `new_nodes_set`. - pub fn into_consensus_meta(self, all_nodes_set_len: usize) -> Result { - Ok(SessionMeta { - id: self.id, - master_node_id: self.master_node_id, - self_node_id: self.self_node_id, - threshold: all_nodes_set_len.checked_sub(1).ok_or(Error::ConsensusUnreachable)?, - configured_nodes_count: self.configured_nodes_count, - connected_nodes_count: self.connected_nodes_count, - }) - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs b/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs deleted file mode 100644 index f22f83e0a..000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs +++ /dev/null @@ -1,1456 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use std::collections::btree_map::Entry; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::H256; -use crypto::publickey::{Public, Signature}; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage}; -use key_server_cluster::math; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, ServersSetChangeMessage, - ConsensusMessageWithServersSet, InitializeConsensusSessionWithServersSet, - ServersSetChangeConsensusMessage, ConfirmConsensusInitialization, UnknownSessionsRequest, UnknownSessions, - ServersSetChangeShareAddMessage, ServersSetChangeError, ServersSetChangeCompleted, - ServersSetChangeDelegate, ServersSetChangeDelegateResponse, InitializeShareChangeSession, - ConfirmShareChangeSessionInitialization, KeyVersionNegotiationMessage, ShareChangeKeyVersionNegotiation}; -use key_server_cluster::share_change_session::{ShareChangeSession, ShareChangeSessionParams, ShareChangeSessionPlan, - prepare_share_change_session_plan}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, - SessionParams as KeyVersionNegotiationSessionParams, LargestSupportResultComputer, - SessionTransport as KeyVersionNegotiationTransport}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::servers_set_change_access_job::{ServersSetChangeAccessJob, ServersSetChangeAccessRequest}; -use key_server_cluster::jobs::unknown_sessions_job::{UnknownSessionsJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; -use key_server_cluster::admin_sessions::sessions_queue::SessionsQueue; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Maximal number of active share change sessions. -const MAX_ACTIVE_KEY_SESSIONS: usize = 64; - -/// Servers set change session. -/// Brief overview: -/// 1) consensus establishing -/// 2) master node requests all other nodes for sessions he is not participating (aka unknown sessions) -/// 3) every slave node responds with sessions id => we are able to collect Map of unknown sessions on master -/// 4) for every known session (i.e. session that master participates in): -/// 4.1) share change plan is created = nodes to add shares for, nodes to move shares from/to, nodes to remove shares from -/// 4.2) share change session is started. Share change session = sequential execution of ShareAdd, then ShareMove && then ShareRemove sessions (order matters here) for single key -/// 5) for every unknown session: -/// 5.1) sub_master is selected from sessions participants -/// 5.2) share change session is delegated from master to this sub_master -/// 5.3) share change session is executed by this sub_master -/// 5.4) share change confirm is sent from sub_master to master -/// 6) upon completing all known share change sessions && receiving confirmations for all unknown share change sessions, session completion signal is sent to all slave nodes && session is completed -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Session state. -#[derive(Debug, PartialEq)] -enum SessionState { - /// Establishing consensus. - EstablishingConsensus, - /// Running share change sessions. - RunningShareChangeSessions, - /// Session is completed. - Finished, -} - -/// Immutable session data. -struct SessionCore { - /// Servers set change session meta (id is computed from new_nodes_set). - pub meta: ShareChangeSessionMeta, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Keys storage. - pub key_storage: Arc, - /// Session-level nonce. - pub nonce: u64, - /// All known nodes. - pub all_nodes_set: BTreeSet, - /// Administrator public key. - pub admin_public: Public, - /// Migration id (if this session is a part of auto-migration process). - pub migration_id: Option, - /// Session completion signal. - pub completed: CompletionSignal<()>, -} - -/// Servers set change consensus session type. -type ServersSetChangeConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Consensus-based servers set change session. - pub consensus_session: Option, - /// New nodes set. - pub new_nodes_set: Option>, - /// Share change sessions queue (valid on master nodes only). - pub sessions_queue: Option, - /// Share change sessions key version negotiation. - pub negotiation_sessions: BTreeMap>, - /// Share change sessions initialization state (valid on master nodes only). - pub sessions_initialization_state: BTreeMap, - /// Sessions delegated to other nodes (valid on master node only). - pub delegated_key_sessions: BTreeMap, - /// Active share change sessions. - pub active_key_sessions: BTreeMap, - /// Servers set change result. - pub result: Option>, -} - -/// Session initialization data. -struct SessionInitializationData { - /// Master node id. - pub master: NodeId, - /// Nodes that have confirmed session initialization request. - pub confirmations: BTreeSet, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session meta (artificial). - pub meta: ShareChangeSessionMeta, - /// Cluster. - pub cluster: Arc, - /// Keys storage. - pub key_storage: Arc, - /// Session nonce. - pub nonce: u64, - /// All known nodes. - pub all_nodes_set: BTreeSet, - /// Administrator public key. - pub admin_public: Public, - /// Migration id (if this session is a part of auto-migration process). - pub migration_id: Option, -} - -/// Servers set change consensus transport. -struct ServersSetChangeConsensusTransport { - /// Session id. - id: SessionId, - /// Session-level nonce. - nonce: u64, - /// Migration id (if part of auto-migration process). - migration_id: Option, - /// Cluster. - cluster: Arc, -} - -/// Unknown sessions job transport. -struct UnknownSessionsJobTransport { - /// Session id. - id: SessionId, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -/// Key version negotiation transport. -struct ServersSetChangeKeyVersionNegotiationTransport { - /// Session id. - id: SessionId, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -impl SessionImpl { - /// Create new servers set change session. - pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - cluster: params.cluster, - key_storage: params.key_storage, - nonce: params.nonce, - all_nodes_set: params.all_nodes_set, - admin_public: params.admin_public, - migration_id: params.migration_id, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::EstablishingConsensus, - consensus_session: None, - new_nodes_set: None, - sessions_queue: None, - negotiation_sessions: BTreeMap::new(), - sessions_initialization_state: BTreeMap::new(), - delegated_key_sessions: BTreeMap::new(), - active_key_sessions: BTreeMap::new(), - result: None, - }), - }, oneshot)) - } - - /// Get session id. - pub fn id(&self) -> &SessionId { - &self.core.meta.id - } - - /// Get migration id. - pub fn migration_id(&self) -> Option<&H256> { - self.core.migration_id.as_ref() - } - - /// Return session completion result (if available). - pub fn result(&self) -> Option> { - self.data.lock().result.clone() - } - - /// Initialize servers set change session on master node. - pub fn initialize(&self, new_nodes_set: BTreeSet, all_set_signature: Signature, new_set_signature: Signature) -> Result<(), Error> { - check_nodes_set(&self.core.all_nodes_set, &new_nodes_set)?; - - let mut data = self.data.lock(); - if data.state != SessionState::EstablishingConsensus || data.consensus_session.is_some() { - return Err(Error::InvalidStateForRequest); - } - - let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(self.core.all_nodes_set.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_master(self.core.admin_public.clone(), - self.core.all_nodes_set.clone(), - new_nodes_set.clone(), - all_set_signature, - new_set_signature), - consensus_transport: ServersSetChangeConsensusTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - migration_id: self.core.migration_id.clone(), - cluster: self.core.cluster.clone(), - }, - })?; - - consensus_session.initialize(self.core.all_nodes_set.clone())?; - - let is_finished = consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - data.consensus_session = Some(consensus_session); - data.new_nodes_set = Some(new_nodes_set); - - // this is the case when all other nodes are isolated - if is_finished { - Self::complete_session(&self.core, &mut *data)?; - } - - Ok(()) - } - - /// Process servers set change message. - pub fn process_message(&self, sender: &NodeId, message: &ServersSetChangeMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &ServersSetChangeMessage::UnknownSessionsRequest(ref message) => - self.on_unknown_sessions_requested(sender, message), - &ServersSetChangeMessage::UnknownSessions(ref message) => - self.on_unknown_sessions(sender, message), - &ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref message) => - self.on_key_version_negotiation(sender, message), - &ServersSetChangeMessage::InitializeShareChangeSession(ref message) => - self.on_initialize_share_change_session(sender, message), - &ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ref message) => - self.on_share_change_session_confirmation(sender, message), - &ServersSetChangeMessage::ServersSetChangeDelegate(ref message) => - self.on_sessions_delegation(sender, message), - &ServersSetChangeMessage::ServersSetChangeDelegateResponse(ref message) => - self.on_delegated_session_completed(sender, message), - &ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref message) => - self.on_share_add_message(sender, message), - &ServersSetChangeMessage::ServersSetChangeError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - &ServersSetChangeMessage::ServersSetChangeCompleted(ref message) => - self.on_session_completed(sender, message), - } - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &ServersSetChangeConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); - } - - // start slave consensus session if needed - if self.core.meta.self_node_id != self.core.meta.master_node_id { - if data.consensus_session.is_none() { - match &message.message { - &ConsensusMessageWithServersSet::InitializeConsensusSession(_) => { - data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(self.core.all_nodes_set.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_slave(self.core.admin_public.clone()), - consensus_transport: ServersSetChangeConsensusTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - migration_id: self.core.migration_id.clone(), - cluster: self.core.cluster.clone(), - }, - })?); - }, - _ => return Err(Error::InvalidStateForRequest), - } - } - } - - // process consensus message - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - let is_establishing_consensus = consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - match &message.message { - &ConsensusMessageWithServersSet::InitializeConsensusSession(ref message) => - consensus_session.on_consensus_partial_request(sender, ServersSetChangeAccessRequest::from(message))?, - &ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ref message) => - consensus_session.on_consensus_partial_response(sender, message.is_confirmed)?, - } - - // when consensus is established => request unknown sessions - let is_consensus_established = consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let unknown_sessions_job = UnknownSessionsJob::new_on_master(self.core.key_storage.clone(), self.core.meta.self_node_id.clone()); - consensus_session.disseminate_jobs(unknown_sessions_job, self.unknown_sessions_transport(), false).map(|_| ()) - } - - /// When unknown sessions are requested. - pub fn on_unknown_sessions_requested(&self, sender: &NodeId, message: &UnknownSessionsRequest) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - let new_nodes_set = { - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - let unknown_sessions_job = UnknownSessionsJob::new_on_slave(self.core.key_storage.clone()); - let unknown_sessions_transport = self.unknown_sessions_transport(); - - // and respond with unknown sessions - consensus_session.on_job_request(&sender, sender.clone(), unknown_sessions_job, unknown_sessions_transport)?; - - consensus_session.consensus_job().executor() - .new_servers_set() - .expect("consensus session is now completed; new_servers_set is intermediate result of consensus session; qed") - .clone() - }; - - // update state - data.state = SessionState::RunningShareChangeSessions; - data.new_nodes_set = Some(new_nodes_set); - - Ok(()) - } - - /// When unknown sessions are received. - pub fn on_unknown_sessions(&self, sender: &NodeId, message: &UnknownSessions) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); - } - - // process message - let unknown_sessions = { - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - consensus_session.on_job_response(sender, message.unknown_sessions.iter().cloned().map(Into::into).collect())?; - if consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } - - // all nodes have reported their unknown sessions - // => we are ready to start adding/moving/removing shares - consensus_session.result()? - }; - - // initialize sessions queue - data.state = SessionState::RunningShareChangeSessions; - data.sessions_queue = Some(SessionsQueue::new(&self.core.key_storage, unknown_sessions.keys().cloned().collect())); - - // and disseminate session initialization requests - Self::disseminate_session_initialization_requests(&self.core, &mut *data) - } - - /// When key version negotiation message is received. - pub fn on_key_version_negotiation(&self, sender: &NodeId, message: &ShareChangeKeyVersionNegotiation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // process message - match &message.message { - &KeyVersionNegotiationMessage::RequestKeyVersions(ref message) if sender == &self.core.meta.master_node_id => { - let key_id = message.session.clone().into(); - let key_share = self.core.key_storage.get(&key_id)?; - let (negotiation_session, _) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { - meta: ShareChangeSessionMeta { - id: key_id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - master_node_id: sender.clone(), - configured_nodes_count: self.core.meta.configured_nodes_count, - connected_nodes_count: self.core.meta.connected_nodes_count, - }, - sub_session: message.sub_session.clone().into(), - key_share: key_share, - result_computer: Arc::new(LargestSupportResultComputer {}), - transport: ServersSetChangeKeyVersionNegotiationTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - cluster: self.core.cluster.clone(), - }, - nonce: message.session_nonce, - }); - negotiation_session.on_key_versions_request(sender, message)?; - debug_assert!(negotiation_session.is_finished()); - Ok(()) - }, - &KeyVersionNegotiationMessage::KeyVersions(ref message) if self.core.meta.self_node_id == self.core.meta.master_node_id => { - let key_id = message.session.clone().into(); - { - let negotiation_session = data.negotiation_sessions.get(&key_id).ok_or(Error::InvalidMessage)?; - negotiation_session.on_key_versions(sender, message)?; - if !negotiation_session.is_finished() { - return Ok(()); - } - } - - // else prepare plan && start share change session - if !Self::initialize_share_change_session(&self.core, &mut *data, key_id)? { - Self::disseminate_session_initialization_requests(&self.core, &mut *data)?; - } - - Ok(()) - }, - _ => Err(Error::InvalidMessage), - } - } - - /// When share change session initialization is requested. - pub fn on_initialize_share_change_session(&self, sender: &NodeId, message: &InitializeShareChangeSession) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests from master node - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // insert new session - let key_id = message.key_id.clone().into(); - match data.active_key_sessions.contains_key(&key_id) { - true => return Err(Error::InvalidMessage), - false => { - let master_plan = ShareChangeSessionPlan { - key_version: message.version.clone().into(), - version_holders: message.version_holders.iter().cloned().map(Into::into).collect(), - consensus_group: message.consensus_group.iter().cloned().map(Into::into).collect(), - new_nodes_map: message.new_nodes_map.iter().map(|(k, v)| (k.clone().into(), v.clone().map(Into::into))).collect(), - }; - - // if master plan is empty, it is cheating - if master_plan.is_empty() { - return Err(Error::InvalidMessage); - } - - // on nodes, holding selected key share version, we could check if master node plan is correct - let master_node_id = message.master_node_id.clone().into(); - if let Some(key_share) = self.core.key_storage.get(&key_id)? { - let version = message.version.clone().into(); - let key_share_owners = message.version_holders.iter().cloned().map(Into::into).collect(); - let new_nodes_set = data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during consensus establishing; change sessions are running after this; qed"); - let local_plan = prepare_share_change_session_plan( - &self.core.all_nodes_set, - key_share.threshold, - &key_id, - version, - &master_node_id, - &key_share_owners, - new_nodes_set)?; - - if local_plan.new_nodes_map.keys().collect::>() != master_plan.new_nodes_map.keys().collect::>() { - return Err(Error::InvalidMessage); - } - } - - let session = Self::create_share_change_session(&self.core, key_id, master_node_id, master_plan)?; - if !session.is_finished() { - data.active_key_sessions.insert(key_id.clone(), session); - } - }, - }; - - // send confirmation - self.core.cluster.send(sender, Message::ServersSetChange(ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ConfirmShareChangeSessionInitialization { - session: message.session.clone(), - session_nonce: message.session_nonce.clone(), - key_id: message.key_id.clone(), - }))) - } - - /// When share change session initialization is confirmed. - pub fn on_share_change_session_confirmation(&self, sender: &NodeId, message: &ConfirmShareChangeSessionInitialization) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests from master node - if self.core.meta.self_node_id != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // add confirmation - let key_id = message.key_id.clone().into(); - let session_master = { - let session_init_data = data.sessions_initialization_state.get_mut(&key_id).ok_or(Error::InvalidMessage)?; - if !session_init_data.confirmations.remove(sender) { - return Err(Error::InvalidMessage); - } - - if !session_init_data.confirmations.is_empty() { - return Ok(()); - } - - session_init_data.master.clone() - }; - - // and start/delegate session if required - data.sessions_initialization_state.remove(&key_id); - if self.core.meta.self_node_id != session_master { - data.delegated_key_sessions.insert(key_id, session_master.clone()); - return self.core.cluster.send(&session_master, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegate(ServersSetChangeDelegate { - session: self.core.meta.id.clone().into(), - session_nonce: self.core.nonce, - key_id: key_id.into(), - }))); - } - - // initialize share change session - { - let key_session = data.active_key_sessions.get_mut(&key_id).ok_or(Error::InvalidMessage)?; - key_session.initialize()?; - if !key_session.is_finished() { - return Ok(()); - } - } - - // complete key session - Self::complete_key_session(&self.core, &mut *data, true, key_id) - } - - /// When sessions execution is delegated to this node. - pub fn on_sessions_delegation(&self, sender: &NodeId, message: &ServersSetChangeDelegate) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests from master node - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // start session - let key_session = data.active_key_sessions.get_mut(&message.key_id.clone().into()).ok_or(Error::InvalidMessage)?; - key_session.initialize() - } - - /// When delegated session execution is completed. - pub fn on_delegated_session_completed(&self, sender: &NodeId, message: &ServersSetChangeDelegateResponse) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests on master node - if self.core.meta.self_node_id != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // forget delegated session - let key_id = message.key_id.clone().into(); - match data.delegated_key_sessions.entry(key_id) { - Entry::Occupied(entry) => if entry.get() == sender { - entry.remove() - } else { - return Err(Error::InvalidMessage); - }, - _ => return Err(Error::InvalidMessage), - }; - - // check if we need to complete the whole change session - Self::disseminate_session_initialization_requests(&self.core, &mut *data) - } - - /// When share add message is received. - pub fn on_share_add_message(&self, sender: &NodeId, message: &ServersSetChangeShareAddMessage) -> Result<(), Error> { - self.on_share_change_message(message.message.session_id().clone().into(), |session| - session.on_share_add_message(sender, &message.message)) - } - - /// When session completion message is received. - pub fn on_session_completed(&self, sender: &NodeId, message: &ServersSetChangeCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - let mut data = self.data.lock(); - data.result = Some(Ok(())); - if data.active_key_sessions.len() != 0 { - return Err(Error::TooEarlyForRequest); - } - - // if we are on the set of nodes that are being removed from the cluster, let's clear database - if !data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during initialization; session is completed after initialization; qed") - .contains(&self.core.meta.self_node_id) { - self.core.key_storage.clear()?; - } - - data.state = SessionState::Finished; - self.core.completed.send(Ok(())); - - Ok(()) - } - - /// Create unknown sessions transport. - fn unknown_sessions_transport(&self) -> UnknownSessionsJobTransport { - UnknownSessionsJobTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - cluster: self.core.cluster.clone(), - } - } - - /// When share change message is received. - fn on_share_change_message Result<(), Error>>(&self, session_id: SessionId, message_processor: F) -> Result<(), Error> { - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // process message - let (is_finished, is_master) = { - let key_session = data.active_key_sessions.get_mut(&session_id).ok_or(Error::InvalidMessage)?; - message_processor(key_session)?; - (key_session.is_finished(), key_session.is_master()) - }; - - if is_finished { - Self::complete_key_session(&self.core, &mut *data, is_master, session_id)?; - } - - Ok(()) - } - - /// Create share change session. - fn create_share_change_session(core: &SessionCore, key_id: SessionId, master_node_id: NodeId, session_plan: ShareChangeSessionPlan) -> Result { - ShareChangeSession::new(ShareChangeSessionParams { - session_id: core.meta.id.clone(), - nonce: core.nonce, - meta: ShareChangeSessionMeta { - id: key_id, - self_node_id: core.meta.self_node_id.clone(), - master_node_id: master_node_id, - configured_nodes_count: core.meta.configured_nodes_count, - connected_nodes_count: core.meta.connected_nodes_count, - }, - cluster: core.cluster.clone(), - key_storage: core.key_storage.clone(), - plan: session_plan, - }) - } - - /// Disseminate session initialization requests. - fn disseminate_session_initialization_requests(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - debug_assert_eq!(core.meta.self_node_id, core.meta.master_node_id); - if data.sessions_queue.is_some() { - let number_of_sessions_active = data.active_key_sessions.len() - + data.delegated_key_sessions.len() - + data.negotiation_sessions.len(); - let mut number_of_sessions_to_start = MAX_ACTIVE_KEY_SESSIONS.saturating_sub(number_of_sessions_active); - while number_of_sessions_to_start > 0 { - let key_id = match data.sessions_queue.as_mut().expect("checked before beginning of the loop; qed").next() { - None => break, // complete session - Some(Err(e)) => return Err(e), - Some(Ok(key_id)) => key_id, - }; - - let key_share = core.key_storage.get(&key_id)?; - let (negotiation_session, _) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { - meta: ShareChangeSessionMeta { - id: key_id, - self_node_id: core.meta.self_node_id.clone(), - master_node_id: core.meta.self_node_id.clone(), - configured_nodes_count: core.meta.configured_nodes_count, - connected_nodes_count: core.meta.connected_nodes_count, - }, - sub_session: math::generate_random_scalar()?, - key_share: key_share, - result_computer: Arc::new(LargestSupportResultComputer {}), // TODO [Opt]: could use modified Fast version - transport: ServersSetChangeKeyVersionNegotiationTransport { - id: core.meta.id.clone(), - nonce: core.nonce, - cluster: core.cluster.clone(), - }, - nonce: 0, - }); - negotiation_session.initialize(core.cluster.nodes())?; - if !negotiation_session.is_finished() { - data.negotiation_sessions.insert(key_id, negotiation_session); - continue; - } - - if !Self::initialize_share_change_session(core, data, key_id)? { - continue; - } - - number_of_sessions_to_start = number_of_sessions_to_start - 1; - } - - // if iteration is not yet finished => return - if number_of_sessions_to_start == 0 { - return Ok(()); - } - } - - // iteration is finished => complete session - if data.state != SessionState::Finished { - data.sessions_queue = None; - if data.active_key_sessions.len() == 0 && - data.delegated_key_sessions.len() == 0 && - data.negotiation_sessions.len() == 0 { - Self::complete_session(core, data)?; - } - } - - Ok(()) - } - - /// Initialize share change session. - fn initialize_share_change_session(core: &SessionCore, data: &mut SessionData, key_id: SessionId) -> Result { - // get selected version && old nodes set from key negotiation session - let negotiation_session = data.negotiation_sessions.remove(&key_id) - .expect("share change session is only initialized when negotiation is completed; qed"); - let (selected_version, selected_master) = negotiation_session - .result() - .expect("share change session is only initialized when negotiation is completed; qed")? - .expect("initialize_share_change_session is only called on share change master; negotiation session completes with some on master; qed"); - let selected_version_holders = negotiation_session.version_holders(&selected_version)?; - let selected_version_threshold = negotiation_session.common_key_data()?.threshold; - - // prepare session change plan && check if something needs to be changed - let old_nodes_set = selected_version_holders; - let new_nodes_set = data.new_nodes_set.as_ref() - .expect("this method is called after consensus estabished; new_nodes_set is a result of consensus session; qed"); - let session_plan = prepare_share_change_session_plan(&core.all_nodes_set, - selected_version_threshold, - &key_id, - selected_version.clone(), - &selected_master, - &old_nodes_set, - new_nodes_set)?; - if session_plan.is_empty() { - return Ok(false); - } - - // send key session initialization requests - let mut confirmations: BTreeSet<_> = session_plan.new_nodes_map.keys().cloned().collect(); - let need_create_session = confirmations.remove(&core.meta.self_node_id); - let initialization_message = Message::ServersSetChange(ServersSetChangeMessage::InitializeShareChangeSession(InitializeShareChangeSession { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - key_id: key_id.clone().into(), - version: selected_version.into(), - version_holders: old_nodes_set.iter().cloned().map(Into::into).collect(), - master_node_id: selected_master.clone().into(), - consensus_group: session_plan.consensus_group.iter().cloned().map(Into::into).collect(), - new_nodes_map: session_plan.new_nodes_map.iter() - .map(|(n, nid)| (n.clone().into(), nid.clone().map(Into::into))) - .collect(), - })); - for node in &confirmations { - core.cluster.send(&node, initialization_message.clone())?; - } - - // create session on this node if required - if need_create_session { - data.active_key_sessions.insert(key_id.clone(), Self::create_share_change_session(core, key_id, - selected_master.clone(), - session_plan)?); - } - - // initialize session if required - let wait_for_confirmations = !confirmations.is_empty(); - if !wait_for_confirmations { - data.active_key_sessions.get_mut(&key_id) - .expect("!wait_for_confirmations is true only if this is the only session participant; if this is session participant, session is created above; qed") - .initialize()?; - } else { - data.sessions_initialization_state.insert(key_id, SessionInitializationData { - master: selected_master, - confirmations: confirmations, - }); - } - - Ok(true) - } - - /// Return delegated session to master. - fn return_delegated_session(core: &SessionCore, key_id: &SessionId) -> Result<(), Error> { - assert!(core.meta.self_node_id != core.meta.master_node_id); - core.cluster.send(&core.meta.master_node_id, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegateResponse(ServersSetChangeDelegateResponse { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - key_id: key_id.clone().into(), - }))) - } - - /// Complete key session. - fn complete_key_session(core: &SessionCore, data: &mut SessionData, is_master: bool, session_id: SessionId) -> Result<(), Error> { - data.active_key_sessions.remove(&session_id); - let is_general_master = core.meta.self_node_id == core.meta.master_node_id; - if is_master && !is_general_master { - Self::return_delegated_session(core, &session_id)?; - } - if is_general_master { - Self::disseminate_session_initialization_requests(core, data)?; - } - - if data.result.is_some() && data.active_key_sessions.len() == 0 { - data.state = SessionState::Finished; - core.completed.send(Ok(())); - } - - Ok(()) - } - - /// Complete servers set change session. - fn complete_session(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - debug_assert_eq!(core.meta.self_node_id, core.meta.master_node_id); - - // send completion notification - core.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(ServersSetChangeCompleted { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - })))?; - - // if we are on the set of nodes that are being removed from the cluster, let's clear database - if !data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during initialization; session is completed after initialization; qed") - .contains(&core.meta.self_node_id) { - core.key_storage.clear()?; - } - - data.state = SessionState::Finished; - data.result = Some(Ok(())); - core.completed.send(Ok(())); - - Ok(()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionId; - type CreationData = (); // never used directly - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "servers set change" - } - - fn id(&self) -> SessionId { - self.core.meta.id.clone() - } - - fn is_finished(&self) -> bool { - self.data.lock().state == SessionState::Finished - } - - fn on_session_timeout(&self) { - self.on_session_error(&self.core.meta.self_node_id, Error::NodeDisconnected); - } - - fn on_node_timeout(&self, node: &NodeId) { - self.on_session_error(node, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in generation session is considered fatal - // => broadcast error if error occured on this node - if *node == self.core.meta.self_node_id { - // do not bother processing send error, as we already processing error - let _ = self.core.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(ServersSetChangeError { - session: self.core.meta.id.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - }))); - } - - let mut data = self.data.lock(); - - warn!(target: "secretstore_net", "{}: servers set change session failed: {} on {}", - self.core.meta.self_node_id, error, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error.clone())); - self.core.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::ServersSetChange(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl JobTransport for ServersSetChangeConsensusTransport { - type PartialJobRequest=ServersSetChangeAccessRequest; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: ServersSetChangeAccessRequest) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ServersSetChangeConsensusMessage { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageWithServersSet::InitializeConsensusSession(InitializeConsensusSessionWithServersSet { - migration_id: self.migration_id.clone().map(Into::into), - old_nodes_set: request.old_servers_set.into_iter().map(Into::into).collect(), - new_nodes_set: request.new_servers_set.into_iter().map(Into::into).collect(), - old_set_signature: request.old_set_signature.into(), - new_set_signature: request.new_set_signature.into(), - }), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ServersSetChangeConsensusMessage { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }), - }))) - } -} - -impl JobTransport for UnknownSessionsJobTransport { - type PartialJobRequest=NodeId; - type PartialJobResponse=BTreeSet; - - fn send_partial_request(&self, node: &NodeId, _request: NodeId) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(UnknownSessionsRequest { - session: self.id.clone().into(), - session_nonce: self.nonce, - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: BTreeSet) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::UnknownSessions(UnknownSessions { - session: self.id.clone().into(), - session_nonce: self.nonce, - unknown_sessions: response.into_iter().map(Into::into).collect(), - }))) - } -} - -impl KeyVersionNegotiationTransport for ServersSetChangeKeyVersionNegotiationTransport { - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: message, - }))) - } - - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: message, - }))) - } -} - -fn check_nodes_set(all_nodes_set: &BTreeSet, new_nodes_set: &BTreeSet) -> Result<(), Error> { - // all_nodes_set is the set of nodes we're currently connected to (and configured for) - match new_nodes_set.iter().any(|n| !all_nodes_set.contains(n)) { - true => Err(Error::NodeDisconnected), - false => Ok(()) - } -} - -#[cfg(test)] -pub mod tests { - use std::sync::Arc; - use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use ethereum_types::H256; - use crypto::publickey::{Random, Generator, Public, Signature, KeyPair, sign}; - use blockchain::SigningKeyPair; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, PlainNodeKeyPair}; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; - use key_server_cluster::generation_session::tests::{MessageLoop as GenerationMessageLoop}; - use key_server_cluster::math; - use key_server_cluster::message::Message; - use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - use key_server_cluster::jobs::servers_set_change_access_job::ordered_nodes_hash; - use super::{SessionImpl, SessionParams}; - - pub trait AdminSessionAdapter { - const SIGN_NEW_NODES: bool; - - fn create( - meta: ShareChangeSessionMeta, - admin_public: Public, - all_nodes_set: BTreeSet, - ml: &ClusterMessageLoop, - idx: usize - ) -> S; - } - - pub struct MessageLoop { - pub ml: ClusterMessageLoop, - pub admin_key_pair: KeyPair, - pub original_key_pair: KeyPair, - pub original_key_version: H256, - pub all_nodes_set: BTreeSet, - pub new_nodes_set: BTreeSet, - pub all_set_signature: Signature, - pub new_set_signature: Signature, - pub sessions: BTreeMap, - pub queue: VecDeque<(NodeId, NodeId, Message)>, - } - - impl ::std::fmt::Debug for MessageLoop { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{:?}", self.ml) - } - } - - struct Adapter; - - impl AdminSessionAdapter for Adapter { - const SIGN_NEW_NODES: bool = true; - - fn create( - mut meta: ShareChangeSessionMeta, - admin_public: Public, - all_nodes_set: BTreeSet, - ml: &ClusterMessageLoop, - idx: usize - ) -> SessionImpl { - meta.self_node_id = *ml.node_key_pair(idx).public(); - SessionImpl::new(SessionParams { - meta: meta, - all_nodes_set: all_nodes_set, - cluster: ml.cluster(idx).view().unwrap(), - key_storage: ml.key_storage(idx).clone(), - nonce: 1, - admin_public: admin_public, - migration_id: None, - }).unwrap().0 - } - } - - impl MessageLoop { - pub fn with_gml>( - gml: GenerationMessageLoop, - master: NodeId, - add: Option>, - removed_nodes_ids: Option>, - isolated_nodes_ids: Option>, - ) -> Self { - // read generated key data - let original_key_pair = gml.compute_key_pair(); - let original_key_version = gml.key_version(); - Self::with_ml::( - gml.0, - original_key_pair, - original_key_version, - master, - add, - removed_nodes_ids, - isolated_nodes_ids) - } - - pub fn and_then>( - self, - master: NodeId, - add: Option>, - removed_nodes_ids: Option>, - isolated_nodes_ids: Option>, - ) -> Self { - Self::with_ml::( - self.ml, - self.original_key_pair, - self.original_key_version, - master, - add, - removed_nodes_ids, - isolated_nodes_ids, - ) - } - - pub fn with_ml>( - mut ml: ClusterMessageLoop, - original_key_pair: KeyPair, - original_key_version: H256, - master: NodeId, - add: Option>, - removed_nodes_ids: Option>, - isolated_nodes_ids: Option>, - ) -> Self { - let add = add.unwrap_or_default(); - let removed_nodes_ids = removed_nodes_ids.unwrap_or_default(); - let isolated_nodes_ids = isolated_nodes_ids.unwrap_or_default(); - - // generate admin key pair - let admin_key_pair = Random.generate(); - let admin_public = admin_key_pair.public().clone(); - - // all active nodes set - let mut all_nodes_set: BTreeSet<_> = ml.nodes().into_iter() - .filter(|n| !isolated_nodes_ids.contains(n)) - .collect(); - // new nodes set includes all old nodes, except nodes being removed + all nodes being added - let new_nodes_set: BTreeSet = all_nodes_set.iter().cloned() - .chain(add.iter().map(|kp| *kp.public())) - .filter(|n| !removed_nodes_ids.contains(n)) - .collect(); - let mut old_set_to_sign = all_nodes_set.clone(); - all_nodes_set.extend(add.iter().map(|kp| *kp.public())); - if C::SIGN_NEW_NODES { - old_set_to_sign.extend(add.iter().map(|kp| *kp.public())); - } - for isolated_node_id in &isolated_nodes_ids { - all_nodes_set.remove(isolated_node_id); - } - - let meta = ShareChangeSessionMeta { - self_node_id: master, - master_node_id: master, - id: SessionId::from([1u8; 32]), - configured_nodes_count: all_nodes_set.len(), - connected_nodes_count: all_nodes_set.len(), - }; - - // include new nodes in the cluster - for node_key_pair in &add { - ml.include(Arc::new(PlainNodeKeyPair::new(node_key_pair.clone()))); - } - // isolate nodes from the cluster - for isolated_node_id in &isolated_nodes_ids { - let idx = ml.nodes().iter().position(|n| n == isolated_node_id).unwrap(); - ml.exclude(idx); - } - - // prepare set of nodes - let sessions: BTreeMap<_, _> = (0..ml.nodes().len()) - .map(|idx| (ml.node(idx), C::create(meta.clone(), admin_public, all_nodes_set.clone(), &ml, idx))) - .collect(); - - let all_set_signature = sign(admin_key_pair.secret(), &ordered_nodes_hash(&old_set_to_sign)).unwrap(); - let new_set_signature = sign(admin_key_pair.secret(), &ordered_nodes_hash(&new_nodes_set)).unwrap(); - - MessageLoop { - ml, - admin_key_pair: admin_key_pair, - original_key_pair, - original_key_version, - all_nodes_set: all_nodes_set.clone(), - new_nodes_set: new_nodes_set, - all_set_signature: all_set_signature, - new_set_signature: new_set_signature, - sessions, - queue: Default::default(), - } - } - - pub fn run(&mut self) { - // run session until completion - while let Some((from, to, message)) = self.take_message() { - self.process_message((from, to, message)).unwrap(); - } - - // check that all sessions have finished - assert!(self.sessions.values().all(|s| s.is_finished())); - } - - pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { - self.ml.take_message().or_else(|| self.queue.pop_front()) - } - - pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { - match self.sessions[&msg.1].on_message(&msg.0, &msg.2) { - Ok(_) => Ok(()), - Err(Error::TooEarlyForRequest) => { - self.queue.push_back(msg); - Ok(()) - }, - Err(err) => Err(err), - } - } - - /// This only works for schemes where threshold = 1 - pub fn check_secret_is_preserved<'a, I: IntoIterator>(&self, nodes: I) { - let nodes: Vec<_> = nodes.into_iter().collect(); - let key_storages: Vec<_> = nodes.iter().map(|n| self.ml.key_storage_of(n)).collect(); - let n = nodes.len(); - let document_secret_plain = math::generate_random_point().unwrap(); - for n1 in 0..n { - for n2 in n1+1..n { - let share1 = key_storages[n1].get(&SessionId::from([1u8; 32])).unwrap(); - let share2 = key_storages[n2].get(&SessionId::from([1u8; 32])).unwrap(); - - let id_number1 = share1.as_ref().unwrap().last_version().unwrap().id_numbers[nodes[n1]].clone(); - let id_number2 = share1.as_ref().unwrap().last_version().unwrap().id_numbers[nodes[n2]].clone(); - // now encrypt and decrypt data - let (document_secret_decrypted, document_secret_decrypted_test) = - math::tests::do_encryption_and_decryption(1, - self.original_key_pair.public(), - &[id_number1, id_number2], - &[share1.unwrap().last_version().unwrap().secret_share.clone(), - share2.unwrap().last_version().unwrap().secret_share.clone()], - Some(self.original_key_pair.secret()), - document_secret_plain.clone()); - - assert_eq!(document_secret_plain, document_secret_decrypted_test); - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - } - } - - impl MessageLoop { - pub fn run_at(mut self, master: NodeId) -> Self { - self.sessions[&master].initialize( - self.new_nodes_set.clone(), - self.all_set_signature.clone(), - self.new_set_signature.clone()).unwrap(); - self.run(); - self - } - } - - pub fn generate_key(num_nodes: usize, threshold: usize) -> GenerationMessageLoop { - let gml = GenerationMessageLoop::new(num_nodes).init(threshold).unwrap(); - gml.0.loop_until(|| gml.0.is_empty()); - gml - } - - #[test] - fn node_added_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // add 1 node so that it becames 2-of-4 session - let add = vec![Random.generate()]; - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys()); - } - - #[test] - fn node_added_using_server_set_change_from_this_node() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // insert 1 node so that it becames 2-of-4 session - // master node is the node we are adding => - // 1) add session is delegated to one of old nodes - // 2) key share is pushed to new node - // 3) delegated session is returned back to added node - let add = vec![Random.generate()]; - let master = add[0].public().clone(); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys()); - } - - #[test] - fn node_moved_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 1 node && insert 1 node so that one share is moved - let master = gml.0.node(0); - let remove: BTreeSet<_> = ::std::iter::once(gml.0.node(1)).collect(); - let add = vec![Random.generate()]; - let ml = MessageLoop::with_gml::(gml, master, Some(add), Some(remove.clone()), None).run_at(master); - - // check that secret is still the same as before moving the share - ml.check_secret_is_preserved(ml.sessions.keys() - .filter(|k| !remove.contains(k))); - - // check that all removed nodes do not own key share - assert!(ml.sessions.keys().filter(|k| remove.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::from([1u8; 32])).unwrap().is_none())); - } - - #[test] - fn node_removed_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 1 node so that session becames 2-of-2 - let remove: BTreeSet<_> = ::std::iter::once(gml.0.node(0)).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, None, Some(remove.clone()), None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys() - .filter(|k| !remove.contains(k))); - - // check that all removed nodes do not own key share - assert!(ml.sessions.keys().filter(|k| remove.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::from([1u8; 32])).unwrap().is_none())); - } - - #[test] - fn isolated_node_removed_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 1 node so that session becames 2-of-2 - let isolate: BTreeSet<_> = ::std::iter::once(gml.0.node(1)).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, None, None, Some(isolate.clone())) - .run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys() - .filter(|k| !isolate.contains(k))); - - // check that all isolated nodes still OWN key share - assert!(ml.sessions.keys().filter(|k| isolate.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::from([1u8; 32])).unwrap().is_some())); - } - - #[test] - fn having_less_than_required_nodes_after_change_does_not_fail_change_session() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 2 nodes so that key becomes irrecoverable (make sure the session is completed - // even though key is irrecoverable) - let remove: BTreeSet<_> = gml.0.nodes().into_iter().skip(1).take(2).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, None, Some(remove.clone()), None).run_at(master); - - // check that all removed nodes do not own key share - assert!(ml.sessions.keys().filter(|k| remove.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::from([1u8; 32])).unwrap().is_none())); - - // and now let's add new node (make sure the session is completed, even though key is still irrecoverable) - // isolated here are not actually isolated, but removed on the previous step - let add = vec![Random.generate()]; - let master = add[0].public().clone(); - let ml = ml.and_then::(master, Some(add.clone()), None, Some(remove)).run_at(master); - - // check that all added nodes do not own key share (there's not enough nodes to run share add session) - assert!(ml.sessions.keys().filter(|k| add.iter().any(|n| n.public() == *k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::from([1u8; 32])).unwrap().is_none())); - } - - #[test] - fn removing_node_from_cluster_of_2_works() { - // initial 2-of-2 session - let gml = generate_key(2, 1); - - // make 2nd node isolated so that key becomes irrecoverable (make sure the session is completed, - // even though key is irrecoverable) - let isolate: BTreeSet<_> = gml.0.nodes().into_iter().skip(1).take(1).collect(); - let master = gml.0.node(0); - MessageLoop::with_gml::(gml, master, None, None, Some(isolate)).run_at(master); - } - - #[test] - fn adding_node_that_has_lost_its_database_works() { - // initial 2-of-2 session - let gml = generate_key(2, 1); - - // insert 1 node so that it becames 2-of-3 session - let add = vec![Random.generate()]; - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, Some(add.clone()), None, None) - .run_at(master); - - // now let's say new node has lost its db and we're trying to join it again - ml.ml.key_storage_of(add[0].public()).clear().unwrap(); - - // this time old nodes have version, where new node is mentioned, but it doesn't report it when negotiating - let ml = ml.and_then::(master, Some(add), None, None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys()); - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/sessions_queue.rs b/secret-store/src/key_server_cluster/admin_sessions/sessions_queue.rs deleted file mode 100644 index 448f5bdd8..000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/sessions_queue.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{VecDeque, BTreeSet}; -use key_server_cluster::{Error, SessionId, KeyStorage}; - -/// Queue of share change sessions. -pub struct SessionsQueue { - /// Sessions, known on this node. - known_sessions: VecDeque, - /// Unknown sessions. - unknown_sessions: VecDeque, -} - -impl SessionsQueue { - /// Create new sessions queue. - pub fn new(key_storage: &Arc, unknown_sessions: BTreeSet) -> Self { - // TODO [Opt]: - // 1) known sessions - change to iter - // 2) unknown sesions - request chunk-by-chunk - SessionsQueue { - known_sessions: key_storage.iter().map(|(k, _)| k).collect(), - unknown_sessions: unknown_sessions.into_iter().collect(), - } - } -} - -impl Iterator for SessionsQueue { - type Item = Result; - - fn next(&mut self) -> Option { - if let Some(known_session) = self.known_sessions.pop_front() { - return Some(Ok(known_session)); - } - - if let Some(unknown_session) = self.unknown_sessions.pop_front() { - return Some(Ok(unknown_session)); - } - - None - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs b/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs deleted file mode 100644 index ad67fe057..000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs +++ /dev/null @@ -1,1114 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::{H256, Address}; -use crypto::publickey::{Public, Secret, Signature}; -use futures::Oneshot; -use parking_lot::Mutex; -use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare, DocumentKeyShareVersion, KeyStorage}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::math; -use key_server_cluster::message::{Message, ShareAddMessage, ShareAddConsensusMessage, ConsensusMessageOfShareAdd, - InitializeConsensusSessionOfShareAdd, KeyShareCommon, NewKeysDissemination, ShareAddError, - ConfirmConsensusInitialization, CommonKeyData}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::dummy_job::{DummyJob, DummyJobTransport}; -use key_server_cluster::jobs::servers_set_change_access_job::{ServersSetChangeAccessJob, ServersSetChangeAccessRequest}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Share addition session transport. -pub trait SessionTransport: Clone + JobTransport { - /// Get all connected nodes. Since ShareAdd session requires all cluster nodes to be connected, this set equals to all known cluster nodes set. - fn nodes(&self) -> BTreeSet; - /// Send message to given node. - fn send(&self, node: &NodeId, message: ShareAddMessage) -> Result<(), Error>; - /// Set data for master node (sent to slave nodes in consensus session initialization message). - fn set_master_data(&mut self, consensus_group: BTreeSet, version_holders: BTreeSet, id_numbers: BTreeMap>); -} - -/// Share addition session. -/// Based on "Efficient Multi-Party Digital Signature using Adaptive Secret Sharing for Low-Power Devices in Wireless Networks" paper: -/// http://www.wu.ece.ufl.edu/mypapers/msig.pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for shares addition the message) asks all other nodes to support addition -/// 2) key refreshing distribution (KRD): node generates new random polynom && sends required data to all other nodes -/// 3) key refreshing verification (KRV): node verifies received data -/// 4) node updates its own key share using generated (&& received) data -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex>, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: ShareChangeSessionMeta, - /// Session-level nonce. - pub nonce: u64, - /// Original key share (for old nodes only). - pub key_share: Option, - /// Session transport to communicate to other cluster nodes. - pub transport: T, - /// Key storage. - pub key_storage: Arc, - /// Administrator public key. - pub admin_public: Option, - /// Session completion signal. - pub completed: CompletionSignal<()>, -} - -/// Share add consensus session type. -type ShareAddChangeConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Key version to use for decryption. - pub version: Option, - /// Consensus session. - pub consensus_session: Option>, - /// Holders of key version. - pub version_holders: Option>, - /// NewKeyShare (for nodes being added). - pub new_key_share: Option, - /// Nodes id numbers. - pub id_numbers: Option>>, - /// Secret subshares received from nodes. - pub secret_subshares: Option>>, - /// Share add change result. - pub result: Option>, -} - -/// New key share. -struct NewKeyShare { - /// NewKeyShare: threshold. - pub threshold: usize, - /// NewKeyShare: author. - pub author: Address, - /// NewKeyShare: joint public. - pub joint_public: Public, - /// NewKeyShare: Common (shared) encryption point. - pub common_point: Option, - /// NewKeyShare: Encrypted point. - pub encrypted_point: Option, -} - -/// Session state. -#[derive(Debug, PartialEq)] -enum SessionState { - /// State when consensus is establishing. - ConsensusEstablishing, - /// Waiting for keys dissemination. - WaitingForKeysDissemination, - /// Session is completed. - Finished, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: ShareChangeSessionMeta, - /// Session transport. - pub transport: T, - /// Key storage. - pub key_storage: Arc, - /// Administrator public key. - pub admin_public: Option, - /// Session nonce. - pub nonce: u64, -} - -/// Isolated ShareAdd session transport. -#[derive(Clone)] -pub struct IsolatedSessionTransport { - /// Key id. - session: SessionId, - /// Key version. - version: Option, - /// Session-level nonce. - nonce: u64, - /// Holders of key version. - version_holders: Option>, - /// Consensus group. - consensus_group: Option>, - /// Id numbers of all new nodes. - id_numbers: Option>>, - /// Cluster. - cluster: Arc, -} - -impl SessionImpl where T: SessionTransport { - /// Create new share addition session. - pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { - let key_share = params.key_storage.get(¶ms.meta.id)?; - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - nonce: params.nonce, - key_share: key_share, - transport: params.transport, - key_storage: params.key_storage, - admin_public: params.admin_public, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::ConsensusEstablishing, - version: None, - consensus_session: None, - version_holders: None, - new_key_share: None, - id_numbers: None, - secret_subshares: None, - result: None, - }), - }, oneshot)) - } - - /// Set pre-established consensus data. - pub fn set_consensus_output(&self, version: &H256, consensus_group: BTreeSet, version_holders: BTreeSet, mut new_nodes_map: BTreeMap>) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::ConsensusEstablishing || data.consensus_session.is_some() || data.id_numbers.is_some() || data.secret_subshares.is_some() { - return Err(Error::InvalidStateForRequest); - } - - // key share version is required on ShareAdd master node - if let Some(key_share) = self.core.key_share.as_ref() { - if let Ok(key_version) = key_share.version(version) { - let non_isolated_nodes = self.core.transport.nodes(); - for (node, id_number) in &key_version.id_numbers { - { - let external_id_number = new_nodes_map.get(node); - match external_id_number { - Some(&Some(ref external_id_number)) => { - if !version_holders.contains(node) { - // possible when joining version holder, that has lost its database - // and haven't reported version ownership - continue; - } - if external_id_number == id_number { - continue; - } - - return Err(Error::ConsensusUnreachable); - }, - Some(&None) => (), - None => { - if non_isolated_nodes.contains(node) { - return Err(Error::ConsensusUnreachable) - } - continue; - }, - } - } - - new_nodes_map.insert(node.clone(), Some(id_number.clone())); - } - - // check that all id_numbers are filled - if new_nodes_map.values().any(Option::is_none) { - return Err(Error::ConsensusUnreachable); - } - } - } - - // check passed consensus data - Self::check_nodes_map(&self.core, version, &consensus_group, &version_holders, &new_nodes_map)?; - - // update data - data.version = Some(version.clone()); - data.id_numbers = Some(new_nodes_map); - data.secret_subshares = Some(consensus_group.into_iter() - .map(|n| (n, None)) - .collect()); - data.version_holders = Some(version_holders); - - Ok(()) - } - - /// Initialize share add session on master node. - pub fn initialize(&self, version: Option, new_nodes_set: Option>, old_set_signature: Option, new_set_signature: Option) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::ConsensusEstablishing || data.consensus_session.is_some() { - return Err(Error::InvalidStateForRequest); - } - - // if consensus is pre-established => start sending ShareAdd-specific messages - let is_consensus_pre_established = data.id_numbers.is_some(); - if is_consensus_pre_established { - return Self::on_consensus_established(&self.core, &mut *data); - } - - // else => prepare to start consensus session - // require all initialization params for consensus session - let version = version.ok_or(Error::InvalidMessage)?; - let old_set_signature = old_set_signature.ok_or(Error::InvalidMessage)?; - let new_set_signature = new_set_signature.ok_or(Error::InvalidMessage)?; - let new_nodes_set = new_nodes_set.ok_or(Error::InvalidMessage)?; - let admin_public = self.core.admin_public.as_ref().cloned().ok_or(Error::ConsensusUnreachable)?; - - // key share version is required on ShareAdd master node - let key_share = self.core.key_share.as_ref().ok_or_else(|| Error::ServerKeyIsNotFound)?; - let key_version = key_share.version(&version)?; - - // old nodes set is all non-isolated owners of version holders - let non_isolated_nodes = self.core.transport.nodes(); - let old_nodes_set: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(n)) - .cloned() - .collect(); - - // new nodes map contains previous id_numbers for old nodes && random number for new nodes - let mut new_nodes_map = BTreeMap::new(); - for new_node in new_nodes_set.into_iter().filter(|n| non_isolated_nodes.contains(n)) { - new_nodes_map.insert(new_node, match key_version.id_numbers.get(&new_node) { - Some(old_id_number) => Some(old_id_number.clone()), - None => Some(math::generate_random_scalar()?), - }); - } - - // let's select consensus group - let consensus_group: BTreeSet<_> = ::std::iter::once(self.core.meta.self_node_id.clone()) - .chain(old_nodes_set.iter() - .filter(|n| **n != self.core.meta.self_node_id && non_isolated_nodes.contains(*n)) - .take(key_share.threshold) - .cloned()) - .collect(); - let version_holders = &old_nodes_set; - - // now check nodes map - Self::check_nodes_map(&self.core, &version, &consensus_group, version_holders, &new_nodes_map)?; - - // prepare consensus session transport - let mut consensus_transport = self.core.transport.clone(); - consensus_transport.set_master_data(consensus_group.clone(), version_holders.clone(), new_nodes_map.clone()); - - // create && initialize consensus session - let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(new_nodes_map.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_master(admin_public, - old_nodes_set.clone(), - new_nodes_map.keys().cloned().collect(), - old_set_signature, - new_set_signature), - consensus_transport: consensus_transport, - })?; - - consensus_session.initialize(new_nodes_map.keys().cloned().collect())?; - - // update data - data.version = Some(version); - data.consensus_session = Some(consensus_session); - data.id_numbers = Some(new_nodes_map); - data.secret_subshares = Some(consensus_group.into_iter().map(|n| (n, None)).collect()); - data.version_holders = Some(version_holders.clone()); - - Ok(()) - } - - /// Process single message. - pub fn process_message(&self, sender: &NodeId, message: &ShareAddMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &ShareAddMessage::ShareAddConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &ShareAddMessage::KeyShareCommon(ref message) => - self.on_common_key_share_data(sender, message), - &ShareAddMessage::NewKeysDissemination(ref message) => - self.on_new_keys_dissemination(sender, message), - &ShareAddMessage::ShareAddError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - } - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &ShareAddConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // start slave consensus session if needed - let mut data = self.data.lock(); - match &message.message { - &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) - if data.consensus_session.is_none() && sender == &self.core.meta.master_node_id => { - let admin_public = self.core.admin_public.as_ref().cloned().ok_or(Error::ConsensusUnreachable)?; - data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(message.new_nodes_map.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_slave(admin_public), - consensus_transport: self.core.transport.clone(), - })?); - }, - _ => (), - }; - - // process consensus message - let (is_establishing_consensus, is_consensus_established, version, new_nodes_map, consensus_group, version_holders) = { - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - let is_establishing_consensus = consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - - let (version, new_nodes_map, consensus_group, version_holders) = match &message.message { - &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) => { - consensus_session.on_consensus_partial_request(sender, ServersSetChangeAccessRequest::from(message))?; - - let version = message.version.clone().into(); - let consensus_group = message.consensus_group.iter().cloned().map(Into::into).collect(); - let version_holders = message.version_holders.iter().cloned().map(Into::into).collect(); - let new_nodes_map: BTreeMap<_, _> = message.new_nodes_map.iter() - .map(|(n, nn)| (n.clone().into(), Some(nn.clone().into()))) - .collect(); - - // check that all id_numbers are filled - if new_nodes_map.values().any(Option::is_none) { - return Err(Error::ConsensusUnreachable); - } - - // check old set of nodes - Self::check_nodes_map(&self.core, &version, &consensus_group, &version_holders, &new_nodes_map)?; - - (Some(version), Some(new_nodes_map), Some(consensus_group), Some(version_holders)) - }, - &ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ref message) => { - consensus_session.on_consensus_partial_response(sender, message.is_confirmed)?; - (None, None, None, None) - }, - }; - - ( - is_establishing_consensus, - consensus_session.state() == ConsensusSessionState::ConsensusEstablished, - version, - new_nodes_map, - consensus_group, - version_holders, - ) - }; - - // update data - if let Some(version) = version { - data.version = Some(version); - } - if let Some(new_nodes_map) = new_nodes_map { - data.id_numbers = Some(new_nodes_map); - } - if let Some(consensus_group) = consensus_group { - data.secret_subshares = Some(consensus_group.into_iter().map(|n| (n, None)).collect()); - } - if let Some(version_holders) = version_holders { - data.version_holders = Some(version_holders); - } - - // if consensus is stablished, proceed - if !is_establishing_consensus || !is_consensus_established || self.core.meta.self_node_id != self.core.meta.master_node_id { - return Ok(()); - } - - Self::on_consensus_established(&self.core, &mut *data) - } - - /// When common key share data is received by new node. - pub fn on_common_key_share_data(&self, sender: &NodeId, message: &KeyShareCommon) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // only master can send this message - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::ConsensusEstablishing || data.id_numbers.is_none() { - return Ok(()); - } - - // we only expect this message once - if data.new_key_share.is_some() { - return Err(Error::InvalidStateForRequest); - } - - // check if we actually waiting for this message - { - let version = data.version.as_ref().ok_or(Error::InvalidStateForRequest)?; - let key_version = self.core.key_share.as_ref().and_then(|ks| ks.version(version).ok()); - if key_version.is_some() { - return Ok(()); - } - } - - // update data - data.state = SessionState::WaitingForKeysDissemination; - data.new_key_share = Some(NewKeyShare { - threshold: message.key_common.threshold, - author: message.key_common.author.clone().into(), - joint_public: message.key_common.public.clone().into(), - common_point: message.common_point.clone().map(Into::into), - encrypted_point: message.encrypted_point.clone().map(Into::into), - }); - - let id_numbers = data.id_numbers.as_mut() - .expect("common key share data is expected after initialization; id_numbers are filled during initialization; qed"); - for (node, id_number) in &message.id_numbers { - let id_number: Secret = id_number.clone().into(); - { - let local_id_number = id_numbers.get(&node.clone().into()); - match local_id_number { - Some(&Some(ref local_id_number)) => { - if *local_id_number == id_number { - continue; - } - - return Err(Error::ConsensusUnreachable); - }, - Some(&None) => (), - None => continue, // can happen for isolated nodes - } - } - - id_numbers.insert(node.clone().into(), Some(id_number)); - } - - Ok(()) - } - - /// When keys dissemination message is received. - pub fn on_new_keys_dissemination(&self, sender: &NodeId, message: &NewKeysDissemination) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - // check state - if data.state == SessionState::ConsensusEstablishing && data.secret_subshares.is_some() { - data.state = SessionState::WaitingForKeysDissemination; - } else if data.state != SessionState::WaitingForKeysDissemination { - return Err(Error::InvalidStateForRequest); - } - - // update data - let explanation = "secret_subshares is filled during initialization; keys are disseminated after initialization; qed"; - { - match data.secret_subshares.as_ref().expect(explanation).get(sender) { - None => return Err(Error::InvalidMessage), - Some(&Some(_)) => return Err(Error::InvalidMessage), - Some(&None) => (), - }; - - let secret_subshare = Self::compute_secret_subshare(&self.core, &mut *data, sender, &message.secret_subshare.clone().into())?; - *data.secret_subshares.as_mut().expect(explanation) - .get_mut(sender) - .expect("checked couple of lines above; qed") = Some(secret_subshare); - } - - // if we have received subshare from master node, it means that we should start dissemination - if sender == &self.core.meta.master_node_id { - Self::on_consensus_established(&self.core, &mut *data)?; - } - - // check if shares from all nodes are received - if data.secret_subshares.as_ref().expect(explanation).values().any(|v| v.is_none()) { - return Ok(()) - } - - // TODO [Trust]: find a way to verificate keys - Self::complete_session(&self.core, &mut *data) - } - - /// Check nodes map. - fn check_nodes_map(core: &SessionCore, version: &H256, consensus_group: &BTreeSet, version_holders: &BTreeSet, new_nodes_map: &BTreeMap>) -> Result<(), Error> { - // check if this node has given version - let has_this_version = match core.key_share.as_ref() { - Some(key_share) => key_share.version(version).is_ok(), - None => false, - }; - - // check && update passed data - match has_this_version { - true => { - // check if version exists - let explanation = "has_this_version is true; it is true if we have given version of the key; qed"; - let key_share = core.key_share.as_ref().expect(explanation); - let key_version = key_share.version(version).expect(explanation); - - // there must be exactly thresold + 1 nodes in consensus group - if consensus_group.len() != key_share.threshold + 1 { - return Err(Error::ConsensusUnreachable); - } - - // every non-isolated node must be a part of new_nodes_set - let non_isolated_nodes = core.transport.nodes(); - if key_version.id_numbers.keys().any(|n| non_isolated_nodes.contains(n) && !new_nodes_map.contains_key(n)) { - return Err(Error::ConsensusUnreachable); - } - - // there must be at least one new node in new_nodes_map - if key_version.id_numbers.keys().filter(|n| non_isolated_nodes.contains(n) && version_holders.contains(n)).count() >= new_nodes_map.len() { - return Err(Error::ConsensusUnreachable); - } - }, - false => { - // if we do not have a share, we should not be a part of consenus group - // but we must be on new nodes set, since this is a ShareAdd session - if consensus_group.contains(&core.meta.self_node_id) || - !new_nodes_map.contains_key(&core.meta.self_node_id) { - return Err(Error::ConsensusUnreachable); - } - }, - } - - // master node must always be a part of consensus group - if !consensus_group.contains(&core.meta.master_node_id) { - return Err(Error::ConsensusUnreachable); - } - - // master node must always be a part of new_nodes_map - if !new_nodes_map.contains_key(&core.meta.master_node_id) { - return Err(Error::ConsensusUnreachable); - } - - Ok(()) - } - - /// Start sending ShareAdd-specific messages, when consensus is established. - fn on_consensus_established(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - // update state - data.state = SessionState::WaitingForKeysDissemination; - - // if we're not a part of consensus group, wait for secret subshares - let explanation = "secret_subshares is a result of consensus job; consensus is established; qed"; - let is_consensus_group_node = data.secret_subshares.as_ref().expect(explanation).contains_key(&core.meta.self_node_id); - if !is_consensus_group_node { - return Ok(()); - } - - // else if master => send shared data to every new node - if core.meta.self_node_id == core.meta.master_node_id { - Self::disseminate_common_share_data(core, data)?; - } - - // ...and then disseminate keys - Self::disseminate_keys(core, data)?; - - // ..and check if session could be completed - if data.secret_subshares.as_ref().expect(explanation).values().any(|v| v.is_none()) { - return Ok(()) - } - - // TODO [Trust]: find a way to verificate keys - Self::complete_session(core, data) - } - - /// Send common share data to evey new node. - fn disseminate_common_share_data(core: &SessionCore, data: &SessionData) -> Result<(), Error> { - let explanation = "disseminate_common_share_data is only called on master node; master node has specified version of the key; qed"; - let old_key_share = core.key_share.as_ref().expect(explanation); - let old_key_version = old_key_share.version(data.version.as_ref().expect(explanation)).expect(explanation); - let version_holders = data.version_holders.as_ref() - .expect("disseminate_common_share_data is only called on master node; version holders is created during initialization on master node; qed"); - let consensus_group = data.secret_subshares.as_ref() - .expect("disseminate_common_share_data is only called on master node; consensus group is created during initialization on master node; qed"); - let nodes = data.id_numbers.as_ref() - .expect("nodes are filled during consensus establishing; common share data sent after consensus is established; qed") - .keys() - .filter(|n| !consensus_group.contains_key(n)); - for new_node in nodes { - core.transport.send(new_node, ShareAddMessage::KeyShareCommon(KeyShareCommon { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - key_common: CommonKeyData { - threshold: old_key_share.threshold, - author: old_key_share.author.into(), - public: old_key_share.public.into(), - }, - common_point: old_key_share.common_point.clone().map(Into::into), - encrypted_point: old_key_share.encrypted_point.clone().map(Into::into), - id_numbers: old_key_version.id_numbers.iter() - .filter(|&(k, _)| version_holders.contains(k)) - .map(|(k, v)| (k.clone().into(), v.clone().into())).collect(), - }))?; - } - - Ok(()) - } - - /// Disseminate key refreshing data. - fn disseminate_keys(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - // generate random polynom with secret share as absolute term - let explanation = "disseminate_keys is only called on consensus group nodes; consensus group nodes have specified version of the key; qed"; - let key_share = core.key_share.as_ref().expect(explanation); - let key_version = key_share.version(data.version.as_ref().expect(explanation)).expect(explanation); - let mut secret_share_polynom = math::generate_random_polynom(key_share.threshold)?; - secret_share_polynom[0] = key_version.secret_share.clone(); - - // calculate secret subshare for every new node (including this node) - let explanation = "disseminate_keys is called after initialization has completed; this field is filled during initialization; qed"; - for (new_node, new_node_number) in data.id_numbers.as_ref().expect(explanation).iter() { - let new_node_number = new_node_number.as_ref().ok_or(Error::InvalidMessage)?; - let secret_subshare = math::compute_polynom(&secret_share_polynom, new_node_number)?; - if new_node != &core.meta.self_node_id { - core.transport.send(new_node, ShareAddMessage::NewKeysDissemination(NewKeysDissemination { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - secret_subshare: secret_subshare.into(), - }))?; - } else { - let secret_subshare = Self::compute_secret_subshare(core, data, new_node, &secret_subshare)?; - *data.secret_subshares.as_mut().expect(explanation) - .get_mut(&core.meta.self_node_id) - .expect("disseminate_keys is only calle on consensus group nodes; there's entry for every consensus node in secret_subshares; qed") - = Some(secret_subshare); - } - } - - Ok(()) - } - - /// Compute secret subshare from passed secret value. - fn compute_secret_subshare(core: &SessionCore, data: &SessionData, sender: &NodeId, secret_value: &Secret) -> Result { - let explanation = "this field is a result of consensus job; compute_secret_subshare is called after consensus is established"; - let id_numbers = data.id_numbers.as_ref().expect(explanation); - let secret_subshares = data.secret_subshares.as_ref().expect(explanation); - let threshold = core.key_share.as_ref().map(|ks| ks.threshold) - .unwrap_or_else(|| data.new_key_share.as_ref() - .expect("computation occurs after receiving key share threshold if not having one already; qed") - .threshold); - - let explanation = "id_numbers are checked to have Some value for every consensus group node when consensus is establishe; qed"; - let sender_id_number = id_numbers[sender].as_ref().expect(explanation); - let other_id_numbers = secret_subshares.keys().filter(|k| *k != sender).map(|n| id_numbers[n].as_ref().expect(explanation)); - math::compute_secret_subshare(threshold, secret_value, sender_id_number, other_id_numbers) - } - - /// Complete session. - fn complete_session(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - // if already completed, do nothing - if data.state == SessionState::Finished { - return Ok(()); - } - - // compose updated key share - let explanation = "this field is a result of consensus job; complete_session is called after consensus is established"; - let id_numbers = data.id_numbers.as_ref().expect(explanation); - let secret_subshares = data.secret_subshares.as_ref() - .expect("nodes are filled during consensus establishing; session is completed after consensus is established; qed"); - let secret_share = math::compute_secret_share(secret_subshares.values().map(|ss| ss.as_ref() - .expect("complete_session is only called when subshares from all nodes are received; qed")))?; - - let refreshed_key_version = DocumentKeyShareVersion::new(id_numbers.clone().into_iter().map(|(k, v)| (k.clone(), - v.expect("id_numbers are checked to have Some value for every consensus group node when consensus is establishe; qed"))).collect(), - secret_share); - let mut refreshed_key_share = core.key_share.as_ref().cloned().unwrap_or_else(|| { - let new_key_share = data.new_key_share.as_ref() - .expect("this is new node; on new nodes this field is filled before KRD; session is completed after KRD; qed"); - DocumentKeyShare { - author: new_key_share.author.clone(), - threshold: new_key_share.threshold, - public: new_key_share.joint_public.clone(), - common_point: new_key_share.common_point.clone(), - encrypted_point: new_key_share.encrypted_point.clone(), - versions: Vec::new(), - } - }); - refreshed_key_share.versions.push(refreshed_key_version); - - // save encrypted data to the key storage - data.state = SessionState::Finished; - if core.key_share.is_some() { - core.key_storage.update(core.meta.id.clone(), refreshed_key_share.clone())?; - } else { - core.key_storage.insert(core.meta.id.clone(), refreshed_key_share.clone())?; - } - - // signal session completion - data.state = SessionState::Finished; - data.result = Some(Ok(())); - core.completed.send(Ok(())); - - Ok(()) - } -} - -impl ClusterSession for SessionImpl where T: SessionTransport { - type Id = SessionId; - type CreationData = (); // never used directly - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "share add" - } - - fn id(&self) -> SessionId { - self.core.meta.id.clone() - } - - fn is_finished(&self) -> bool { - self.data.lock().state == SessionState::Finished - } - - fn on_session_timeout(&self) { - self.on_session_error(&self.core.meta.self_node_id, Error::NodeDisconnected) - } - - fn on_node_timeout(&self, node: &NodeId) { - self.on_session_error(node, Error::NodeDisconnected) - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in generation session is considered fatal - // => broadcast error if error occured on this node - if *node == self.core.meta.self_node_id { - for node in self.core.transport.nodes() { - // do not bother processing send error, as we already processing error - let _ = self.core.transport.send(&node, ShareAddMessage::ShareAddError(ShareAddError { - session: self.core.meta.id.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - } - } - - let mut data = self.data.lock(); - - warn!(target: "secretstore_net", "{}: share add session failed: {} on {}", - self.core.meta.self_node_id, error, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error.clone())); - self.core.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::ShareAdd(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl IsolatedSessionTransport { - pub fn new(session_id: SessionId, version: Option, nonce: u64, cluster: Arc) -> Self { - IsolatedSessionTransport { - session: session_id, - version: version, - nonce: nonce, - cluster: cluster, - id_numbers: None, - version_holders: None, - consensus_group: None, - } - } -} - -impl JobTransport for IsolatedSessionTransport { - type PartialJobRequest = ServersSetChangeAccessRequest; - type PartialJobResponse = bool; - - fn send_partial_request(&self, node: &NodeId, request: ServersSetChangeAccessRequest) -> Result<(), Error> { - let explanation = "partial requests are sent from master node only; on master node this field is filled during creation; qed"; - let id_numbers = self.id_numbers.as_ref().expect(explanation); - - self.cluster.send(node, Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ShareAddConsensusMessage { - session: self.session.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageOfShareAdd::InitializeConsensusSession(InitializeConsensusSessionOfShareAdd { - version: self.version.clone().expect(explanation).into(), - version_holders: self.version_holders.as_ref().expect(explanation).iter().cloned().map(Into::into).collect(), - consensus_group: self.consensus_group.as_ref().expect(explanation).iter().cloned().map(Into::into).collect(), - old_nodes_set: request.old_servers_set.into_iter().map(Into::into).collect(), - new_nodes_map: request.new_servers_set.into_iter() - .filter_map(|n| id_numbers.get(&n) - .map(|id| (n.into(), id.clone() - .expect("partial requests are sent from master node only after consensus is established; - on master id_numbers are initialized with Some id_number for every consensus group node; qed").into()))) - .collect(), - old_set_signature: request.old_set_signature.into(), - new_set_signature: request.new_set_signature.into(), - }), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ShareAddConsensusMessage { - session: self.session.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }), - }))) - } -} - -impl SessionTransport for IsolatedSessionTransport { - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn set_master_data(&mut self, consensus_group: BTreeSet, version_holders: BTreeSet, id_numbers: BTreeMap>) { - self.version_holders = Some(version_holders); - self.consensus_group = Some(consensus_group); - self.id_numbers = Some(id_numbers); - } - - fn send(&self, node: &NodeId, message: ShareAddMessage) -> Result<(), Error> { - self.cluster.send(node, Message::ShareAdd(message)) - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::BTreeSet; - use crypto::publickey::{Random, Generator, Public}; - use blockchain::SigningKeyPair; - use key_server_cluster::{NodeId, Error, KeyStorage}; - use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; - use key_server_cluster::servers_set_change_session::tests::{MessageLoop, AdminSessionAdapter, generate_key}; - use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - use super::{SessionImpl, SessionParams, IsolatedSessionTransport}; - - struct Adapter; - - impl AdminSessionAdapter> for Adapter { - const SIGN_NEW_NODES: bool = false; - - fn create( - mut meta: ShareChangeSessionMeta, - admin_public: Public, - _: BTreeSet, - ml: &ClusterMessageLoop, - idx: usize - ) -> SessionImpl { - let key_storage = ml.key_storage(idx).clone(); - let key_version = key_storage.get(&meta.id).unwrap().map(|ks| ks.last_version().unwrap().hash); - - meta.self_node_id = *ml.node_key_pair(idx).public(); - SessionImpl::new(SessionParams { - meta: meta.clone(), - transport: IsolatedSessionTransport::new(meta.id, key_version, 1, ml.cluster(idx).view().unwrap()), - key_storage, - admin_public: Some(admin_public), - nonce: 1, - }).unwrap().0 - } - } - - impl MessageLoop> { - pub fn init_at(self, master: NodeId) -> Result { - self.sessions[&master].initialize( - Some(self.original_key_version), - Some(self.new_nodes_set.clone()), - Some(self.all_set_signature.clone()), - Some(self.new_set_signature.clone()))?; - Ok(self) - } - - pub fn run_at(self, master: NodeId) -> Result { - let mut ml = self.init_at(master)?; - ml.run(); - Ok(ml) - } - } - - #[test] - fn node_add_fails_if_nodes_removed() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to remove 1 node - let add = vec![Random.generate()]; - let remove: BTreeSet<_> = ::std::iter::once(gml.0.node(1)).collect(); - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), Some(remove), None) - .run_at(master).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn node_add_fails_if_no_nodes_added() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 0 nodes - let add = vec![]; - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .run_at(master).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn node_add_fails_if_started_on_adding_node() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 1 node using this node as a master node - let add = vec![Random.generate()]; - let master = *add[0].public(); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .run_at(master).unwrap_err(), Error::ServerKeyIsNotFound); - } - - #[test] - fn node_add_fails_if_initialized_twice() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 1 node using this node as a master node - let add = vec![Random.generate()]; - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .init_at(master).unwrap() - .init_at(master).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn node_add_fails_if_started_without_signatures() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 1 node using this node as a master node - let add = vec![Random.generate()]; - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .sessions[&master] - .initialize(None, None, None, None).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn nodes_added_using_share_add() { - let test_cases = vec![(3, 1), (3, 3)]; - for (n, add) in test_cases { - // generate key - let gml = generate_key(n, 1); - - // run share add session - let add = (0..add).map(|_| Random.generate()).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, None) - .run_at(master).unwrap(); - - // check that secret is still the same as before adding the share - ml.check_secret_is_preserved(ml.sessions.keys()); - } - } - - #[test] - fn nodes_added_using_share_add_with_isolated_nodes() { - let (n, add) = (3, 3); - - // generate key - let gml = generate_key(n, 1); - - // run share add session - let master = gml.0.node(0); - let node_to_isolate = gml.0.node(1); - let add = (0..add).map(|_| Random.generate()).collect(); - let isolate = ::std::iter::once(node_to_isolate).collect(); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, Some(isolate)) - .run_at(master).unwrap(); - - // check that secret is still the same as before adding the share - ml.check_secret_is_preserved(ml.sessions.keys()); - } - - #[test] - fn nodes_add_to_the_node_with_obsolete_version() { - let (n, add) = (3, 3); - - // generate key - let gml = generate_key(n, 1); - - // run share add session - let master = gml.0.node(0); - let node_to_isolate_key_pair = gml.0.node_key_pair(1).clone(); - let node_to_isolate = gml.0.node(1); - let isolated_key_storage = gml.0.key_storage(1).clone(); - let mut oldest_nodes_set = gml.0.nodes(); - oldest_nodes_set.remove(&node_to_isolate); - let add = (0..add).map(|_| Random.generate()).collect::>(); - let newest_nodes_set = add.iter().map(|kp| *kp.public()).collect::>(); - let isolate = ::std::iter::once(node_to_isolate).collect(); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, Some(isolate)) - .run_at(master).unwrap(); - let dummy_doc = [1u8; 32].into(); - let new_key_version = ml.ml.key_storage(0).get(&dummy_doc) - .unwrap().unwrap().last_version().unwrap().hash; - - // now let's add back old node so that key becames 2-of-6 - let add = vec![node_to_isolate_key_pair.key_pair().clone()]; - let mut ml = ml.and_then::(master.clone(), Some(add), None, None); - ml.original_key_version = new_key_version; - ml.ml.replace_key_storage_of(&node_to_isolate, isolated_key_storage.clone()); - ml.sessions.get_mut(&node_to_isolate).unwrap().core.key_share = - isolated_key_storage.get(&dummy_doc).unwrap(); - ml.sessions.get_mut(&node_to_isolate).unwrap().core.key_storage = isolated_key_storage; - let ml = ml.run_at(master).unwrap(); - - // check that secret is still the same as before adding the share - ml.check_secret_is_preserved(ml.sessions.keys()); - - // check that all oldest nodes have versions A, B, C - // isolated node has version A, C - // new nodes have versions B, C - let oldest_key_share = ml.ml.key_storage_of(oldest_nodes_set.iter().nth(0).unwrap()) - .get(&dummy_doc).unwrap().unwrap(); - debug_assert_eq!(oldest_key_share.versions.len(), 3); - let version_a = oldest_key_share.versions[0].hash.clone(); - let version_b = oldest_key_share.versions[1].hash.clone(); - let version_c = oldest_key_share.versions[2].hash.clone(); - debug_assert!(version_a != version_b && version_b != version_c); - - debug_assert!(oldest_nodes_set.iter().all(|n| vec![version_a.clone(), version_b.clone(), version_c.clone()] == - ml.ml.key_storage_of(n).get(&dummy_doc).unwrap().unwrap() - .versions.iter().map(|v| v.hash).collect::>())); - debug_assert!(::std::iter::once(&node_to_isolate).all(|n| vec![version_a.clone(), version_c.clone()] == - ml.ml.key_storage_of(n).get(&dummy_doc).unwrap().unwrap() - .versions.iter().map(|v| v.hash).collect::>())); - debug_assert!(newest_nodes_set.iter().all(|n| vec![version_b.clone(), version_c.clone()] == - ml.ml.key_storage_of(n).get(&dummy_doc).unwrap().unwrap() - .versions.iter().map(|v| v.hash).collect::>())); - } - - #[test] - fn nodes_add_fails_when_not_enough_share_owners_are_connected() { - let (n, add) = (3, 3); - - // generate key - let gml = generate_key(n, 1); - - // run share add session - let master = gml.0.node(0); - let add = (0..add).map(|_| Random.generate()).collect::>(); - let isolate = vec![gml.0.node(1), gml.0.node(2)].into_iter().collect(); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, Some(isolate)) - .run_at(master).unwrap_err(), Error::ConsensusUnreachable); - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/share_change_session.rs b/secret-store/src/key_server_cluster/admin_sessions/share_change_session.rs deleted file mode 100644 index e3d58d965..000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/share_change_session.rs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::H256; -use crypto::publickey::Secret; -use key_server_cluster::{Error, NodeId, SessionId, ServerKeyId, KeyStorage}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::ClusterSession; -use key_server_cluster::math; -use key_server_cluster::jobs::servers_set_change_access_job::ServersSetChangeAccessRequest; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::message::{Message, ServersSetChangeMessage, ServersSetChangeShareAddMessage}; -use key_server_cluster::share_add_session::{SessionTransport as ShareAddSessionTransport, - SessionImpl as ShareAddSessionImpl, SessionParams as ShareAddSessionParams}; -use key_server_cluster::message::ShareAddMessage; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Single session meta-change session. Brief overview: -/// 1) nodes that have been already removed from cluster (isolated nodes) are removed from session -/// 2) new shares are added to the session -/// 3) shares are moved between nodes -/// 4) shares are removed from nodes -pub struct ShareChangeSession { - /// Servers set change session id. - session_id: SessionId, - /// Session nonce. - nonce: u64, - /// Share change session meta. - meta: ShareChangeSessionMeta, - /// Cluster. - cluster: Arc, - /// Key storage. - key_storage: Arc, - /// Key version. - key_version: H256, - /// Nodes that have reported version ownership. - version_holders: Option>, - /// Consensus group to use in ShareAdd session. - consensus_group: Option>, - /// Nodes to add shares for. - new_nodes_map: Option>>, - /// Share add session. - share_add_session: Option>, - /// Is finished. - is_finished: bool, -} - -/// Share change session plan. -#[derive(Debug)] -pub struct ShareChangeSessionPlan { - /// Key version that plan is valid for. - pub key_version: H256, - /// Nodes that have reported version ownership. - pub version_holders: BTreeSet, - /// Consensus group to use in ShareAdd session. - pub consensus_group: BTreeSet, - /// Nodes to add shares for. - pub new_nodes_map: BTreeMap>, -} - -/// Session parameters. -pub struct ShareChangeSessionParams { - /// Servers set change session id. - pub session_id: SessionId, - /// Session nonce. - pub nonce: u64, - /// Share change session meta. - pub meta: ShareChangeSessionMeta, - /// Cluster. - pub cluster: Arc, - /// Keys storage. - pub key_storage: Arc, - /// Session plan. - pub plan: ShareChangeSessionPlan, -} - -/// Share add session transport. -#[derive(Clone)] -pub struct ShareChangeTransport { - /// Servers set change session id. - session_id: SessionId, - /// Session nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -impl ShareChangeSession { - /// Create new share change session. - pub fn new(params: ShareChangeSessionParams) -> Result { - // we can't create sessions right now, because key share is read when session is created, but it can change in previous session - let key_version = params.plan.key_version; - let consensus_group = if !params.plan.consensus_group.is_empty() { Some(params.plan.consensus_group) } else { None }; - let version_holders = if !params.plan.version_holders.is_empty() { Some(params.plan.version_holders) } else { None }; - let new_nodes_map = if !params.plan.new_nodes_map.is_empty() { Some(params.plan.new_nodes_map) } else { None }; - debug_assert!(new_nodes_map.is_some()); - - let is_finished = new_nodes_map.is_none(); - Ok(ShareChangeSession { - session_id: params.session_id, - nonce: params.nonce, - meta: params.meta, - cluster: params.cluster, - key_storage: params.key_storage, - key_version: key_version, - version_holders: version_holders, - consensus_group: consensus_group, - new_nodes_map: new_nodes_map, - share_add_session: None, - is_finished: is_finished, - }) - } - - /// Is finished?. - pub fn is_finished(&self) -> bool { - self.is_finished - } - - /// Is master node?. - pub fn is_master(&self) -> bool { - self.meta.self_node_id == self.meta.master_node_id - } - - /// Initialize session (on master node). - pub fn initialize(&mut self) -> Result<(), Error> { - self.proceed_to_next_state() - } - - /// When share-add message is received. - pub fn on_share_add_message(&mut self, sender: &NodeId, message: &ShareAddMessage) -> Result<(), Error> { - if self.share_add_session.is_none() { - self.create_share_add_session()?; - } - - let change_state_needed = self.share_add_session.as_ref() - .map(|share_add_session| { - let was_finished = share_add_session.is_finished(); - share_add_session.process_message(sender, message) - .map(|_| share_add_session.is_finished() && !was_finished) - }) - .unwrap_or(Err(Error::InvalidMessage))?; - if change_state_needed { - self.proceed_to_next_state()?; - } - - Ok(()) - } - - /// Create new share add session. - fn create_share_add_session(&mut self) -> Result<(), Error> { - let consensus_group = self.consensus_group.take().ok_or(Error::InvalidStateForRequest)?; - let version_holders = self.version_holders.take().ok_or(Error::InvalidStateForRequest)?; - let new_nodes_map = self.new_nodes_map.take().ok_or(Error::InvalidStateForRequest)?; - let (share_add_session, _) = ShareAddSessionImpl::new(ShareAddSessionParams { - meta: self.meta.clone(), - nonce: self.nonce, - transport: ShareChangeTransport::new(self.session_id, self.nonce, self.cluster.clone()), - key_storage: self.key_storage.clone(), - admin_public: None, - })?; - share_add_session.set_consensus_output(&self.key_version, consensus_group, version_holders, new_nodes_map)?; - self.share_add_session = Some(share_add_session); - Ok(()) - } - - /// Proceed to the next state. - fn proceed_to_next_state(&mut self) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - if self.new_nodes_map.is_none() { - self.is_finished = true; - } - return Ok(()); - } - - if self.new_nodes_map.is_some() { - self.create_share_add_session()?; - return self.share_add_session.as_ref() - .expect("either create_share_add_session fails, or session is created; qed") - .initialize(None, None, None, None); - } - - self.is_finished = true; - - Ok(()) - } -} - -impl ShareChangeTransport { - pub fn new(session_id: SessionId, nonce: u64, cluster: Arc) -> Self { - ShareChangeTransport { - session_id: session_id, - nonce: nonce, - cluster: cluster, - } - } -} - -impl JobTransport for ShareChangeTransport { - type PartialJobRequest = ServersSetChangeAccessRequest; - type PartialJobResponse = bool; - - fn send_partial_request(&self, _node: &NodeId, _request: ServersSetChangeAccessRequest) -> Result<(), Error> { - unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") - } - - fn send_partial_response(&self, _node: &NodeId, _response: bool) -> Result<(), Error> { - unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") - } -} - -impl ShareAddSessionTransport for ShareChangeTransport { - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn set_master_data(&mut self, _consensus_group: BTreeSet, _version_holders: BTreeSet, _id_numbers: BTreeMap>) { - unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") - } - - fn send(&self, node: &NodeId, message: ShareAddMessage) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeShareAddMessage(ServersSetChangeShareAddMessage { - session: self.session_id.clone().into(), - session_nonce: self.nonce, - message: message, - }))) - } -} - -/// Prepare share change plan for moving from old `old_key_version_owners` to `new_nodes_set`. -pub fn prepare_share_change_session_plan(cluster_nodes: &BTreeSet, threshold: usize, key_id: &ServerKeyId, key_version: H256, master: &NodeId, old_key_version_owners: &BTreeSet, new_nodes_set: &BTreeSet) -> Result { - // we can't do anything if there are no enought shares - if old_key_version_owners.len() < threshold + 1 { - warn!("cannot add shares to key {} with threshold {}: only {} shares owners are available", - key_id, threshold, old_key_version_owners.len()); - return Ok(ShareChangeSessionPlan { - key_version: key_version, - version_holders: Default::default(), - consensus_group: Default::default(), - new_nodes_map: Default::default(), - }); - } - - // warn if we're loosing the key - if new_nodes_set.len() < threshold + 1 { - warn!("losing key {} with threshold {}: only {} nodes left after servers set change session", - key_id, threshold, new_nodes_set.len()); - } - - // make new nodes map, so that: - // all non-isolated old nodes will have their id number preserved - // all new nodes will have new id number - let mut new_nodes_map = new_nodes_set.difference(&old_key_version_owners) - .map(|n| math::generate_random_scalar().map(|id| (n.clone(), Some(id)))) - .collect::, _>>()?; - if !new_nodes_map.is_empty() { - for old_node in old_key_version_owners.iter().filter(|n| cluster_nodes.contains(n)) { - new_nodes_map.insert(old_node.clone(), None); - } - } - - // select consensus group if there are some nodes to add - let consensus_group = if !new_nodes_map.is_empty() { - ::std::iter::once(master.clone()) - .chain(old_key_version_owners.iter() - .filter(|n| *n != master && cluster_nodes.contains(*n)) - .take(threshold) - .cloned()) - .collect() - } else { - BTreeSet::new() - }; - - Ok(ShareChangeSessionPlan { - key_version: key_version, - version_holders: old_key_version_owners.clone(), - consensus_group: consensus_group, - new_nodes_map: new_nodes_map, - }) -} - -impl ShareChangeSessionPlan { - /// Is empty (nothing-to-do) plan? - pub fn is_empty(&self) -> bool { - self.new_nodes_map.is_empty() - } -} - -#[cfg(test)] -mod tests { - use key_server_cluster::math; - use super::prepare_share_change_session_plan; - - #[test] - fn share_change_plan_creates_empty_plan() { - let cluster_nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let master = cluster_nodes[0].clone(); - let old_key_version_owners = cluster_nodes.iter().cloned().collect(); - let new_nodes_set = cluster_nodes.iter().cloned().collect(); - let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), - 1, &Default::default(), Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); - - assert!(plan.is_empty()); - } - - #[test] - fn share_change_plan_adds_new_nodes() { - let cluster_nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let master = cluster_nodes[0].clone(); - let old_key_version_owners = cluster_nodes[0..2].iter().cloned().collect(); - let new_nodes_set = cluster_nodes.iter().cloned().collect(); - let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), - 1, &Default::default(), Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); - - assert!(!plan.is_empty()); - assert_eq!(old_key_version_owners, plan.consensus_group); - assert_eq!(new_nodes_set, plan.new_nodes_map.keys().cloned().collect()); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/decryption_session.rs b/secret-store/src/key_server_cluster/client_sessions/decryption_session.rs deleted file mode 100644 index dd846ce82..000000000 --- a/secret-store/src/key_server_cluster/client_sessions/decryption_session.rs +++ /dev/null @@ -1,1484 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::{Address, H256}; -use crypto::publickey::Secret; -use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, Requester, - EncryptedDocumentKeyShadow, SessionMeta}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, - PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage, InitializeConsensusSession, - ConfirmConsensusInitialization, DecryptionSessionDelegation, DecryptionSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport}; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::decryption_job::{PartialDecryptionRequest, PartialDecryptionResponse, DecryptionJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; - -/// Distributed decryption session. -/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: -/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for decrypting the secret) requests all other nodes to decrypt the secret -/// 2) ACL check: all nodes which have received the request are querying ACL-contract to check if requestor has access to the document -/// 3) partial decryption: every node which has succussfully checked access for the requestor do a partial decryption -/// 4) decryption: master node receives all partial decryptions of the secret and restores the secret -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: SessionMeta, - /// Decryption session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Session-level nonce. - pub nonce: u64, - /// Session completion signal. - pub completed: CompletionSignal, -} - -/// Decryption consensus session type. -type DecryptionConsensusSession = ConsensusSession; -/// Broadcast decryption job session type. -type BroadcastDecryptionJobSession = JobSession; - -/// Mutable session data. -struct SessionData { - /// Key version to use for decryption. - pub version: Option, - /// Session origin (if any). - pub origin: Option
, - /// Consensus-based decryption session. - pub consensus_session: DecryptionConsensusSession, - /// Broadcast decryption job. - pub broadcast_job_session: Option, - /// Is shadow decryption requested? - pub is_shadow_decryption: Option, - /// Decryption result must be reconstructed on all participating nodes. This is useful - /// for service contract API so that all nodes from consensus group can confirm decryption. - pub is_broadcast_session: Option, - /// Delegation status. - pub delegation_status: Option, - /// Decryption result. - pub result: Option>, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: SessionMeta, - /// Session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// ACL storage. - pub acl_storage: Arc, - /// Cluster. - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Decryption consensus transport. -struct DecryptionConsensusTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Session origin (if any). - origin: Option
, - /// Selected key version (on master node). - version: Option, - /// Cluster. - cluster: Arc, -} - -/// Decryption job transport -struct DecryptionJobTransport { - /// Session id. - id: SessionId, - //// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Is this a broadcast transport? If true, requests are not send and responses are sent only to non-master nodes. - is_broadcast_transport: bool, - /// Master node id. - master_node_id: NodeId, - /// Cluster. - cluster: Arc, -} - -/// Session delegation status. -enum DelegationStatus { - /// Delegated to other node. - DelegatedTo(NodeId), - /// Delegated from other node. - DelegatedFrom(NodeId, u64), -} - -impl SessionImpl { - /// Create new decryption session. - pub fn new( - params: SessionParams, - requester: Option, - ) -> Result<(Self, Oneshot>), Error> { - debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); - - // check that common_point and encrypted_point are already set - if let Some(key_share) = params.key_share.as_ref() { - // encrypted data must be set - if key_share.common_point.is_none() || key_share.encrypted_point.is_none() { - return Err(Error::DocumentKeyIsNotFound); - } - } - - let consensus_transport = DecryptionConsensusTransport { - id: params.meta.id.clone(), - access_key: params.access_key.clone(), - nonce: params.nonce, - origin: None, - version: None, - cluster: params.cluster.clone(), - }; - let consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: params.meta.clone(), - consensus_executor: match requester { - Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), - None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), - }, - consensus_transport: consensus_transport, - })?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - access_key: params.access_key, - key_share: params.key_share, - cluster: params.cluster, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - version: None, - origin: None, - consensus_session: consensus_session, - broadcast_job_session: None, - is_shadow_decryption: None, - is_broadcast_session: None, - delegation_status: None, - result: None, - }), - }, oneshot)) - } - - /// Get this node id. - #[cfg(test)] - pub fn node(&self) -> &NodeId { - &self.core.meta.self_node_id - } - - /// Get this session access key. - #[cfg(test)] - pub fn access_key(&self) -> &Secret { - &self.core.access_key - } - - /// Get session state (tests only). - #[cfg(test)] - pub fn state(&self) -> ConsensusSessionState { - self.data.lock().consensus_session.state() - } - - /// Get decrypted secret - #[cfg(test)] - pub fn decrypted_secret(&self) -> Option> { - self.data.lock().result.clone() - } - - /// Get key requester. - pub fn requester(&self) -> Option { - self.data.lock().consensus_session.consensus_job().executor().requester().cloned() - } - - /// Get session origin. - pub fn origin(&self) -> Option
{ - self.data.lock().origin.clone() - } - - /// Get session completion result (if available). - pub fn result(&self) -> Option> { - self.data.lock().result.clone() - } - - /// Get broadcasted shadows. - pub fn broadcast_shadows(&self) -> Option>> { - let data = self.data.lock(); - - if data.result.is_none() || (data.is_broadcast_session, data.is_shadow_decryption) != (Some(true), Some(true)) { - return None; - } - - let proof = "data.is_shadow_decryption is true; decrypt_shadow.is_some() is checked in DecryptionJob::check_partial_response; qed"; - Some(match self.core.meta.master_node_id == self.core.meta.self_node_id { - true => data.consensus_session.computation_job().responses().iter() - .map(|(n, r)| (n.clone(), r.decrypt_shadow.clone().expect(proof))) - .collect(), - false => data.broadcast_job_session.as_ref().expect("session completed; is_shadow_decryption == true; we're on non-master node; qed").responses().iter() - .map(|(n, r)| (n.clone(), r.decrypt_shadow.clone().expect(proof))) - .collect(), - }) - } - - /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, origin: Option
, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(DecryptionSessionDelegation { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - origin: origin.map(Into::into), - requester: data.consensus_session.consensus_job().executor().requester() - .expect("signature is passed to master node on creation; session can be delegated from master node only; qed") - .clone().into(), - version: version.into(), - is_shadow_decryption: is_shadow_decryption, - is_broadcast_session: is_broadcast_session, - })))?; - data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); - Ok(()) - } - - /// Initialize decryption session on master node. - pub fn initialize(&self, origin: Option
, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - // check if version exists - let key_version = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version)?, - }; - - let mut data = self.data.lock(); - let non_isolated_nodes = self.core.cluster.nodes(); - let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(*n)) - .cloned() - .chain(::std::iter::once(self.core.meta.self_node_id.clone())) - .collect(); - if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { - consensus_nodes.remove(&delegation_master); - } - - data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); - data.consensus_session.consensus_job_mut().transport_mut().origin = origin.clone(); - data.origin = origin; - data.version = Some(version.clone()); - data.is_shadow_decryption = Some(is_shadow_decryption); - data.is_broadcast_session = Some(is_broadcast_session); - data.consensus_session.initialize(consensus_nodes)?; - - if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - Self::disseminate_jobs(&self.core, &mut *data, &version, is_shadow_decryption, is_broadcast_session)?; - - debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); - let result = data.consensus_session.result()?; - Self::set_decryption_result(&self.core, &mut *data, Ok(result)); - } - - Ok(()) - } - - /// Process decryption message. - pub fn process_message(&self, sender: &NodeId, message: &DecryptionMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &DecryptionMessage::DecryptionConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &DecryptionMessage::RequestPartialDecryption(ref message) => - self.on_partial_decryption_requested(sender, message), - &DecryptionMessage::PartialDecryption(ref message) => - self.on_partial_decryption(sender, message), - &DecryptionMessage::DecryptionSessionError(ref message) => - self.process_node_error(Some(&sender), message.error.clone()), - &DecryptionMessage::DecryptionSessionCompleted(ref message) => - self.on_session_completed(sender, message), - &DecryptionMessage::DecryptionSessionDelegation(ref message) => - self.on_session_delegated(sender, message), - &DecryptionMessage::DecryptionSessionDelegationCompleted(ref message) => - self.on_session_delegation_completed(sender, message), - } - } - - /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &DecryptionSessionDelegation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - { - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_requester(message.requester.clone().into()); - data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); - } - - self.initialize(message.origin.clone().map(Into::into), message.version.clone().into(), message.is_shadow_decryption, message.is_broadcast_session) - } - - /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &DecryptionSessionDelegationCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), - _ => return Err(Error::InvalidMessage), - } - - Self::set_decryption_result(&self.core, &mut *data, Ok(EncryptedDocumentKeyShadow { - decrypted_secret: message.decrypted_secret.clone().into(), - common_point: message.common_point.clone().map(Into::into), - decrypt_shadows: message.decrypt_shadows.clone().map(Into::into), - })); - - Ok(()) - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &DecryptionConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - let mut data = self.data.lock(); - let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { - let version = msg.version.clone().into(); - let has_key_share = self.core.key_share.as_ref() - .map(|ks| ks.version(&version).is_ok()) - .unwrap_or(false); - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); - data.version = Some(version); - data.origin = message.origin.clone().map(Into::into); - } - data.consensus_session.on_consensus_message(&sender, &message.message)?; - - let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let is_shadow_decryption = data.is_shadow_decryption - .expect("we are on master node; on master node is_shadow_decryption is filled in initialize(); on_consensus_message follows initialize (state check in consensus_session); qed"); - let is_broadcast_session = data.is_broadcast_session - .expect("we are on master node; on master node is_broadcast_session is filled in initialize(); on_consensus_message follows initialize (state check in consensus_session); qed"); - Self::disseminate_jobs(&self.core, &mut *data, &version, is_shadow_decryption, is_broadcast_session) - } - - /// When partial decryption is requested. - pub fn on_partial_decryption_requested(&self, sender: &NodeId, message: &RequestPartialDecryption) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let mut data = self.data.lock(); - let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?)?.hash.clone(); - let requester_public = data.consensus_session.consensus_job().executor().requester() - .ok_or(Error::InvalidStateForRequest)? - .public(&self.core.meta.id) - .map_err(Error::InsufficientRequesterData)?; - let decryption_job = DecryptionJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.access_key.clone(), - requester_public.clone(), key_share.clone(), key_version)?; - let decryption_transport = self.core.decryption_transport(false); - - // update flags if not on master - if self.core.meta.self_node_id != self.core.meta.master_node_id { - data.is_shadow_decryption = Some(message.is_shadow_decryption); - data.is_broadcast_session = Some(message.is_broadcast_session); - } - - // respond to request - let partial_decryption = data.consensus_session.on_job_request(sender, PartialDecryptionRequest { - id: message.request_id.clone().into(), - is_shadow_decryption: message.is_shadow_decryption, - is_broadcast_session: message.is_broadcast_session, - other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), - }, decryption_job, decryption_transport)?; - - // ...and prepare decryption job session if we need to broadcast result - if message.is_broadcast_session { - let consensus_group: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).collect(); - let broadcast_decryption_job = DecryptionJob::new_on_master(self.core.meta.self_node_id.clone(), - self.core.access_key.clone(), requester_public, key_share.clone(), key_version, - message.is_shadow_decryption, message.is_broadcast_session)?; - Self::create_broadcast_decryption_job(&self.core, &mut *data, consensus_group, broadcast_decryption_job, - message.request_id.clone().into(), Some(partial_decryption.take_response()))?; - } - - Ok(()) - } - - /// When partial decryption is received. - pub fn on_partial_decryption(&self, sender: &NodeId, message: &PartialDecryption) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - let is_master_node = self.core.meta.self_node_id == self.core.meta.master_node_id; - let result = if is_master_node { - data.consensus_session.on_job_response(sender, PartialDecryptionResponse { - request_id: message.request_id.clone().into(), - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - })?; - - if data.consensus_session.state() != ConsensusSessionState::Finished && - data.consensus_session.state() != ConsensusSessionState::Failed { - return Ok(()); - } - - // send completion signal to all nodes, except for rejected nodes - if is_master_node { - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } - } - - data.consensus_session.result() - } else { - match data.broadcast_job_session.as_mut() { - Some(broadcast_job_session) => { - broadcast_job_session.on_partial_response(sender, PartialDecryptionResponse { - request_id: message.request_id.clone().into(), - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - })?; - - if broadcast_job_session.state() != JobSessionState::Finished && - broadcast_job_session.state() != JobSessionState::Failed { - return Ok(()); - } - - broadcast_job_session.result() - }, - None => return Err(Error::InvalidMessage), - } - }; - - Self::set_decryption_result(&self.core, &mut *data, result); - - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &DecryptionSessionCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - // if it is a broadcast session, wait for all answers before completing the session - let decryption_result = match data.broadcast_job_session.as_ref() { - Some(broadcast_job_session) => { - if !broadcast_job_session.is_result_ready() { - return Err(Error::TooEarlyForRequest); - } - - Some(broadcast_job_session.result()) - }, - None => None, - }; - if let Some(decryption_result) = decryption_result { - Self::set_decryption_result(&self.core, &mut *data, decryption_result); - } - - data.consensus_session.on_session_completed(sender) - } - - /// Process error from the other node. - fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { - let mut data = self.data.lock(); - let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); - // error is always fatal if coming from this node - if is_self_node_error { - Self::set_decryption_result(&self.core, &mut *data, Err(error.clone())); - return Err(error); - } - - match { - match node { - Some(node) => data.consensus_session.on_node_error(node, error.clone()), - None => data.consensus_session.on_session_timeout(), - } - } { - Ok(false) => Ok(()), - Ok(true) => { - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let proof = "on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when is_shadow_decryption.is_some(); qed"; - let is_shadow_decryption = data.is_shadow_decryption.expect(proof); - let is_broadcast_session = data.is_broadcast_session.expect(proof); - let disseminate_result = Self::disseminate_jobs(&self.core, &mut *data, &version, is_shadow_decryption, is_broadcast_session); - match disseminate_result { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - - Self::set_decryption_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - } - } - }, - Err(err) => { - warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - - Self::set_decryption_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - }, - } - } - - /// Disseminate jobs on session master. - fn disseminate_jobs(core: &SessionCore, data: &mut SessionData, version: &H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { - let key_share = match core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let key_version = key_share.version(version)?.hash.clone(); - let requester = data.consensus_session.consensus_job().executor().requester().ok_or(Error::InvalidStateForRequest)?.clone(); - let requester_public = requester.public(&core.meta.id).map_err(Error::InsufficientRequesterData)?; - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - let decryption_job = DecryptionJob::new_on_master(core.meta.self_node_id.clone(), - core.access_key.clone(), requester_public.clone(), key_share.clone(), key_version, - is_shadow_decryption, is_broadcast_session)?; - let decryption_request_id = decryption_job.request_id().clone() - .expect("DecryptionJob always have request_id when created on master; it is created using new_on_master above; qed"); - let decryption_transport = core.decryption_transport(false); - let is_broadcast_session = data.is_broadcast_session - .expect("disseminate_jobs is called on master node only; on master node is_broadcast_session is filled during initialization; qed"); - let self_response = data.consensus_session.disseminate_jobs(decryption_job, decryption_transport, is_broadcast_session)?; - - // ...and prepare decryption job session if we need to broadcast result - if is_broadcast_session { - let broadcast_decryption_job = DecryptionJob::new_on_master(core.meta.self_node_id.clone(), - core.access_key.clone(), requester_public, key_share.clone(), key_version, is_shadow_decryption, is_broadcast_session)?; - Self::create_broadcast_decryption_job(&core, data, consensus_group, broadcast_decryption_job, - decryption_request_id, self_response)?; - } - - Ok(()) - } - - /// Create broadcast decryption job. - fn create_broadcast_decryption_job(core: &SessionCore, data: &mut SessionData, mut consensus_group: BTreeSet, mut job: DecryptionJob, request_id: Secret, self_response: Option) -> Result<(), Error> { - consensus_group.insert(core.meta.self_node_id.clone()); - job.set_request_id(request_id.clone().into()); - - let transport = core.decryption_transport(true); - let mut job_session = JobSession::new(SessionMeta { - id: core.meta.id.clone(), - master_node_id: core.meta.self_node_id.clone(), - self_node_id: core.meta.self_node_id.clone(), - threshold: core.meta.threshold, - configured_nodes_count: core.meta.configured_nodes_count, - connected_nodes_count: core.meta.connected_nodes_count, - }, job, transport); - job_session.initialize(consensus_group, self_response, core.meta.self_node_id != core.meta.master_node_id)?; - data.broadcast_job_session = Some(job_session); - - Ok(()) - } - - /// Set decryption result. - fn set_decryption_result(core: &SessionCore, data: &mut SessionData, result: Result) { - if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { - // error means can't communicate => ignore it - let _ = match result.as_ref() { - Ok(document_key) => core.cluster.send(&master, Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(DecryptionSessionDelegationCompleted { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - decrypted_secret: document_key.decrypted_secret.clone().into(), - common_point: document_key.common_point.clone().map(Into::into), - decrypt_shadows: document_key.decrypt_shadows.clone(), - }))), - Err(error) => core.cluster.send(&master, Message::Decryption(DecryptionMessage::DecryptionSessionError(DecryptionSessionError { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - error: error.clone().into(), - }))), - }; - } - - data.result = Some(result.clone()); - core.completed.send(result); - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionIdWithSubSession; - type CreationData = Requester; - type SuccessfulResult = EncryptedDocumentKeyShadow; - - fn type_name() -> &'static str { - "decryption" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.consensus_session.state() == ConsensusSessionState::Failed - || data.consensus_session.state() == ConsensusSessionState::Finished - || data.result.is_some() - } - - fn on_node_timeout(&self, node: &NodeId) { - // ignore error, only state matters - let _ = self.process_node_error(Some(node), Error::NodeDisconnected); - } - - fn on_session_timeout(&self) { - // ignore error, only state matters - let _ = self.process_node_error(None, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); - let is_this_node_error = *node == self.core.meta.self_node_id; - if is_fatal || is_this_node_error { - // error in signing session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - let message = Message::Decryption(DecryptionMessage::DecryptionSessionError(DecryptionSessionError { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - - // do not bother processing send error, as we already processing error - let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { - self.core.cluster.broadcast(message) - } else { - self.core.cluster.send(&self.core.meta.master_node_id, message) - }; - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::Decryption(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl SessionCore { - pub fn decryption_transport(&self, is_broadcast_transport: bool) -> DecryptionJobTransport { - DecryptionJobTransport { - id: self.meta.id.clone(), - access_key: self.access_key.clone(), - nonce: self.nonce, - is_broadcast_transport: is_broadcast_transport, - master_node_id: self.meta.master_node_id.clone(), - cluster: self.cluster.clone(), - } - } -} - -impl JobTransport for DecryptionConsensusTransport { - type PartialJobRequest=Requester; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Requester) -> Result<(), Error> { - let version = self.version.as_ref() - .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - origin: self.origin.clone().map(Into::into), - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: request.into(), - version: version.clone().into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - origin: None, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - -impl JobTransport for DecryptionJobTransport { - type PartialJobRequest=PartialDecryptionRequest; - type PartialJobResponse=PartialDecryptionResponse; - - fn send_partial_request(&self, node: &NodeId, request: PartialDecryptionRequest) -> Result<(), Error> { - if !self.is_broadcast_transport { - self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: request.id.into(), - is_shadow_decryption: request.is_shadow_decryption, - is_broadcast_session: request.is_broadcast_session, - nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), - })))?; - } - - Ok(()) - } - - fn send_partial_response(&self, node: &NodeId, response: PartialDecryptionResponse) -> Result<(), Error> { - if !self.is_broadcast_transport || *node != self.master_node_id { - self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: response.request_id.into(), - shadow_point: response.shadow_point.into(), - decrypt_shadow: response.decrypt_shadow, - })))?; - } - - Ok(()) - } -} - -#[cfg(test)] -pub fn create_default_decryption_session() -> Arc { - use acl_storage::DummyAclStorage; - use key_server_cluster::cluster::tests::DummyCluster; - use ethereum_types::H512; - - Arc::new(SessionImpl::new(SessionParams { - meta: SessionMeta { - id: Default::default(), - self_node_id: Default::default(), - master_node_id: Default::default(), - threshold: 0, - configured_nodes_count: 0, - connected_nodes_count: 0, - }, - access_key: Secret::zero(), - key_share: Default::default(), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(Default::default())), - nonce: 0, - }, Some(Requester::Public(H512::from_low_u64_be(2)))).unwrap().0) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::collections::{BTreeMap, VecDeque}; - use acl_storage::DummyAclStorage; - use crypto::publickey::{KeyPair, Random, Generator, Public, Secret, public_to_address}; - use key_server_cluster::{NodeId, DocumentKeyShare, DocumentKeyShareVersion, SessionId, Requester, - Error, EncryptedDocumentKeyShadow, SessionMeta}; - use key_server_cluster::cluster::tests::DummyCluster; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::decryption_session::{SessionImpl, SessionParams}; - use key_server_cluster::message::{self, Message, DecryptionMessage}; - use key_server_cluster::math; - use key_server_cluster::jobs::consensus_session::ConsensusSessionState; - use ethereum_types::{H512, Address}; - use std::str::FromStr; - - const SECRET_PLAIN: &'static str = "d2b57ae7619e070af0af6bc8c703c0cd27814c54d5d6a999cacac0da34ede279ca0d9216e85991029e54e2f0c92ee0bd30237725fa765cbdbfc4529489864c5f"; - const DUMMY_SESSION_ID: [u8; 32] = [1u8; 32]; - fn prepare_decryption_sessions() -> (KeyPair, Vec>, Vec>, Vec) { - // prepare encrypted data + cluster configuration for scheme 4-of-5 - let session_id = SessionId::from(DUMMY_SESSION_ID); - let access_key = Random.generate().secret().clone(); - let secret_shares: Vec = vec![ - "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), - "5a3c1d90fafafa66bb808bcc464354a98b05e6b2c95b5f609d4511cdd1b17a0b".parse().unwrap(), - "71bf61e7848e08e3a8486c308ce521bdacfebcf9116a0151447eb301f3a2d0e9".parse().unwrap(), - "80c0e5e2bea66fa9b2e07f7ce09630a9563e8242446d5ee63221feb09c4338f4".parse().unwrap(), - "c06546b5669877ba579ca437a5602e89425c53808c708d44ccd6afcaa4610fad".parse().unwrap(), - ]; - let id_numbers: Vec<(NodeId, Secret)> = vec![ - (H512::from_str("b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8").unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()), - (H512::from_str("1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb").unwrap(), - "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse().unwrap()), - (H512::from_str("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc").unwrap(), - "f43ac0fba42a5b6ed95707d2244659e89ba877b1c9b82c0d0a9dcf834e80fc62".parse().unwrap()), - (H512::from_str("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c").unwrap(), - "5a324938dfb2516800487d25ab7289ba8ec38811f77c3df602e4e65e3c9acd9f".parse().unwrap()), - (H512::from_str("321977760d1d8e15b047a309e4c7fe6f355c10bb5a06c68472b676926427f69f229024fa2692c10da167d14cdc77eb95d0fce68af0a0f704f0d3db36baa83bb2").unwrap(), - "12cf422d50002d04e52bd4906fd7f5f235f051ca36abfe37e061f8da248008d8".parse().unwrap()), - ]; - let common_point: Public = H512::from_str("6962be696e1bcbba8e64cc7fddf140f854835354b5804f3bb95ae5a2799130371b589a131bd39699ac7174ccb35fc4342dab05331202209582fc8f3a40916ab0").unwrap(); - let encrypted_point: Public = H512::from_str("b07031982bde9890e12eff154765f03c56c3ab646ad47431db5dd2d742a9297679c4c65b998557f8008469afd0c43d40b6c5f6c6a1c7354875da4115237ed87a").unwrap(); - let encrypted_datas: Vec<_> = (0..5).map(|i| DocumentKeyShare { - author: Default::default(), - threshold: 3, - public: Default::default(), - common_point: Some(common_point.clone()), - encrypted_point: Some(encrypted_point.clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: id_numbers.clone().into_iter().collect(), - secret_share: secret_shares[i].clone(), - }], - }).collect(); - let acl_storages: Vec<_> = (0..5).map(|_| Arc::new(DummyAclStorage::default())).collect(); - let clusters: Vec<_> = (0..5).map(|i| { - let cluster = Arc::new(DummyCluster::new(id_numbers.iter().nth(i).clone().unwrap().0)); - for id_number in &id_numbers { - cluster.add_node(id_number.0.clone()); - } - cluster - }).collect(); - let requester = Random.generate(); - let signature = Some(crypto::publickey::sign(requester.secret(), &session_id).unwrap()); - let sessions: Vec<_> = (0..5).map(|i| SessionImpl::new(SessionParams { - meta: SessionMeta { - id: session_id, - self_node_id: id_numbers.iter().nth(i).clone().unwrap().0, - master_node_id: id_numbers.iter().nth(0).clone().unwrap().0, - threshold: encrypted_datas[i].threshold, - configured_nodes_count: 5, - connected_nodes_count: 5, - }, - access_key: access_key.clone(), - key_share: Some(encrypted_datas[i].clone()), - acl_storage: acl_storages[i].clone(), - cluster: clusters[i].clone(), - nonce: 0, - }, if i == 0 { signature.clone().map(Into::into) } else { None }).unwrap().0).collect(); - - (requester, clusters, acl_storages, sessions) - } - - fn do_messages_exchange(clusters: &[Arc], sessions: &[SessionImpl]) -> Result<(), Error> { - do_messages_exchange_until(clusters, sessions, |_, _, _| false) - } - - fn do_messages_exchange_until(clusters: &[Arc], sessions: &[SessionImpl], mut cond: F) -> Result<(), Error> where F: FnMut(&NodeId, &NodeId, &Message) -> bool { - let mut queue: VecDeque<(NodeId, NodeId, Message)> = VecDeque::new(); - while let Some((mut from, mut to, mut message)) = clusters.iter().filter_map(|c| c.take_message().map(|(to, msg)| (c.node(), to, msg))).next() { - if cond(&from, &to, &message) { - break; - } - - let mut is_queued_message = false; - loop { - let session = &sessions[sessions.iter().position(|s| s.node() == &to).unwrap()]; - match session.on_message(&from, &message) { - Ok(_) => { - if let Some(qmessage) = queue.pop_front() { - from = qmessage.0; - to = qmessage.1; - message = qmessage.2; - is_queued_message = true; - continue; - } - break; - }, - Err(Error::TooEarlyForRequest) => { - if is_queued_message { - queue.push_front((from, to, message)); - } else { - queue.push_back((from, to, message)); - } - break; - }, - Err(err) => return Err(err), - } - } - } - - Ok(()) - } - - #[test] - fn constructs_in_cluster_of_single_node() { - let mut nodes = BTreeMap::new(); - let self_node_id = Random.generate().public().clone(); - nodes.insert(self_node_id, Random.generate().secret().clone()); - let msg = [1u8; 32].into(); - let requester = Some(Requester::Signature(crypto::publickey::sign(Random.generate().secret(), &msg).unwrap())); - let params = SessionParams { - meta: SessionMeta { - id: SessionId::from([1u8; 32]), - self_node_id: self_node_id.clone(), - master_node_id: self_node_id.clone(), - threshold: 0, - configured_nodes_count: 1, - connected_nodes_count: 1, - }, - access_key: Random.generate().secret().clone(), - key_share: Some(DocumentKeyShare { - author: Default::default(), - threshold: 0, - public: Default::default(), - common_point: Some(Random.generate().public().clone()), - encrypted_point: Some(Random.generate().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: nodes, - secret_share: Random.generate().secret().clone(), - }], - }), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - nonce: 0, - }; - match SessionImpl::new(params, requester) { - Ok(_) => (), - _ => panic!("unexpected"), - } - } - - #[test] - fn fails_to_initialize_if_does_not_have_a_share() { - let self_node_id = Random.generate().public().clone(); - let session = SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::from(DUMMY_SESSION_ID), - self_node_id: self_node_id.clone(), - master_node_id: self_node_id.clone(), - threshold: 0, - configured_nodes_count: 1, - connected_nodes_count: 1, - }, - access_key: Random.generate().secret().clone(), - key_share: None, - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - nonce: 0, - }, Some(Requester::Signature( - crypto::publickey::sign(Random.generate().secret(), &SessionId::from(DUMMY_SESSION_ID)).unwrap() - ))).unwrap().0; - assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::InvalidMessage)); - } - - #[test] - fn fails_to_initialize_if_threshold_is_wrong() { - let mut nodes = BTreeMap::new(); - let self_node_id = Random.generate().public().clone(); - nodes.insert(self_node_id.clone(), Random.generate().secret().clone()); - nodes.insert(Random.generate().public().clone(), Random.generate().secret().clone()); - let session = SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::from(DUMMY_SESSION_ID), - self_node_id: self_node_id.clone(), - master_node_id: self_node_id.clone(), - threshold: 2, - configured_nodes_count: 1, - connected_nodes_count: 1, - }, - access_key: Random.generate().secret().clone(), - key_share: Some(DocumentKeyShare { - author: Default::default(), - threshold: 2, - public: Default::default(), - common_point: Some(Random.generate().public().clone()), - encrypted_point: Some(Random.generate().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: nodes, - secret_share: Random.generate().secret().clone(), - }], - }), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - nonce: 0, - }, Some(Requester::Signature( - crypto::publickey::sign(Random.generate().secret(), &SessionId::from(DUMMY_SESSION_ID)).unwrap() - ))).unwrap().0; - assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::ConsensusUnreachable)); - } - - #[test] - fn fails_to_initialize_when_already_initialized() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(), ()); - assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_initialization_when_already_initialized() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(), ()); - assert_eq!(sessions[0].on_consensus_message(sessions[1].node(), &message::DecryptionConsensusMessage { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - origin: None, - message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(crypto::publickey::sign( - Random.generate().secret(), &SessionId::from(DUMMY_SESSION_ID)).unwrap()).into(), - version: Default::default(), - }), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_partial_decrypt_if_requested_by_slave() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - origin: None, - message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(crypto::publickey::sign(Random.generate().secret(), - &SessionId::from(DUMMY_SESSION_ID)).unwrap()).into(), - version: Default::default(), - }), - }).unwrap(), ()); - assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node(), &message::RequestPartialDecryption { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - request_id: Random.generate().secret().clone().into(), - is_shadow_decryption: false, - is_broadcast_session: false, - nodes: sessions.iter().map(|s| s.node().clone().into()).take(4).collect(), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_partial_decrypt_if_wrong_number_of_nodes_participating() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - origin: None, - message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(crypto::publickey::sign(Random.generate().secret(), - &SessionId::from(DUMMY_SESSION_ID)).unwrap()).into(), - version: Default::default(), - }), - }).unwrap(), ()); - assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node(), &message::RequestPartialDecryption { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - request_id: Random.generate().secret().clone().into(), - is_shadow_decryption: false, - is_broadcast_session: false, - nodes: sessions.iter().map(|s| s.node().clone().into()).take(2).collect(), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_accept_partial_decrypt_if_not_waiting() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].on_partial_decryption(sessions[1].node(), &message::PartialDecryption { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - request_id: Random.generate().secret().clone().into(), - shadow_point: Random.generate().public().clone().into(), - decrypt_shadow: None, - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_partial_decrypt_twice() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - let mut pd_from = None; - let mut pd_msg = None; - do_messages_exchange_until(&clusters, &sessions, |from, _, msg| match msg { - &Message::Decryption(DecryptionMessage::PartialDecryption(ref msg)) => { - pd_from = Some(from.clone()); - pd_msg = Some(msg.clone()); - true - }, - _ => false, - }).unwrap(); - - assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.clone().unwrap()).unwrap(), ()); - assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.unwrap()).unwrap_err(), Error::InvalidNodeForRequest); - } - - #[test] - fn decryption_fails_on_session_timeout() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert!(sessions[0].decrypted_secret().is_none()); - sessions[0].on_session_timeout(); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn node_is_marked_rejected_when_timed_out_during_initialization_confirmation() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - // 1 node disconnects => we still can recover secret - sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains_key(sessions[1].node())); - assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); - - // 2 node are disconnected => we can not recover secret - sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].state() == ConsensusSessionState::Failed); - } - - #[test] - fn session_does_not_fail_if_rejected_node_disconnects() { - let (_, clusters, acl_storages, sessions) = prepare_decryption_sessions(); - let key_pair = Random.generate(); - - acl_storages[1].prohibit(public_to_address(key_pair.public()), SessionId::from(DUMMY_SESSION_ID)); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // 1st node disconnects => ignore this - sessions[0].on_node_timeout(sessions[1].node()); - assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn session_does_not_fail_if_requested_node_disconnects() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // 1 node disconnects => we still can recover secret - sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); - - // 2 node are disconnected => we can not recover secret - sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].state() == ConsensusSessionState::Failed); - } - - #[test] - fn session_does_not_fail_if_node_with_shadow_point_disconnects() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults - && sessions[0].data.lock().consensus_session.computation_job().responses().len() == 2).unwrap(); - - // disconnects from the node which has already sent us its own shadow point - let disconnected = sessions[0].data.lock(). - consensus_session.computation_job().responses().keys() - .filter(|n| *n != sessions[0].node()) - .cloned().nth(0).unwrap(); - sessions[0].on_node_timeout(&disconnected); - assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn session_restarts_if_confirmed_node_disconnects() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // disconnects from the node which has already confirmed its participation - let disconnected = sessions[0].data.lock().consensus_session.computation_job().requests().iter().cloned().nth(0).unwrap(); - sessions[0].on_node_timeout(&disconnected); - assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - assert!(sessions[0].data.lock().consensus_session.computation_job().rejects().contains_key(&disconnected)); - assert!(!sessions[0].data.lock().consensus_session.computation_job().requests().contains(&disconnected)); - } - - #[test] - fn session_does_not_fail_if_non_master_node_disconnects_from_non_master_node() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // disconnects from the node which has already confirmed its participation - sessions[1].on_node_timeout(sessions[2].node()); - assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); - assert!(sessions[1].state() == ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn complete_dec_session() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 5 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); - // 2) 1 session has decrypted key value - assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn complete_shadow_dec_session() { - let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), true, false).unwrap(); - - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 5 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); - // 2) 1 session has decrypted key value - assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - - let decrypted_secret = sessions[0].decrypted_secret().unwrap().unwrap(); - // check that decrypted_secret != SECRET_PLAIN - assert!(decrypted_secret.decrypted_secret != H512::from_str(SECRET_PLAIN).unwrap()); - // check that common point && shadow coefficients are returned - assert!(decrypted_secret.common_point.is_some()); - assert!(decrypted_secret.decrypt_shadows.is_some()); - // check that KS client is able to restore original secret - use crypto::DEFAULT_MAC; - use crypto::publickey::ecies::decrypt; - let decrypt_shadows: Vec<_> = decrypted_secret.decrypt_shadows.unwrap().into_iter() - .map(|c| Secret::copy_from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap()) - .collect(); - let decrypted_secret = math::decrypt_with_shadow_coefficients(decrypted_secret.decrypted_secret, decrypted_secret.common_point.unwrap(), decrypt_shadows).unwrap(); - assert_eq!(decrypted_secret, H512::from_str(SECRET_PLAIN).unwrap()); - } - - #[test] - fn failed_dec_session() { - let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - // we need 4 out of 5 nodes to agree to do a decryption - // let's say that 2 of these nodes are disagree - let document = [1u8; 32].into(); - acl_storages[1].prohibit(public_to_address(key_pair.public()), document); - acl_storages[2].prohibit(public_to_address(key_pair.public()), document); - - assert_eq!(do_messages_exchange(&clusters, &sessions).unwrap_err(), Error::ConsensusUnreachable); - - // check that 3 nodes have failed state - assert_eq!(sessions[0].state(), ConsensusSessionState::Failed); - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Failed).count(), 3); - } - - #[test] - fn complete_dec_session_with_acl_check_failed_on_master() { - let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); - - // we need 4 out of 5 nodes to agree to do a decryption - // let's say that 1 of these nodes (master) is disagree - acl_storages[0].prohibit(public_to_address(key_pair.public()), SessionId::from(DUMMY_SESSION_ID)); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 4 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); - // 2) 1 session has decrypted key value - assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn decryption_message_fails_when_nonce_is_wrong() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].process_message(sessions[0].node(), &message::DecryptionMessage::DecryptionSessionCompleted( - message::DecryptionSessionCompleted { - session: SessionId::from(DUMMY_SESSION_ID).into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 10, - } - )), Err(Error::ReplayProtection)); - } - - #[test] - fn decryption_works_when_delegated_to_other_node() { - let (_, clusters, _, mut sessions) = prepare_decryption_sessions(); - - // let's say node1 doesn't have a share && delegates decryption request to node0 - // initially session is created on node1 => node1 is master for itself, but for other nodes node0 is still master - sessions[1].core.meta.master_node_id = sessions[1].core.meta.self_node_id.clone(); - sessions[1].data.lock().consensus_session.consensus_job_mut().executor_mut().set_requester( - sessions[0].data.lock().consensus_session.consensus_job().executor().requester().unwrap().clone() - ); - - // now let's try to do a decryption - sessions[1].delegate(sessions[0].core.meta.self_node_id.clone(), Default::default(), Default::default(), false, false).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 4 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 4); - // 2) 1 session has decrypted key value - assert_eq!(sessions[1].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn decryption_works_when_share_owners_are_isolated() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - - // we need 4 out of 5 nodes to agree to do a decryption - // let's say that 1 of these nodes (master) is isolated - let isolated_node_id = sessions[4].core.meta.self_node_id.clone(); - for cluster in &clusters { - cluster.remove_node(&isolated_node_id); - } - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn decryption_result_restored_on_all_nodes_if_broadcast_session_is_completed() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, true).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // decryption result must be the same and available on 4 nodes - let result = sessions[0].decrypted_secret(); - assert!(result.clone().unwrap().is_ok()); - assert_eq!(result.clone().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == result).count()); - assert_eq!(1, sessions.iter().skip(1).filter(|s| s.decrypted_secret().is_none()).count()); - } - - #[test] - fn decryption_shadows_restored_on_all_nodes_if_shadow_broadcast_session_is_completed() { - let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), true, true).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // decryption shadows must be the same and available on 4 nodes - let broadcast_shadows = sessions[0].broadcast_shadows(); - assert!(broadcast_shadows.is_some()); - assert_eq!(3, sessions.iter().skip(1).filter(|s| s.broadcast_shadows() == broadcast_shadows).count()); - assert_eq!(1, sessions.iter().skip(1).filter(|s| s.broadcast_shadows().is_none()).count()); - - // 4 nodes must be able to recover original secret - use crypto::DEFAULT_MAC; - use crypto::publickey::ecies::decrypt; - let result = sessions[0].decrypted_secret().unwrap().unwrap(); - assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == Some(Ok(result.clone()))).count()); - let decrypt_shadows: Vec<_> = result.decrypt_shadows.unwrap().into_iter() - .map(|c| Secret::copy_from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap()) - .collect(); - let decrypted_secret = math::decrypt_with_shadow_coefficients(result.decrypted_secret, result.common_point.unwrap(), decrypt_shadows).unwrap(); - assert_eq!(decrypted_secret, H512::from_str(SECRET_PLAIN).unwrap()); - } - - #[test] - fn decryption_session_origin_is_known_to_all_initialized_nodes() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Some(Address::from_low_u64_be(1)), Default::default(), true, true).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // all session must have origin set - assert_eq!(5, sessions.iter().filter(|s| s.origin() == Some(Address::from_low_u64_be(1))).count()); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/encryption_session.rs b/secret-store/src/key_server_cluster/client_sessions/encryption_session.rs deleted file mode 100644 index 05713b1df..000000000 --- a/secret-store/src/key_server_cluster/client_sessions/encryption_session.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::Address; -use crypto::publickey::Public; -use key_server_cluster::{Error, NodeId, SessionId, Requester, KeyStorage, - DocumentKeyShare, ServerKeyId}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, EncryptionMessage, InitializeEncryptionSession, - ConfirmEncryptionInitialization, EncryptionSessionError}; - -/// Encryption (distributed key generation) session. -/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: -/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for storing the secret) initializes the session on all other nodes -/// 2) master node sends common_point + encrypted_point to all other nodes -/// 3) common_point + encrypted_point are saved on all nodes -/// 4) in case of error, previous values are restored -pub struct SessionImpl { - /// Unique session id. - id: SessionId, - /// Public identifier of this node. - self_node_id: NodeId, - /// Encrypted data. - encrypted_data: Option, - /// Key storage. - key_storage: Arc, - /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, - /// Session nonce. - nonce: u64, - /// Session completion signal. - completed: CompletionSignal<()>, - /// Mutable session data. - data: Mutex, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// SessionImpl identifier. - pub id: SessionId, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Encrypted data (result of running generation_session::SessionImpl). - pub encrypted_data: Option, - /// Key storage. - pub key_storage: Arc, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Mutable data of encryption (distributed key generation) session. -#[derive(Debug)] -struct SessionData { - /// Current state of the session. - state: SessionState, - /// Nodes-specific data. - nodes: BTreeMap, - /// Encryption session result. - result: Option>, -} - -/// Mutable node-specific data. -#[derive(Debug, Clone)] -struct NodeData { - // === Values, filled during initialization phase === - /// Flags marking that node has confirmed session initialization. - pub initialization_confirmed: bool, -} - -/// Encryption (distributed key generation) session state. -#[derive(Debug, Clone, PartialEq)] -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Master node waits for every other node to confirm initialization. - WaitingForInitializationConfirm, - - // === Final states of the session === - /// Encryption data is saved. - Finished, - /// Failed to save encryption data. - Failed, -} - -impl SessionImpl { - /// Create new encryption session. - pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { - check_encrypted_data(params.encrypted_data.as_ref())?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - id: params.id, - self_node_id: params.self_node_id, - encrypted_data: params.encrypted_data, - key_storage: params.key_storage, - cluster: params.cluster, - nonce: params.nonce, - completed, - data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - nodes: BTreeMap::new(), - result: None, - }), - }, oneshot)) - } - - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id - } - - /// Start new session initialization. This must be called on master node. - pub fn initialize(&self, requester: Requester, common_point: Public, encrypted_point: Public) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.state = SessionState::WaitingForInitializationConfirm; - data.nodes.extend(self.cluster.nodes().into_iter().map(|n| (n, NodeData { - initialization_confirmed: &n == self.node(), - }))); - - // TODO [Sec]: id signature is not enough here, as it was already used in key generation - // TODO [Reliability]: there could be situation when some nodes have failed to store encrypted data - // => potential problems during restore. some confirmation step is needed (2pc)? - // save encryption data - if let Some(encrypted_data) = self.encrypted_data.clone() { - let requester_address = requester.address(&self.id).map_err(Error::InsufficientRequesterData)?; - update_encrypted_data(&self.key_storage, self.id.clone(), - encrypted_data, requester_address, common_point.clone(), encrypted_point.clone())?; - } - - // start initialization - if data.nodes.len() > 1 { - self.cluster.broadcast(Message::Encryption(EncryptionMessage::InitializeEncryptionSession(InitializeEncryptionSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - requester: requester.into(), - common_point: common_point.into(), - encrypted_point: encrypted_point.into(), - }))) - } else { - data.state = SessionState::Finished; - data.result = Some(Ok(())); - self.completed.send(Ok(())); - - Ok(()) - } - } - - /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeEncryptionSession) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // check that the requester is the author of the encrypted data - if let Some(encrypted_data) = self.encrypted_data.clone() { - let requester: Requester = message.requester.clone().into(); - let requester_address = requester.address(&self.id).map_err(Error::InsufficientRequesterData)?; - update_encrypted_data(&self.key_storage, self.id.clone(), - encrypted_data, requester_address, message.common_point.clone().into(), message.encrypted_point.clone().into())?; - } - - // update state - data.state = SessionState::Finished; - - // send confirmation back to master node - self.cluster.send(&sender, Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(ConfirmEncryptionInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - }))) - } - - /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmEncryptionInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check if all nodes have confirmed initialization - data.nodes.get_mut(&sender) - .expect("message is received from cluster; nodes contains all cluster nodes; qed") - .initialization_confirmed = true; - if !data.nodes.values().all(|n| n.initialization_confirmed) { - return Ok(()); - } - - // update state - data.state = SessionState::Finished; - data.result = Some(Ok(())); - self.completed.send(Ok(())); - - Ok(()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionId; - type CreationData = (); - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "encryption" - } - - fn id(&self) -> SessionId { - self.id.clone() - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.state == SessionState::Failed - || data.state == SessionState::Finished - } - - fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - warn!("{}: encryption session failed because {} connection has timeouted", self.node(), node); - - data.state = SessionState::Failed; - data.result = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - warn!("{}: encryption session failed with timeout", self.node()); - - data.state = SessionState::Failed; - data.result = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in encryption session is considered fatal - // => broadcast error if error occured on this node - if *node == self.self_node_id { - // do not bother processing send error, as we already processing error - let _ = self.cluster.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(EncryptionSessionError { - session: self.id.clone().into(), - session_nonce: self.nonce, - error: error.clone().into(), - }))); - } - - let mut data = self.data.lock(); - - warn!("{}: encryption session failed with error: {} from {}", self.node(), error, node); - - data.state = SessionState::Failed; - data.result = Some(Err(error.clone())); - self.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - if Some(self.nonce) != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &Message::Encryption(ref message) => match message { - &EncryptionMessage::InitializeEncryptionSession(ref message) => - self.on_initialize_session(sender.clone(), message), - &EncryptionMessage::ConfirmEncryptionInitialization(ref message) => - self.on_confirm_initialization(sender.clone(), message), - &EncryptionMessage::EncryptionSessionError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - }, - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl Debug for SessionImpl { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - write!(f, "Encryption session {} on {}", self.id, self.self_node_id) - } -} - -/// Check that common_point and encrypted point are not yet set in key share. -pub fn check_encrypted_data(key_share: Option<&DocumentKeyShare>) -> Result<(), Error> { - if let Some(key_share) = key_share { - // check that common_point and encrypted_point are still not set yet - if key_share.common_point.is_some() || key_share.encrypted_point.is_some() { - return Err(Error::DocumentKeyAlreadyStored); - } - } - - Ok(()) -} - -/// Update key share with encrypted document key. -pub fn update_encrypted_data(key_storage: &Arc, key_id: ServerKeyId, mut key_share: DocumentKeyShare, author: Address, common_point: Public, encrypted_point: Public) -> Result<(), Error> { - // author must be the same - if key_share.author != author { - return Err(Error::AccessDenied); - } - - // save encryption data - key_share.common_point = Some(common_point); - key_share.encrypted_point = Some(encrypted_point); - key_storage.update(key_id, key_share) -} diff --git a/secret-store/src/key_server_cluster/client_sessions/generation_session.rs b/secret-store/src/key_server_cluster/client_sessions/generation_session.rs deleted file mode 100644 index 8c606e307..000000000 --- a/secret-store/src/key_server_cluster/client_sessions/generation_session.rs +++ /dev/null @@ -1,1289 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap, VecDeque}; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::Address; -use crypto::publickey::{Public, Secret}; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; -use key_server_cluster::math; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, - KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; - -/// Distributed key generation session. -/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: -/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for generating joint public + secret) initializes the session on all other nodes -/// 2) key dissemination (KD): all nodes are generating secret + public values and send these to appropriate nodes -/// 3) key verification (KV): all nodes are checking values, received for other nodes -/// 4) key generation phase (KG): nodes are exchanging with information, enough to generate joint public key -pub struct SessionImpl { - /// Unique session id. - id: SessionId, - /// Public identifier of this node. - self_node_id: NodeId, - /// Key storage. - key_storage: Option>, - /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, - /// Session-level nonce. - nonce: u64, - /// Mutable session data. - data: Mutex, - /// Session completion signal. - completed: CompletionSignal, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// SessionImpl identifier. - pub id: SessionId, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Key storage. - pub key_storage: Option>, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: Option, -} - -/// Mutable data of distributed key generation session. -#[derive(Debug)] -struct SessionData { - /// Current state of the session. - state: SessionState, - /// Simulate faulty behaviour? - simulate_faulty_behaviour: bool, - - // === Values, filled when session initialization just starts === - /// Reference to the node, which has started this session. - master: Option, - /// Address of the creator of the session. - author: Option
, - - // === Values, filled when session initialization is completed === - /// Session origin (if any). - origin: Option
, - /// Is zero secret generation session? - is_zero: Option, - /// Threshold value for this DKG. Only `threshold + 1` will be able to collectively recreate joint secret, - /// and thus - decrypt message, encrypted with joint public. - threshold: Option, - /// Random point, jointly generated by every node in the cluster. - derived_point: Option, - /// Nodes-specific data. - nodes: BTreeMap, - - // === Values, filled during KD phase === - /// Polynom1. - polynom1: Option>, - /// Value of polynom1[0], generated by this node. - secret_coeff: Option, - - // === Values, filled during KG phase === - /// Secret share, which this node holds. Persistent + private. - secret_share: Option, - - /// === Values, filled when DKG session is completed successfully === - /// Key share. - key_share: Option>, - /// Jointly generated public key, which can be used to encrypt secret. Public. - joint_public_and_secret: Option>, -} - -/// Mutable node-specific data. -#[derive(Debug, Clone)] -struct NodeData { - /// Random unique scalar. Persistent. - pub id_number: Secret, - - // === Values, filled during KD phase === - /// Secret value1, which has been received from this node. - pub secret1: Option, - /// Secret value2, which has been received from this node. - pub secret2: Option, - /// Public values, which have been received from this node. - pub publics: Option>, - - // === Values, filled during KG phase === - /// Public share, which has been received from this node. - pub public_share: Option, - - // === Values, filled during completion phase === - /// Flags marking that node has confirmed session completion (generated key is stored). - pub completion_confirmed: bool, -} - -/// Schedule for visiting other nodes of cluster. -#[derive(Debug, Clone, PartialEq)] -pub struct EveryOtherNodeVisitor { - /// Already visited nodes. - visited: BTreeSet, - /// Not yet visited nodes. - unvisited: VecDeque, - /// Nodes, which are currently visited. - in_progress: BTreeSet, -} - -/// Distributed key generation session state. -#[derive(Debug, Clone, PartialEq)] -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Master node asks every other node to confirm initialization. - /// Derived point is generated by all nodes in the cluster. - WaitingForInitializationConfirm(EveryOtherNodeVisitor), - /// Slave nodes are in this state until initialization completion is reported by master node. - WaitingForInitializationComplete, - - // === KD phase states === - /// Node is waiting for generated keys from every other node. - WaitingForKeysDissemination, - - // === KG phase states === - /// Node is waiting for joint public key share to be received from every other node. - WaitingForPublicKeyShare, - - // === Generation phase states === - /// Node is waiting for session completion/session completion confirmation. - WaitingForGenerationConfirmation, - - // === Final states of the session === - /// Joint public key generation is completed. - Finished, - /// Joint public key generation is failed. - Failed, -} - -pub enum InitializationNodes { - RandomNumbers(BTreeSet), - SpecificNumbers(BTreeMap) -} - -impl InitializationNodes { - pub fn set(&self) -> BTreeSet { - match *self { - InitializationNodes::RandomNumbers(ref nodes) => nodes.clone(), - InitializationNodes::SpecificNumbers(ref nodes) => nodes.keys().cloned().collect(), - } - } -} - -impl From> for InitializationNodes { - fn from(nodes: BTreeSet) -> Self { - InitializationNodes::RandomNumbers(nodes) - } -} - -impl From> for InitializationNodes { - fn from(nodes: BTreeMap) -> Self { - InitializationNodes::SpecificNumbers(nodes) - } -} - -impl SessionImpl { - /// Create new generation session. - pub fn new(params: SessionParams) -> (Self, Oneshot>) { - let (completed, oneshot) = CompletionSignal::new(); - (SessionImpl { - id: params.id, - self_node_id: params.self_node_id, - key_storage: params.key_storage, - cluster: params.cluster, - // when nonce.is_nonce(), generation session is wrapped - // => nonce is checked somewhere else && we can pass any value - nonce: params.nonce.unwrap_or_default(), - completed, - data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - simulate_faulty_behaviour: false, - master: None, - author: None, - origin: None, - is_zero: None, - threshold: None, - derived_point: None, - nodes: BTreeMap::new(), - polynom1: None, - secret_coeff: None, - secret_share: None, - key_share: None, - joint_public_and_secret: None, - }), - }, oneshot) - } - - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id - } - - /// Get derived point. - #[cfg(test)] - pub fn derived_point(&self) -> Option { - self.data.lock().derived_point.clone() - } - - /// Simulate faulty generation session behaviour. - pub fn simulate_faulty_behaviour(&self) { - self.data.lock().simulate_faulty_behaviour = true; - } - - /// Get session state. - pub fn state(&self) -> SessionState { - self.data.lock().state.clone() - } - - /// Get session origin. - pub fn origin(&self) -> Option
{ - self.data.lock().origin.clone() - } - - /// Get session completion result (if available). - pub fn result(&self) -> Option> { - self.data.lock().joint_public_and_secret.clone() - .map(|r| r.map(|r| r.0.clone())) - } - - /// Get generated public and secret (if any). - pub fn joint_public_and_secret(&self) -> Option> { - self.data.lock().joint_public_and_secret.clone() - } - - /// Start new session initialization. This must be called on master node. - pub fn initialize(&self, origin: Option
, author: Address, is_zero: bool, threshold: usize, nodes: InitializationNodes) -> Result<(), Error> { - check_cluster_nodes(self.node(), &nodes.set())?; - check_threshold(threshold, &nodes.set())?; - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.master = Some(self.node().clone()); - data.author = Some(author.clone()); - data.origin = origin.clone(); - data.is_zero = Some(is_zero); - data.threshold = Some(threshold); - match nodes { - InitializationNodes::RandomNumbers(nodes) => { - for node_id in nodes { - // generate node identification parameter - let node_id_number = math::generate_random_scalar()?; - data.nodes.insert(node_id, NodeData::with_id_number(node_id_number)); - } - }, - InitializationNodes::SpecificNumbers(nodes) => { - for (node_id, node_id_number) in nodes { - data.nodes.insert(node_id, NodeData::with_id_number(node_id_number)); - } - }, - } - - let mut visit_policy = EveryOtherNodeVisitor::new(self.node(), data.nodes.keys().cloned()); - let derived_point = math::generate_random_point()?; - match visit_policy.next_node() { - Some(next_node) => { - data.state = SessionState::WaitingForInitializationConfirm(visit_policy); - - // start initialization - self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - origin: origin.map(Into::into), - author: author.into(), - nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), - is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), - threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), - derived_point: derived_point.into(), - }))) - }, - None => { - drop(data); - self.complete_initialization(derived_point)?; - self.disseminate_keys()?; - self.verify_keys()?; - self.complete_generation()?; - - let mut data = self.data.lock(); - let result = data.joint_public_and_secret.clone() - .expect("session is instantly completed on a single node; qed") - .map(|(p, _, _)| p); - data.state = SessionState::Finished; - self.completed.send(result); - - Ok(()) - } - } - } - - /// Process single message. - pub fn process_message(&self, sender: &NodeId, message: &GenerationMessage) -> Result<(), Error> { - if self.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &GenerationMessage::InitializeSession(ref message) => - self.on_initialize_session(sender.clone(), message), - &GenerationMessage::ConfirmInitialization(ref message) => - self.on_confirm_initialization(sender.clone(), message), - &GenerationMessage::CompleteInitialization(ref message) => - self.on_complete_initialization(sender.clone(), message), - &GenerationMessage::KeysDissemination(ref message) => - self.on_keys_dissemination(sender.clone(), message), - &GenerationMessage::PublicKeyShare(ref message) => - self.on_public_key_share(sender.clone(), message), - &GenerationMessage::SessionError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - &GenerationMessage::SessionCompleted(ref message) => - self.on_session_completed(sender.clone(), message), - } - } - - /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeSession) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - // check message - let nodes_ids = message.nodes.keys().cloned().map(Into::into).collect(); - check_threshold(message.threshold, &nodes_ids)?; - check_cluster_nodes(self.node(), &nodes_ids)?; - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update derived point with random scalar - let mut derived_point = message.derived_point.clone().into(); - math::update_random_point(&mut derived_point)?; - - // send confirmation back to master node - self.cluster.send(&sender, Message::Generation(GenerationMessage::ConfirmInitialization(ConfirmInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - derived_point: derived_point.into(), - })))?; - - // update state - data.master = Some(sender); - data.author = Some(message.author.clone().into()); - data.state = SessionState::WaitingForInitializationComplete; - data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); - data.origin = message.origin.clone().map(Into::into); - data.is_zero = Some(message.is_zero); - data.threshold = Some(message.threshold); - - Ok(()) - } - - /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check state && select new node to be initialized - let next_receiver = match data.state { - SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { - if !visit_policy.mark_visited(&sender) { - return Err(Error::InvalidStateForRequest); - } - - visit_policy.next_node() - }, - _ => return Err(Error::InvalidStateForRequest), - }; - - // proceed message - if let Some(next_receiver) = next_receiver { - return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - origin: data.origin.clone().map(Into::into), - author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), - nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), - is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), - threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), - derived_point: message.derived_point.clone().into(), - }))); - } - - // now it is time for keys dissemination (KD) phase - drop(data); - self.complete_initialization(message.derived_point.clone().into())?; - self.disseminate_keys() - } - - /// When session initialization completion message is received. - pub fn on_complete_initialization(&self, sender: NodeId, message: &CompleteInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitializationComplete { - return Err(Error::InvalidStateForRequest); - } - if data.master != Some(sender) { - return Err(Error::InvalidMessage); - } - - // remember passed data - data.derived_point = Some(message.derived_point.clone().into()); - - // now it is time for keys dissemination (KD) phase - drop(data); - self.disseminate_keys() - } - - /// When keys dissemination message is received. - pub fn on_keys_dissemination(&self, sender: NodeId, message: &KeysDissemination) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // simulate failure, if required - if data.simulate_faulty_behaviour { - return Err(Error::Internal("simulated error".into())); - } - - // check state - if data.state != SessionState::WaitingForKeysDissemination { - match data.state { - SessionState::WaitingForInitializationComplete | SessionState::WaitingForInitializationConfirm(_) => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - debug_assert!(data.nodes.contains_key(&sender)); - - // check message - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"); - let threshold = data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"); - if !is_zero && message.publics.len() != threshold + 1 { - return Err(Error::InvalidMessage); - } - - // update node data - { - let node_data = data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; - if node_data.secret1.is_some() || node_data.secret2.is_some() || node_data.publics.is_some() { - return Err(Error::InvalidStateForRequest); - } - - node_data.secret1 = Some(message.secret1.clone().into()); - node_data.secret2 = Some(message.secret2.clone().into()); - node_data.publics = Some(message.publics.iter().cloned().map(Into::into).collect()); - } - - // check if we have received keys from every other node - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && (node_data.publics.is_none() || node_data.secret1.is_none() || node_data.secret2.is_none())) { - return Ok(()) - } - - drop(data); - self.verify_keys() - } - - /// When public key share is received. - pub fn on_public_key_share(&self, sender: NodeId, message: &PublicKeyShare) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForPublicKeyShare { - match data.state { - SessionState::WaitingForInitializationComplete | - SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - - // update node data with received public share - { - let node_data = &mut data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; - if node_data.public_share.is_some() { - return Err(Error::InvalidMessage); - } - - node_data.public_share = Some(message.public_share.clone().into()); - } - - // if there's also nodes, which has not sent us their public shares - do nothing - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && node_data.public_share.is_none()) { - return Ok(()); - } - - drop(data); - self.complete_generation() - } - - /// When session completion message is received. - pub fn on_session_completed(&self, sender: NodeId, message: &SessionCompleted) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check state - if data.state != SessionState::WaitingForGenerationConfirmation { - match data.state { - SessionState::WaitingForPublicKeyShare => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - - // if we are not masters, save result and respond with confirmation - if data.master.as_ref() != Some(self.node()) { - // check that we have received message from master - if data.master.as_ref() != Some(&sender) { - return Err(Error::InvalidMessage); - } - - // calculate joint public key - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); - let joint_public = if !is_zero { - let public_shares = data.nodes.values().map(|n| n.public_share.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_joint_public(public_shares)? - } else { - Default::default() - }; - - // save encrypted data to key storage - let encrypted_data = DocumentKeyShare { - author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), - threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), - public: joint_public, - common_point: None, - encrypted_point: None, - versions: vec![DocumentKeyShareVersion::new( - data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), - )], - }; - - if let Some(ref key_storage) = self.key_storage { - key_storage.insert(self.id.clone(), encrypted_data.clone())?; - } - - // then respond with confirmation - data.state = SessionState::Finished; - return self.cluster.send(&sender, Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { - session: self.id.clone().into(), - session_nonce: self.nonce, - }))); - } - - // remember that we have received confirmation from sender node - { - let sender_node = data.nodes.get_mut(&sender).expect("node is always qualified by himself; qed"); - if sender_node.completion_confirmed { - return Err(Error::InvalidMessage); - } - - sender_node.completion_confirmed = true; - } - - // check if we have received confirmations from all cluster nodes - if data.nodes.iter().any(|(_, node_data)| !node_data.completion_confirmed) { - return Ok(()) - } - - // we have received enough confirmations => complete session - let result = data.joint_public_and_secret.clone() - .expect("we're on master node; we have received last completion confirmation; qed") - .map(|(p, _, _)| p); - data.state = SessionState::Finished; - self.completed.send(result); - - Ok(()) - } - - /// Complete initialization (when all other nodex has responded with confirmation) - fn complete_initialization(&self, mut derived_point: Public) -> Result<(), Error> { - // update point once again to make sure that derived point is not generated by last node - math::update_random_point(&mut derived_point)?; - - // remember derived point - let mut data = self.data.lock(); - data.derived_point = Some(derived_point.clone().into()); - - // broadcast derived point && other session paraeters to every other node - self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - derived_point: derived_point.into(), - }))) - } - - /// Keys dissemination (KD) phase - fn disseminate_keys(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // pick 2t + 2 random numbers as polynomial coefficients for 2 polynoms - let threshold = data.threshold.expect("threshold is filled on initialization phase; KD phase follows initialization phase; qed"); - let is_zero = data.is_zero.expect("is_zero is filled on initialization phase; KD phase follows initialization phase; qed"); - let mut polynom1 = math::generate_random_polynom(threshold)?; - if is_zero { - polynom1[0] = math::zero_scalar(); - } - let polynom2 = math::generate_random_polynom(threshold)?; - data.polynom1 = Some(polynom1.clone()); - data.secret_coeff = Some(polynom1[0].clone()); - - // compute t+1 public values - let publics = match is_zero { - false => math::public_values_generation(threshold, - data.derived_point.as_ref().expect("keys dissemination occurs after derived point is agreed; qed"), - &polynom1, - &polynom2)?, - true => Default::default(), - }; - - // compute secret values for every other node - for (node, node_data) in data.nodes.iter_mut() { - let secret1 = math::compute_polynom(&polynom1, &node_data.id_number)?; - let secret2 = math::compute_polynom(&polynom2, &node_data.id_number)?; - - // send a message containing secret1 && secret2 to other node - if node != self.node() { - self.cluster.send(&node, Message::Generation(GenerationMessage::KeysDissemination(KeysDissemination { - session: self.id.clone().into(), - session_nonce: self.nonce, - secret1: secret1.into(), - secret2: secret2.into(), - publics: publics.iter().cloned().map(Into::into).collect(), - })))?; - } else { - node_data.secret1 = Some(secret1); - node_data.secret2 = Some(secret2); - node_data.publics = Some(publics.clone()); - } - } - - // update state - data.state = SessionState::WaitingForKeysDissemination; - - Ok(()) - } - - /// Keys verification (KV) phase - fn verify_keys(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // key verification (KV) phase: check that other nodes have passed correct secrets - let threshold = data.threshold.expect("threshold is filled in initialization phase; KV phase follows initialization phase; qed"); - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KV phase follows initialization phase; qed"); - let self_public_share = { - if !is_zero { - let derived_point = data.derived_point.clone().expect("derived point generated on initialization phase; KV phase follows initialization phase; qed"); - let number_id = data.nodes[self.node()].id_number.clone(); - for (_ , node_data) in data.nodes.iter_mut().filter(|&(node_id, _)| node_id != self.node()) { - let secret1 = node_data.secret1.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let secret2 = node_data.secret2.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let publics = node_data.publics.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let is_key_verification_ok = math::keys_verification(threshold, &derived_point, &number_id, - secret1, secret2, publics)?; - - if !is_key_verification_ok { - // node has sent us incorrect values. In original ECDKG protocol we should have sent complaint here. - return Err(Error::InvalidMessage); - } - } - - // calculate public share - let self_public_share = { - let self_secret_coeff = data.secret_coeff.as_ref().expect("secret_coeff is generated on KD phase; KG phase follows KD phase; qed"); - math::compute_public_share(self_secret_coeff)? - }; - - self_public_share - } else { - // TODO [Trust]: add verification when available - Default::default() - } - }; - - // calculate self secret + public shares - let self_secret_share = { - let secret_values_iter = data.nodes.values() - .map(|n| n.secret1.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_secret_share(secret_values_iter)? - }; - - // update state - data.state = SessionState::WaitingForPublicKeyShare; - data.secret_share = Some(self_secret_share); - let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); - self_node.public_share = Some(self_public_share.clone()); - - // broadcast self public key share - self.cluster.broadcast(Message::Generation(GenerationMessage::PublicKeyShare(PublicKeyShare { - session: self.id.clone().into(), - session_nonce: self.nonce, - public_share: self_public_share.into(), - }))) - } - - /// Complete generation - fn complete_generation(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // calculate joint public key - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); - let joint_public = if !is_zero { - let public_shares = data.nodes.values().map(|n| n.public_share.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_joint_public(public_shares)? - } else { - Default::default() - }; - - // prepare key data - let secret_share = data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(); - let encrypted_data = DocumentKeyShare { - author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), - threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), - public: joint_public.clone(), - common_point: None, - encrypted_point: None, - versions: vec![DocumentKeyShareVersion::new( - data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - secret_share.clone(), - )], - }; - - // if we are at the slave node - wait for session completion - let secret_coeff = data.secret_coeff.as_ref().expect("secret coeff is selected on initialization phase; current phase follows initialization; qed").clone(); - if data.master.as_ref() != Some(self.node()) { - data.key_share = Some(Ok(encrypted_data)); - data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff, secret_share))); - data.state = SessionState::WaitingForGenerationConfirmation; - return Ok(()); - } - - // then save encrypted data to the key storage - if let Some(ref key_storage) = self.key_storage { - key_storage.insert(self.id.clone(), encrypted_data.clone())?; - } - - // then distribute encrypted data to every other node - self.cluster.broadcast(Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { - session: self.id.clone().into(), - session_nonce: self.nonce, - })))?; - - // then wait for confirmation from all other nodes - { - let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); - self_node.completion_confirmed = true; - } - data.key_share = Some(Ok(encrypted_data)); - data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff, secret_share))); - data.state = SessionState::WaitingForGenerationConfirmation; - - Ok(()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionId; - type CreationData = (); - type SuccessfulResult = Public; - - fn type_name() -> &'static str { - "generation" - } - - fn id(&self) -> SessionId { - self.id.clone() - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.state == SessionState::Failed - || data.state == SessionState::Finished - } - - fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - // all nodes are required for generation session - // => fail without check - warn!("{}: generation session failed because {} connection has timeouted", self.node(), node); - - data.state = SessionState::Failed; - data.key_share = Some(Err(Error::NodeDisconnected)); - data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - warn!("{}: generation session failed with timeout", self.node()); - - data.state = SessionState::Failed; - data.key_share = Some(Err(Error::NodeDisconnected)); - data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in generation session is considered fatal - // => broadcast error if error occured on this node - if *node == self.self_node_id { - // do not bother processing send error, as we already processing error - let _ = self.cluster.broadcast(Message::Generation(GenerationMessage::SessionError(SessionError { - session: self.id.clone().into(), - session_nonce: self.nonce, - error: error.clone().into(), - }))); - } - - let mut data = self.data.lock(); - data.state = SessionState::Failed; - data.key_share = Some(Err(error.clone())); - data.joint_public_and_secret = Some(Err(error.clone())); - self.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::Generation(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl EveryOtherNodeVisitor { - pub fn new(self_id: &NodeId, nodes: I) -> Self where I: Iterator { - EveryOtherNodeVisitor { - visited: BTreeSet::new(), - unvisited: nodes.filter(|n| n != self_id).collect(), - in_progress: BTreeSet::new(), - } - } - - pub fn next_node(&mut self) -> Option { - let next_node = self.unvisited.pop_front(); - if let Some(ref next_node) = next_node { - self.in_progress.insert(next_node.clone()); - } - next_node - } - - pub fn mark_visited(&mut self, node: &NodeId) -> bool { - if !self.in_progress.remove(node) { - return false; - } - self.visited.insert(node.clone()) - } -} - -impl NodeData { - fn with_id_number(node_id_number: Secret) -> Self { - NodeData { - id_number: node_id_number, - secret1: None, - secret2: None, - publics: None, - public_share: None, - completion_confirmed: false, - } - } -} - -impl Debug for SessionImpl { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - write!(f, "Generation session {} on {}", self.id, self.self_node_id) - } -} - -fn check_cluster_nodes(self_node_id: &NodeId, nodes: &BTreeSet) -> Result<(), Error> { - assert!(nodes.contains(self_node_id)); - Ok(()) -} - -fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Error> { - // at least threshold + 1 nodes are required to collectively decrypt message - if threshold >= nodes.len() { - return Err(Error::NotEnoughNodesForThreshold); - } - - Ok(()) -} - -#[cfg(test)] -pub mod tests { - use std::sync::Arc; - use ethereum_types::H256; - use crypto::publickey::{Random, Generator, KeyPair, Secret}; - use key_server_cluster::{NodeId, Error, KeyStorage, SessionId}; - use key_server_cluster::message::{self, Message, GenerationMessage, KeysDissemination, - PublicKeyShare, ConfirmInitialization}; - use key_server_cluster::cluster::tests::{MessageLoop as ClusterMessageLoop, make_clusters_and_preserve_sessions}; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::generation_session::{SessionImpl, SessionState}; - use key_server_cluster::math; - use key_server_cluster::math::tests::do_encryption_and_decryption; - use ServerKeyId; - - #[derive(Debug)] - pub struct MessageLoop(pub ClusterMessageLoop); - - impl MessageLoop { - pub fn new(num_nodes: usize) -> Self { - MessageLoop(make_clusters_and_preserve_sessions(num_nodes)) - } - - pub fn init(self, threshold: usize) -> Result { - self.0.cluster(0).client().new_generation_session(SessionId::from([1u8; 32]), None, Default::default(), threshold) - .map(|_| self) - } - - pub fn session_at(&self, idx: usize) -> Arc { - self.0.sessions(idx).generation_sessions.first().unwrap() - } - - pub fn session_of(&self, node: &NodeId) -> Arc { - self.0.sessions_of(node).generation_sessions.first().unwrap() - } - - pub fn take_message_confirm_initialization(&self) -> (NodeId, NodeId, ConfirmInitialization) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn take_message_keys_dissemination(&self) -> (NodeId, NodeId, KeysDissemination) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::KeysDissemination(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn take_message_public_key_share(&self) -> (NodeId, NodeId, PublicKeyShare) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::PublicKeyShare(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn nodes_id_numbers(&self) -> Vec { - let session = self.session_at(0); - let session_data = session.data.lock(); - session_data.nodes.values().map(|n| n.id_number.clone()).collect() - } - - pub fn nodes_secret_shares(&self) -> Vec { - (0..self.0.nodes().len()).map(|i| { - let session = self.session_at(i); - let session_data = session.data.lock(); - session_data.secret_share.as_ref().unwrap().clone() - }).collect() - } - - pub fn compute_key_pair(&self) -> KeyPair { - let server_key_id = ServerKeyId::from([1u8; 32]); - let t = self.0.key_storage(0).get(&server_key_id).unwrap().unwrap().threshold; - let secret_shares = self.nodes_secret_shares(); - let id_numbers = self.nodes_id_numbers(); - let secret_shares = secret_shares.iter().take(t + 1).collect::>(); - let id_numbers = id_numbers.iter().take(t + 1).collect::>(); - let joint_secret = math::compute_joint_secret_from_shares(t, &secret_shares, &id_numbers).unwrap(); - - KeyPair::from_secret(joint_secret).unwrap() - } - - pub fn key_version(&self) -> H256 { - let server_key_id = ServerKeyId::from([1u8; 32]); - self.0.key_storage(0).get(&server_key_id) - .unwrap().unwrap().versions.iter().last().unwrap().hash - } - } - - #[test] - fn initializes_in_cluster_of_single_node() { - MessageLoop::new(1).init(0).unwrap(); - } - - #[test] - fn fails_to_initialize_if_threshold_is_wrong() { - assert_eq!(MessageLoop::new(2).init(2).unwrap_err(), Error::NotEnoughNodesForThreshold); - } - - #[test] - fn fails_to_initialize_when_already_initialized() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert_eq!( - ml.session_at(0).initialize(Default::default(), Default::default(), false, 0, ml.0.nodes().into()), - Err(Error::InvalidStateForRequest), - ); - } - - #[test] - fn fails_to_accept_initialization_when_already_initialized() { - let ml = MessageLoop::new(2).init(0).unwrap(); - let (from, to, msg) = ml.0.take_message().unwrap(); - ml.0.process_message(from, to, msg.clone()); - assert_eq!( - ml.session_of(&to).on_message(&from, &msg), - Err(Error::InvalidStateForRequest), - ); - } - - #[test] - fn slave_updates_derived_point_on_initialization() { - let ml = MessageLoop::new(2).init(0).unwrap(); - let original_point = match ml.0.take_message().unwrap() { - (from, to, Message::Generation(GenerationMessage::InitializeSession(msg))) => { - let original_point = msg.derived_point.clone(); - let msg = Message::Generation(GenerationMessage::InitializeSession(msg)); - ml.0.process_message(from, to, msg); - original_point - }, - _ => panic!("unexpected"), - }; - - match ml.0.take_message().unwrap() { - (_, _, Message::Generation(GenerationMessage::ConfirmInitialization(msg))) => - assert!(original_point != msg.derived_point), - _ => panic!("unexpected"), - } - } - - #[test] - fn fails_to_accept_initialization_confirmation_if_already_accepted_from_the_same_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); - - let (from, to, msg) = ml.take_message_confirm_initialization(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg.clone()))); - assert_eq!(ml.session_of(&to).on_confirm_initialization(from, &msg), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn fails_to_accept_initialization_confirmation_if_initialization_already_completed() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(0).on_confirm_initialization(ml.0.node(1), &message::ConfirmInitialization { - session: [1u8; 32].into(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn master_updates_derived_point_on_initialization_completion() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - let original_point = match ml.0.take_message().unwrap() { - (from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg))) => { - let original_point = msg.derived_point.clone(); - let msg = Message::Generation(GenerationMessage::ConfirmInitialization(msg)); - ml.session_of(&to).on_message(&from, &msg).unwrap(); - original_point - }, - _ => panic!("unexpected"), - }; - - assert!(ml.session_at(0).derived_point().unwrap() != original_point.into()); - } - - #[test] - fn fails_to_complete_initialization_if_not_waiting_for_it() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(0).on_complete_initialization(ml.0.node(1), &message::CompleteInitialization { - session: [1u8; 32].into(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn fails_to_complete_initialization_from_non_master_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(1).on_complete_initialization(ml.0.node(2), &message::CompleteInitialization { - session: [1u8; 32].into(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidMessage)); - } - - #[test] - fn fails_to_accept_keys_dissemination_if_not_waiting_for_it() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert_eq!(ml.session_at(0).on_keys_dissemination(ml.0.node(1), &message::KeysDissemination { - session: [1u8; 32].into(), - session_nonce: 0, - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into()], - }), Err(Error::TooEarlyForRequest)); - } - - #[test] - fn fails_to_accept_keys_dissemination_if_wrong_number_of_publics_passed() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - - let (from, to, mut msg) = ml.take_message_keys_dissemination(); - msg.publics.clear(); - assert_eq!(ml.session_of(&to).on_keys_dissemination(from, &msg), Err(Error::InvalidMessage)); - } - - #[test] - fn fails_to_accept_keys_dissemination_second_time_from_the_same_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - - let (from, to, msg) = ml.take_message_keys_dissemination(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::KeysDissemination(msg.clone()))); - assert_eq!(ml.session_of(&to).on_keys_dissemination(from, &msg), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn should_not_accept_public_key_share_when_is_not_waiting_for_it() { - let ml = MessageLoop::new(3).init(1).unwrap(); - assert_eq!(ml.session_at(0).on_public_key_share(ml.0.node(1), &message::PublicKeyShare { - session: [1u8; 32].into(), - session_nonce: 0, - public_share: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn should_not_accept_public_key_share_when_receiving_twice() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - ml.0.take_and_process_message(); // m -> s1: KeysDissemination - ml.0.take_and_process_message(); // m -> s2: KeysDissemination - ml.0.take_and_process_message(); // s1 -> m: KeysDissemination - ml.0.take_and_process_message(); // s1 -> s2: KeysDissemination - ml.0.take_and_process_message(); // s2 -> m: KeysDissemination - ml.0.take_and_process_message(); // s2 -> s1: KeysDissemination - - let (from, to, msg) = ml.take_message_public_key_share(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::PublicKeyShare(msg.clone()))); - assert_eq!(ml.session_of(&to).on_public_key_share(from, &msg), Err(Error::InvalidMessage)); - } - - #[test] - fn encryption_fails_on_session_timeout() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert!(ml.session_at(0).joint_public_and_secret().is_none()); - ml.session_at(0).on_session_timeout(); - assert_eq!(ml.session_at(0).joint_public_and_secret().unwrap(), Err(Error::NodeDisconnected)); - } - - #[test] - fn encryption_fails_on_node_timeout() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert!(ml.session_at(0).joint_public_and_secret().is_none()); - ml.session_at(0).on_node_timeout(&ml.0.node(1)); - assert_eq!(ml.session_at(0).joint_public_and_secret().unwrap(), Err(Error::NodeDisconnected)); - } - - #[test] - fn complete_enc_dec_session() { - let test_cases = [(0, 5), (2, 5), (3, 5)]; - for &(threshold, num_nodes) in &test_cases { - let ml = MessageLoop::new(num_nodes).init(threshold).unwrap(); - ml.0.loop_until(|| ml.0.is_empty()); - - // check that all nodes has finished joint public generation - let joint_public_key = ml.session_at(0).joint_public_and_secret().unwrap().unwrap().0; - for i in 0..num_nodes { - let session = ml.session_at(i); - assert_eq!(session.state(), SessionState::Finished); - assert_eq!(session.joint_public_and_secret().map(|p| p.map(|p| p.0)), Some(Ok(joint_public_key))); - } - - // now let's encrypt some secret (which is a point on EC) - let document_secret_plain = Random.generate().public().clone(); - let all_nodes_id_numbers = ml.nodes_id_numbers(); - let all_nodes_secret_shares = ml.nodes_secret_shares(); - let document_secret_decrypted = do_encryption_and_decryption(threshold, &joint_public_key, - &all_nodes_id_numbers, - &all_nodes_secret_shares, - None, - document_secret_plain.clone() - ).0; - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - - #[test] - fn generation_message_fails_when_nonce_is_wrong() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - - let msg = message::GenerationMessage::KeysDissemination(message::KeysDissemination { - session: [1u8; 32].into(), - session_nonce: 10, - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into()], - }); - assert_eq!(ml.session_at(1).process_message(&ml.0.node(0), &msg).unwrap_err(), Error::ReplayProtection); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/mod.rs b/secret-store/src/key_server_cluster/client_sessions/mod.rs deleted file mode 100644 index 536a09a7c..000000000 --- a/secret-store/src/key_server_cluster/client_sessions/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod decryption_session; -pub mod encryption_session; -pub mod generation_session; -pub mod signing_session_ecdsa; -pub mod signing_session_schnorr; diff --git a/secret-store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs b/secret-store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs deleted file mode 100644 index bc5355304..000000000 --- a/secret-store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs +++ /dev/null @@ -1,1183 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::collections::btree_map::Entry; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use crypto::publickey::{Public, Secret, Signature, sign}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, AclStorage, DocumentKeyShare, Requester}; -use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - SessionState as GenerationSessionState}; -use key_server_cluster::math; -use key_server_cluster::message::{Message, EcdsaSigningMessage, EcdsaSigningConsensusMessage, EcdsaSignatureNonceGenerationMessage, - EcdsaInversionNonceGenerationMessage, EcdsaInversionZeroGenerationMessage, EcdsaSigningInversedNonceCoeffShare, - EcdsaRequestPartialSignature, EcdsaPartialSignature, EcdsaSigningSessionCompleted, GenerationMessage, - ConsensusMessage, EcdsaSigningSessionError, InitializeConsensusSession, ConfirmConsensusInitialization, - EcdsaSigningSessionDelegation, EcdsaSigningSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::signing_job_ecdsa::{EcdsaPartialSigningRequest, EcdsaPartialSigningResponse, EcdsaSigningJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; - -/// Distributed ECDSA-signing session. -/// Based on "A robust threshold elliptic curve digital signature providing a new verifiable secret sharing scheme" paper. -/// WARNING: can only be used if 2*t < N is true for key generation scheme -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: SessionMeta, - /// Signing session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Session-level nonce. - pub nonce: u64, - /// Session completion signal. - pub completed: CompletionSignal, -} - -/// Signing consensus session type. -type SigningConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Message hash. - pub message_hash: Option, - /// Key version to use for decryption. - pub version: Option, - /// Consensus-based signing session. - pub consensus_session: SigningConsensusSession, - /// Signature nonce generation session. - pub sig_nonce_generation_session: Option, - /// Inversion nonce generation session. - pub inv_nonce_generation_session: Option, - /// Inversion zero generation session. - pub inv_zero_generation_session: Option, - /// Inversed nonce coefficient shares. - pub inversed_nonce_coeff_shares: Option>, - /// Delegation status. - pub delegation_status: Option, - /// Decryption result. - pub result: Option>, -} - -/// Signing session state. -#[derive(Debug, PartialEq)] -pub enum SessionState { - /// Consensus is establishing. - ConsensusEstablishing, - /// Nonces (signature, inversion && zero) are generating. - NoncesGenerating, - /// Waiting for inversed nonce shares. - WaitingForInversedNonceShares, - /// State when signature is computing. - SignatureComputing, -} - -/// Session creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: SessionMeta, - /// Session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// ACL storage. - pub acl_storage: Arc, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Signing consensus transport. -struct SigningConsensusTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Selected key version (on master node). - version: Option, - /// Cluster. - cluster: Arc, -} - -/// Signing key generation transport. -struct NonceGenerationTransport EcdsaSigningMessage + Send + Sync> { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, - /// Other nodes ids. - other_nodes_ids: BTreeSet, - /// Message mapping function. - map: F, -} - -/// Signing job transport -struct SigningJobTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -/// Session delegation status. -enum DelegationStatus { - /// Delegated to other node. - DelegatedTo(NodeId), - /// Delegated from other node. - DelegatedFrom(NodeId, u64), -} - -impl SessionImpl { - /// Create new signing session. - pub fn new( - params: SessionParams, - requester: Option, - ) -> Result<(Self, Oneshot>), Error> { - debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); - - let consensus_transport = SigningConsensusTransport { - id: params.meta.id.clone(), - access_key: params.access_key.clone(), - nonce: params.nonce, - version: None, - cluster: params.cluster.clone(), - }; - let consensus_session = ConsensusSession::new(ConsensusSessionParams { - // this session requires responses from 2 * t nodes - meta: SessionMeta { - id: params.meta.id, - master_node_id: params.meta.master_node_id, - self_node_id: params.meta.self_node_id, - threshold: params.meta.threshold * 2, - configured_nodes_count: params.meta.configured_nodes_count, - connected_nodes_count: params.meta.connected_nodes_count, - }, - consensus_executor: match requester { - Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), - None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), - }, - consensus_transport: consensus_transport, - })?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - access_key: params.access_key, - key_share: params.key_share, - cluster: params.cluster, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::ConsensusEstablishing, - message_hash: None, - version: None, - consensus_session: consensus_session, - sig_nonce_generation_session: None, - inv_nonce_generation_session: None, - inv_zero_generation_session: None, - inversed_nonce_coeff_shares: None, - delegation_status: None, - result: None, - }), - }, oneshot)) - } - - /// Wait for session completion. - #[cfg(test)] - pub fn wait(&self) -> Result { - Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) - .expect("wait_session returns Some if called without timeout; qed") - } - - /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, version: H256, message_hash: H256) -> Result<(), Error> { - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(EcdsaSigningSessionDelegation { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - requester: data.consensus_session.consensus_job().executor().requester() - .expect("requester is passed to master node on creation; session can be delegated from master node only; qed") - .clone().into(), - version: version.into(), - message_hash: message_hash.into(), - })))?; - data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); - Ok(()) - } - - /// Initialize signing session on master node. - pub fn initialize(&self, version: H256, message_hash: H256) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - // check if version exists - let key_version = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version)?, - }; - - // select nodes to participate in consensus etablish session - let mut data = self.data.lock(); - let non_isolated_nodes = self.core.cluster.nodes(); - let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(*n)) - .cloned() - .chain(::std::iter::once(self.core.meta.self_node_id.clone())) - .collect(); - if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { - consensus_nodes.remove(&delegation_master); - } - - // start consensus establish sesssion - data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); - data.version = Some(version.clone()); - data.message_hash = Some(message_hash); - data.consensus_session.initialize(consensus_nodes)?; - - // consensus established => threshold is 0 => we can generate signature on this node - if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - let result = sign(&key_version.secret_share, &message_hash).map_err(Into::into); - data.result = Some(result.clone()); - self.core.completed.send(result); - } - - Ok(()) - } - - /// Process signing message. - pub fn process_message(&self, sender: &NodeId, message: &EcdsaSigningMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref message) => - self.on_signature_nonce_generation_message(sender, message), - &EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref message) => - self.on_inversion_nonce_generation_message(sender, message), - &EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref message) => - self.on_inversion_zero_generation_message(sender, message), - &EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref message) => - self.on_inversed_nonce_coeff_share(sender, message), - &EcdsaSigningMessage::EcdsaRequestPartialSignature(ref message) => - self.on_partial_signature_requested(sender, message), - &EcdsaSigningMessage::EcdsaPartialSignature(ref message) => - self.on_partial_signature(sender, message), - &EcdsaSigningMessage::EcdsaSigningSessionError(ref message) => - self.process_node_error(Some(&sender), message.error.clone()), - &EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref message) => - self.on_session_completed(sender, message), - &EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message) => - self.on_session_delegated(sender, message), - &EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref message) => - self.on_session_delegation_completed(sender, message), - } - } - - /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &EcdsaSigningSessionDelegation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - { - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_requester(message.requester.clone().into()); - data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); - } - - self.initialize(message.version.clone().into(), message.message_hash.clone().into()) - } - - /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &EcdsaSigningSessionDelegationCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), - _ => return Err(Error::InvalidMessage), - } - - Self::set_signing_result(&self.core, &mut *data, Ok(message.signature.clone().into())); - - Ok(()) - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &EcdsaSigningConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - - if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { - let version = msg.version.clone().into(); - let has_key_share = self.core.key_share.as_ref() - .map(|ks| ks.version(&version).is_ok()) - .unwrap_or(false); - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); - data.version = Some(version); - } - data.consensus_session.on_consensus_message(&sender, &message.message)?; - - let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let key_share = self.core.key_share.as_ref() - .expect("this is master node; master node is selected so that it has key version; qed"); - let key_version = key_share.version(data.version.as_ref() - .expect("this is master node; master node is selected so that it has key version; qed"))?; - - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - let consensus_group_map: BTreeMap<_, _> = consensus_group.iter().map(|n| (n.clone(), key_version.id_numbers[n].clone())).collect(); - - // start generation of signature nonce - let sig_nonce_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage( - EcdsaSignatureNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - })); - sig_nonce_generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; - data.sig_nonce_generation_session = Some(sig_nonce_generation_session); - - // start generation of inversed nonce computation session - let inv_nonce_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, - move |s, k, n, m| EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage( - EcdsaInversionNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - })); - inv_nonce_generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; - data.inv_nonce_generation_session = Some(inv_nonce_generation_session); - - // start generation of zero-secret shares for inversed nonce computation session - let inv_zero_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, - move |s, k, n, m| EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage( - EcdsaInversionZeroGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - })); - inv_zero_generation_session.initialize(Default::default(), Default::default(), true, key_share.threshold * 2, consensus_group_map.clone().into())?; - data.inv_zero_generation_session = Some(inv_zero_generation_session); - - data.state = SessionState::NoncesGenerating; - - Ok(()) - } - - /// When signature nonce generation message is received. - pub fn on_signature_nonce_generation_message(&self, sender: &NodeId, message: &EcdsaSignatureNonceGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - data.sig_nonce_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage( - EcdsaSignatureNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - }))); - - data.state = SessionState::NoncesGenerating; - } - - { - let generation_session = data.sig_nonce_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - if !Self::check_nonces_generated(&*data) { - return Ok(()); - } - - Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; - data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { - SessionState::SignatureComputing - } else { - SessionState::WaitingForInversedNonceShares - }; - - Ok(()) - } - - /// When inversion nonce generation message is received. - pub fn on_inversion_nonce_generation_message(&self, sender: &NodeId, message: &EcdsaInversionNonceGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - data.inv_nonce_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage( - EcdsaInversionNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - }))); - - data.state = SessionState::NoncesGenerating; - } - - { - let generation_session = data.inv_nonce_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - if !Self::check_nonces_generated(&*data) { - return Ok(()); - } - - Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; - data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { - SessionState::SignatureComputing - } else { - SessionState::WaitingForInversedNonceShares - }; - - Ok(()) - } - - /// When inversion zero generation message is received. - pub fn on_inversion_zero_generation_message(&self, sender: &NodeId, message: &EcdsaInversionZeroGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - data.inv_zero_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage( - EcdsaInversionZeroGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - }))); - - data.state = SessionState::NoncesGenerating; - } - - { - let generation_session = data.inv_zero_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - if !Self::check_nonces_generated(&*data) { - return Ok(()); - } - - Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; - data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { - SessionState::SignatureComputing - } else { - SessionState::WaitingForInversedNonceShares - }; - - Ok(()) - } - - /// When inversed nonce share is received. - pub fn on_inversed_nonce_coeff_share(&self, sender: &NodeId, message: &EcdsaSigningInversedNonceCoeffShare) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if self.core.meta.self_node_id != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - match data.state { - SessionState::WaitingForInversedNonceShares => (), - SessionState::NoncesGenerating => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - - let inversed_nonce_coeff = { - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - { - let inversed_nonce_coeff_shares = data.inversed_nonce_coeff_shares.as_mut() - .expect("we are in WaitingForInversedNonceShares state; inversed_nonce_coeff_shares are filled before this state; qed"); - match inversed_nonce_coeff_shares.entry(sender.clone()) { - Entry::Occupied(_) => return Err(Error::InvalidStateForRequest), - Entry::Vacant(entry) => { - entry.insert(message.inversed_nonce_coeff_share.clone().into()); - }, - } - - if consensus_group.iter().any(|n| !inversed_nonce_coeff_shares.contains_key(n)) { - return Ok(()); - } - } - - Self::compute_inversed_nonce_coeff(&self.core, &*data)? - }; - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let message_hash = data.message_hash - .expect("we are on master node; on master node message_hash is filled in initialize(); on_generation_message follows initialize; qed"); - - let nonce_exists_proof = "nonce is generated before signature is computed; we are in SignatureComputing state; qed"; - let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; - let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; - - self.core.disseminate_jobs(&mut data.consensus_session, &version, sig_nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash) - } - - /// When partial signature is requested. - pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &EcdsaRequestPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let mut data = self.data.lock(); - - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if data.state != SessionState::SignatureComputing { - return Err(Error::InvalidStateForRequest); - } - - let nonce_exists_proof = "nonce is generated before signature is computed; we are in SignatureComputing state; qed"; - let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; - let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let key_version = key_share.version(&version)?.hash.clone(); - - let signing_job = EcdsaSigningJob::new_on_slave(key_share.clone(), key_version, sig_nonce_public, inv_nonce_share)?; - let signing_transport = self.core.signing_transport(); - - data.consensus_session.on_job_request(sender, EcdsaPartialSigningRequest { - id: message.request_id.clone().into(), - inversed_nonce_coeff: message.inversed_nonce_coeff.clone().into(), - message_hash: message.message_hash.clone().into(), - }, signing_job, signing_transport).map(|_| ()) - } - - /// When partial signature is received. - pub fn on_partial_signature(&self, sender: &NodeId, message: &EcdsaPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - data.consensus_session.on_job_response(sender, EcdsaPartialSigningResponse { - request_id: message.request_id.clone().into(), - partial_signature_s: message.partial_signature_s.clone().into(), - })?; - - if data.consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } - - // send compeltion signal to all nodes, except for rejected nodes - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(EcdsaSigningSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } - - let result = data.consensus_session.result()?; - Self::set_signing_result(&self.core, &mut *data, Ok(result)); - - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &EcdsaSigningSessionCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - self.data.lock().consensus_session.on_session_completed(sender) - } - - /// Process error from the other node. - fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { - let mut data = self.data.lock(); - let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); - // error is always fatal if coming from this node - if is_self_node_error { - Self::set_signing_result(&self.core, &mut *data, Err(error.clone())); - return Err(error); - } - - match { - match node { - Some(node) => data.consensus_session.on_node_error(node, error.clone()), - None => data.consensus_session.on_session_timeout(), - } - } { - Ok(false) => { - Ok(()) - }, - Ok(true) => { - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - - let message_hash = data.message_hash.as_ref().cloned() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed"); - - let nonce_exists_proof = "on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when nonces generation has completed; qed"; - let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; - let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; - - let inversed_nonce_coeff = Self::compute_inversed_nonce_coeff(&self.core, &*data)?; - - let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, &version, sig_nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash); - match disseminate_result { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: ECDSA signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - } - } - }, - Err(err) => { - warn!("{}: ECDSA signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - }, - } - } - - /// Start generation session. - fn start_generation_session(core: &SessionCore, other_consensus_group_nodes: &BTreeSet, map_message: F) -> GenerationSession - where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync + 'static { - GenerationSession::new(GenerationSessionParams { - id: core.meta.id.clone(), - self_node_id: core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(NonceGenerationTransport { - id: core.meta.id.clone(), - access_key: core.access_key.clone(), - nonce: core.nonce, - cluster: core.cluster.clone(), - other_nodes_ids: other_consensus_group_nodes.clone(), - map: map_message, - }), - nonce: None, - }).0 - } - - /// Set signing session result. - fn set_signing_result(core: &SessionCore, data: &mut SessionData, result: Result) { - if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { - // error means can't communicate => ignore it - let _ = match result.as_ref() { - Ok(signature) => core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(EcdsaSigningSessionDelegationCompleted { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - signature: signature.clone().into(), - }))), - Err(error) => core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(EcdsaSigningSessionError { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - error: error.clone().into(), - }))), - }; - } - - data.result = Some(result.clone()); - core.completed.send(result); - } - - /// Check if all nonces are generated. - fn check_nonces_generated(data: &SessionData) -> bool { - let expect_proof = "check_nonces_generated is called when som nonce-gen session is completed; - all nonce-gen sessions are created at once; qed"; - let sig_nonce_generation_session = data.sig_nonce_generation_session.as_ref().expect(expect_proof); - let inv_nonce_generation_session = data.inv_nonce_generation_session.as_ref().expect(expect_proof); - let inv_zero_generation_session = data.inv_zero_generation_session.as_ref().expect(expect_proof); - - sig_nonce_generation_session.state() == GenerationSessionState::Finished - && inv_nonce_generation_session.state() == GenerationSessionState::Finished - && inv_zero_generation_session.state() == GenerationSessionState::Finished - } - - /// Broadcast inversed nonce share. - fn send_inversed_nonce_coeff_share(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - let proof = "inversed nonce coeff share is sent after nonces generation is completed; qed"; - - let sig_nonce_generation_session = data.sig_nonce_generation_session.as_ref().expect(proof); - let sig_nonce = sig_nonce_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; - - let inv_nonce_generation_session = data.inv_nonce_generation_session.as_ref().expect(proof); - let inv_nonce = inv_nonce_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; - - let inv_zero_generation_session = data.inv_zero_generation_session.as_ref().expect(proof); - let inv_zero = inv_zero_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; - - let inversed_nonce_coeff_share = math::compute_ecdsa_inversed_secret_coeff_share(&sig_nonce, &inv_nonce, &inv_zero)?; - if core.meta.self_node_id == core.meta.master_node_id { - let mut inversed_nonce_coeff_shares = BTreeMap::new(); - inversed_nonce_coeff_shares.insert(core.meta.self_node_id.clone(), inversed_nonce_coeff_share); - data.inversed_nonce_coeff_shares = Some(inversed_nonce_coeff_shares); - Ok(()) - } else { - core.cluster.send(&core.meta.master_node_id, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(EcdsaSigningInversedNonceCoeffShare { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: core.nonce, - inversed_nonce_coeff_share: inversed_nonce_coeff_share.into(), - }))) - } - } - - /// Compute inversed nonce coefficient on master node. - fn compute_inversed_nonce_coeff(core: &SessionCore, data: &SessionData) -> Result { - let proof = "inversed nonce coeff is computed on master node; key version exists on master node"; - let key_share = core.key_share.as_ref().expect(proof); - let key_version = key_share.version(data.version.as_ref().expect(proof)).expect(proof); - - let proof = "inversed nonce coeff is computed after all shares are received; qed"; - let inversed_nonce_coeff_shares = data.inversed_nonce_coeff_shares.as_ref().expect(proof); - - math::compute_ecdsa_inversed_secret_coeff_from_shares(key_share.threshold, - &inversed_nonce_coeff_shares.keys().map(|n| key_version.id_numbers[n].clone()).collect::>(), - &inversed_nonce_coeff_shares.values().cloned().collect::>()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionIdWithSubSession; - type CreationData = Requester; - type SuccessfulResult = Signature; - - fn type_name() -> &'static str { - "ecdsa_signing" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.consensus_session.state() == ConsensusSessionState::Failed - || data.consensus_session.state() == ConsensusSessionState::Finished - || data.result.is_some() - } - - fn on_node_timeout(&self, node: &NodeId) { - // ignore error, only state matters - let _ = self.process_node_error(Some(node), Error::NodeDisconnected); - } - - fn on_session_timeout(&self) { - // ignore error, only state matters - let _ = self.process_node_error(None, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); - let is_this_node_error = *node == self.core.meta.self_node_id; - if is_fatal || is_this_node_error { - // error in signing session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - let message = Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(EcdsaSigningSessionError { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - - // do not bother processing send error, as we already processing error - let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { - self.core.cluster.broadcast(message) - } else { - self.core.cluster.send(&self.core.meta.master_node_id, message) - }; - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::EcdsaSigning(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl NonceGenerationTransport where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync { - fn map_message(&self, message: Message) -> Result { - match message { - Message::Generation(message) => Ok(Message::EcdsaSigning((self.map)(self.id.clone(), self.access_key.clone(), self.nonce, message))), - _ => Err(Error::InvalidMessage), - } - } -} - -impl Cluster for NonceGenerationTransport where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync { - fn broadcast(&self, message: Message) -> Result<(), Error> { - let message = self.map_message(message)?; - for to in &self.other_nodes_ids { - self.cluster.send(to, message.clone())?; - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - debug_assert!(self.other_nodes_ids.contains(to)); - self.cluster.send(to, self.map_message(message)?) - } - - fn is_connected(&self, node: &NodeId) -> bool { - self.cluster.is_connected(node) - } - - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn configured_nodes_count(&self) -> usize { - self.cluster.configured_nodes_count() - } - - fn connected_nodes_count(&self) -> usize { - self.cluster.connected_nodes_count() - } -} - -impl SessionCore { - pub fn signing_transport(&self) -> SigningJobTransport { - SigningJobTransport { - id: self.meta.id.clone(), - access_key: self.access_key.clone(), - nonce: self.nonce, - cluster: self.cluster.clone() - } - } - - pub fn disseminate_jobs(&self, consensus_session: &mut SigningConsensusSession, version: &H256, nonce_public: Public, inv_nonce_share: Secret, inversed_nonce_coeff: Secret, message_hash: H256) -> Result<(), Error> { - let key_share = match self.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let key_version = key_share.version(version)?.hash.clone(); - let signing_job = EcdsaSigningJob::new_on_master(key_share.clone(), key_version, nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash)?; - consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false).map(|_| ()) - } -} - -impl JobTransport for SigningConsensusTransport { - type PartialJobRequest=Requester; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Requester) -> Result<(), Error> { - let version = self.version.as_ref() - .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: request.into(), - version: version.clone().into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - -impl JobTransport for SigningJobTransport { - type PartialJobRequest=EcdsaPartialSigningRequest; - type PartialJobResponse=EcdsaPartialSigningResponse; - - fn send_partial_request(&self, node: &NodeId, request: EcdsaPartialSigningRequest) -> Result<(), Error> { - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(EcdsaRequestPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: request.id.into(), - inversed_nonce_coeff: request.inversed_nonce_coeff.into(), - message_hash: request.message_hash.into(), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: EcdsaPartialSigningResponse) -> Result<(), Error> { - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(EcdsaPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: response.request_id.into(), - partial_signature_s: response.partial_signature_s.into(), - }))) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use ethereum_types::H256; - use crypto::publickey::{Random, Generator, Public, verify_public, public_to_address}; - use key_server_cluster::{SessionId, Error, KeyStorage}; - use key_server_cluster::cluster::tests::{MessageLoop as ClusterMessageLoop}; - use key_server_cluster::signing_session_ecdsa::SessionImpl; - use key_server_cluster::generation_session::tests::MessageLoop as GenerationMessageLoop; - use ServerKeyId; - - const DUMMY_SESSION_ID: [u8; 32] = [1u8; 32]; - - #[derive(Debug)] - pub struct MessageLoop(pub ClusterMessageLoop); - - impl MessageLoop { - pub fn new(num_nodes: usize, threshold: usize) -> Result { - let ml = GenerationMessageLoop::new(num_nodes).init(threshold)?; - ml.0.loop_until(|| ml.0.is_empty()); // complete generation session - - Ok(MessageLoop(ml.0)) - } - - pub fn init_with_version(self, key_version: Option) -> Result<(Self, Public, H256), Error> { - let message_hash = H256::random(); - let requester = Random.generate(); - let signature = crypto::publickey::sign(requester.secret(), &SessionId::from(DUMMY_SESSION_ID)).unwrap(); - self.0.cluster(0).client() - .new_ecdsa_signing_session(SessionId::from(DUMMY_SESSION_ID), signature.into(), key_version, message_hash) - .map(|_| (self, *requester.public(), message_hash)) - } - - pub fn init(self) -> Result<(Self, Public, H256), Error> { - let key_version = self.0.key_storage(0).get(&ServerKeyId::from(DUMMY_SESSION_ID)) - .unwrap().unwrap().versions.iter().last().unwrap().hash; - self.init_with_version(Some(key_version)) - } - - pub fn init_delegated(self) -> Result<(Self, Public, H256), Error> { - self.0.key_storage(0).remove(&ServerKeyId::from(DUMMY_SESSION_ID)).unwrap(); - self.init_with_version(None) - } - - pub fn init_with_isolated(self) -> Result<(Self, Public, H256), Error> { - self.0.isolate(1); - self.init() - } - - pub fn session_at(&self, idx: usize) -> Arc { - self.0.sessions(idx).ecdsa_signing_sessions.first().unwrap() - } - - pub fn ensure_completed(&self) { - self.0.loop_until(|| self.0.is_empty()); - assert!(self.session_at(0).wait().is_ok()); - } - } - - #[test] - fn failed_gen_ecdsa_sign_session_when_threshold_is_too_low() { - let test_cases = [(1, 2), (2, 4), (3, 6), (4, 6)]; - for &(threshold, num_nodes) in &test_cases { - assert_eq!(MessageLoop::new(num_nodes, threshold).unwrap().init().unwrap_err(), - Error::ConsensusUnreachable); - } - } - - #[test] - fn complete_gen_ecdsa_sign_session() { - let test_cases = [(0, 1), (2, 5), (2, 6), (3, 11), (4, 11)]; - for &(threshold, num_nodes) in &test_cases { - let (ml, _, message) = MessageLoop::new(num_nodes, threshold).unwrap().init().unwrap(); - ml.0.loop_until(|| ml.0.is_empty()); - - let signer_public = ml.0.key_storage(0).get(&ServerKeyId::from(DUMMY_SESSION_ID)).unwrap().unwrap().public; - let signature = ml.session_at(0).wait().unwrap(); - assert!(verify_public(&signer_public, &signature, &message).unwrap()); - } - } - - #[test] - fn ecdsa_complete_signing_session_with_single_node_failing() { - let (ml, requester, _) = MessageLoop::new(4, 1).unwrap().init().unwrap(); - - // we need at least 3-of-4 nodes to agree to reach consensus - // let's say 1 of 4 nodes disagee - ml.0.acl_storage(1).prohibit(public_to_address(&requester), ServerKeyId::from(DUMMY_SESSION_ID)); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn ecdsa_complete_signing_session_with_acl_check_failed_on_master() { - let (ml, requester, _) = MessageLoop::new(4, 1).unwrap().init().unwrap(); - - // we need at least 3-of-4 nodes to agree to reach consensus - // let's say 1 of 4 nodes (here: master) disagee - ml.0.acl_storage(0).prohibit(public_to_address(&requester), ServerKeyId::from(DUMMY_SESSION_ID)); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn ecdsa_signing_works_when_delegated_to_other_node() { - MessageLoop::new(4, 1).unwrap().init_delegated().unwrap().0.ensure_completed(); - } - - #[test] - fn ecdsa_signing_works_when_share_owners_are_isolated() { - MessageLoop::new(6, 2).unwrap().init_with_isolated().unwrap().0.ensure_completed(); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs b/secret-store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs deleted file mode 100644 index 302fb8107..000000000 --- a/secret-store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs +++ /dev/null @@ -1,1126 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use crypto::publickey::{Public, Secret}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, SessionId, Requester, SessionMeta, AclStorage, DocumentKeyShare}; -use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - SessionState as GenerationSessionState}; -use key_server_cluster::message::{Message, SchnorrSigningMessage, SchnorrSigningConsensusMessage, SchnorrSigningGenerationMessage, - SchnorrRequestPartialSignature, SchnorrPartialSignature, SchnorrSigningSessionCompleted, GenerationMessage, - ConsensusMessage, SchnorrSigningSessionError, InitializeConsensusSession, ConfirmConsensusInitialization, - SchnorrSigningSessionDelegation, SchnorrSigningSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::signing_job_schnorr::{SchnorrPartialSigningRequest, SchnorrPartialSigningResponse, SchnorrSigningJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; - -/// Distributed Schnorr-signing session. -/// Based on "Efficient Multi-Party Digital Signature using Adaptive Secret Sharing for Low-Power Devices in Wireless Network" paper. -/// Brief overview: -/// 1) initialization: master node (which has received request for signing the message) requests all other nodes to sign the message -/// 2) ACL check: all nodes which have received the request are querying ACL-contract to check if requestor has access to the private key -/// 3) partial signing: every node which has succussfully checked access for the requestor do a partial signing -/// 4) signing: master node receives all partial signatures of the secret and computes the signature -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: SessionMeta, - /// Signing session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Session-level nonce. - pub nonce: u64, - /// SessionImpl completion signal. - pub completed: CompletionSignal<(Secret, Secret)>, -} - -/// Signing consensus session type. -type SigningConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Message hash. - pub message_hash: Option, - /// Key version to use for decryption. - pub version: Option, - /// Consensus-based signing session. - pub consensus_session: SigningConsensusSession, - /// Session key generation session. - pub generation_session: Option, - /// Delegation status. - pub delegation_status: Option, - /// Decryption result. - pub result: Option>, -} - -/// Signing session state. -#[derive(Debug, PartialEq)] -#[cfg_attr(test, derive(Clone, Copy))] -pub enum SessionState { - /// State when consensus is establishing. - ConsensusEstablishing, - /// State when session key is generating. - SessionKeyGeneration, - /// State when signature is computing. - SignatureComputing, -} - -/// Session creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: SessionMeta, - /// Session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// ACL storage. - pub acl_storage: Arc, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Signing consensus transport. -struct SigningConsensusTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Selected key version (on master node). - version: Option, - /// Cluster. - cluster: Arc, -} - -/// Signing key generation transport. -struct SessionKeyGenerationTransport { - /// Session access key. - access_key: Secret, - /// Cluster. - cluster: Arc, - /// Session-level nonce. - nonce: u64, - /// Other nodes ids. - other_nodes_ids: BTreeSet, -} - -/// Signing job transport -struct SigningJobTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -/// Session delegation status. -enum DelegationStatus { - /// Delegated to other node. - DelegatedTo(NodeId), - /// Delegated from other node. - DelegatedFrom(NodeId, u64), -} - -impl SessionImpl { - /// Create new signing session. - pub fn new( - params: SessionParams, - requester: Option, - ) -> Result<(Self, Oneshot>), Error> { - debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); - - let consensus_transport = SigningConsensusTransport { - id: params.meta.id.clone(), - access_key: params.access_key.clone(), - nonce: params.nonce, - version: None, - cluster: params.cluster.clone(), - }; - let consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: params.meta.clone(), - consensus_executor: match requester { - Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), - None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), - }, - consensus_transport: consensus_transport, - })?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - access_key: params.access_key, - key_share: params.key_share, - cluster: params.cluster, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::ConsensusEstablishing, - message_hash: None, - version: None, - consensus_session: consensus_session, - generation_session: None, - delegation_status: None, - result: None, - }), - }, oneshot)) - } - - /// Wait for session completion. - #[cfg(test)] - pub fn wait(&self) -> Result<(Secret, Secret), Error> { - Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) - .expect("wait_session returns Some if called without timeout; qed") - } - - /// Get session state (tests only). - #[cfg(test)] - pub fn state(&self) -> SessionState { - self.data.lock().state - } - - /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, version: H256, message_hash: H256) -> Result<(), Error> { - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(SchnorrSigningSessionDelegation { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - requester: data.consensus_session.consensus_job().executor().requester() - .expect("requester is passed to master node on creation; session can be delegated from master node only; qed") - .clone().into(), - version: version.into(), - message_hash: message_hash.into(), - })))?; - data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); - Ok(()) - - } - - /// Initialize signing session on master node. - pub fn initialize(&self, version: H256, message_hash: H256) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - // check if version exists - let key_version = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version)?, - }; - - let mut data = self.data.lock(); - let non_isolated_nodes = self.core.cluster.nodes(); - let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(*n)) - .cloned() - .chain(::std::iter::once(self.core.meta.self_node_id.clone())) - .collect(); - if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { - consensus_nodes.remove(&delegation_master); - } - - data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); - data.version = Some(version.clone()); - data.message_hash = Some(message_hash); - data.consensus_session.initialize(consensus_nodes)?; - - if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - let generation_session = GenerationSession::new(GenerationSessionParams { - id: self.core.meta.id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(SessionKeyGenerationTransport { - access_key: self.core.access_key.clone(), - cluster: self.core.cluster.clone(), - nonce: self.core.nonce, - other_nodes_ids: BTreeSet::new() - }), - nonce: None, - }).0; - generation_session.initialize(Default::default(), Default::default(), false, 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect::>().into())?; - - debug_assert_eq!(generation_session.state(), GenerationSessionState::Finished); - let joint_public_and_secret = generation_session - .joint_public_and_secret() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - data.generation_session = Some(generation_session); - data.state = SessionState::SignatureComputing; - - self.core.disseminate_jobs(&mut data.consensus_session, &version, joint_public_and_secret.0, joint_public_and_secret.1, message_hash)?; - - debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); - let result = data.consensus_session.result()?; - Self::set_signing_result(&self.core, &mut *data, Ok(result)); - } - - Ok(()) - } - - /// Process signing message. - pub fn process_message(&self, sender: &NodeId, message: &SchnorrSigningMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref message) => - self.on_generation_message(sender, message), - &SchnorrSigningMessage::SchnorrRequestPartialSignature(ref message) => - self.on_partial_signature_requested(sender, message), - &SchnorrSigningMessage::SchnorrPartialSignature(ref message) => - self.on_partial_signature(sender, message), - &SchnorrSigningMessage::SchnorrSigningSessionError(ref message) => - self.process_node_error(Some(&sender), message.error.clone()), - &SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref message) => - self.on_session_completed(sender, message), - &SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message) => - self.on_session_delegated(sender, message), - &SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref message) => - self.on_session_delegation_completed(sender, message), - } - } - - /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &SchnorrSigningSessionDelegation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - { - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_requester(message.requester.clone().into()); - data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); - } - - self.initialize(message.version.clone().into(), message.message_hash.clone().into()) - } - - /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &SchnorrSigningSessionDelegationCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), - _ => return Err(Error::InvalidMessage), - } - - Self::set_signing_result(&self.core, &mut *data, Ok((message.signature_c.clone().into(), message.signature_s.clone().into()))); - - Ok(()) - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &SchnorrSigningConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - - if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { - let version = msg.version.clone().into(); - let has_key_share = self.core.key_share.as_ref() - .map(|ks| ks.version(&version).is_ok()) - .unwrap_or(false); - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); - data.version = Some(version); - } - data.consensus_session.on_consensus_message(&sender, &message.message)?; - - let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let generation_session = GenerationSession::new(GenerationSessionParams { - id: self.core.meta.id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(SessionKeyGenerationTransport { - access_key: self.core.access_key.clone(), - cluster: self.core.cluster.clone(), - nonce: self.core.nonce, - other_nodes_ids: other_consensus_group_nodes, - }), - nonce: None, - }).0; - - generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group.into())?; - data.generation_session = Some(generation_session); - data.state = SessionState::SessionKeyGeneration; - - Ok(()) - } - - /// When session key related message is received. - pub fn on_generation_message(&self, sender: &NodeId, message: &SchnorrSigningGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - let generation_session = GenerationSession::new(GenerationSessionParams { - id: self.core.meta.id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(SessionKeyGenerationTransport { - access_key: self.core.access_key.clone(), - cluster: self.core.cluster.clone(), - nonce: self.core.nonce, - other_nodes_ids: other_consensus_group_nodes - }), - nonce: None, - }).0; - data.generation_session = Some(generation_session); - data.state = SessionState::SessionKeyGeneration; - } - - { - let generation_session = data.generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - data.state = SessionState::SignatureComputing; - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Ok(()); - } - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let message_hash = data.message_hash - .expect("we are on master node; on master node message_hash is filled in initialize(); on_generation_message follows initialize; qed"); - let joint_public_and_secret = data.generation_session.as_ref() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") - .joint_public_and_secret() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - self.core.disseminate_jobs(&mut data.consensus_session, &version, joint_public_and_secret.0, joint_public_and_secret.1, message_hash) - } - - /// When partial signature is requested. - pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &SchnorrRequestPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let mut data = self.data.lock(); - - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if data.state != SessionState::SignatureComputing { - return Err(Error::InvalidStateForRequest); - } - - let joint_public_and_secret = data.generation_session.as_ref() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") - .joint_public_and_secret() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?)?.hash.clone(); - let signing_job = SchnorrSigningJob::new_on_slave(self.core.meta.self_node_id.clone(), key_share.clone(), key_version, joint_public_and_secret.0, joint_public_and_secret.1)?; - let signing_transport = self.core.signing_transport(); - - data.consensus_session.on_job_request(sender, SchnorrPartialSigningRequest { - id: message.request_id.clone().into(), - message_hash: message.message_hash.clone().into(), - other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), - }, signing_job, signing_transport).map(|_| ()) - } - - /// When partial signature is received. - pub fn on_partial_signature(&self, sender: &NodeId, message: &SchnorrPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - data.consensus_session.on_job_response(sender, SchnorrPartialSigningResponse { - request_id: message.request_id.clone().into(), - partial_signature: message.partial_signature.clone().into(), - })?; - - if data.consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } - - // send compeltion signal to all nodes, except for rejected nodes - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(SchnorrSigningSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } - - let result = data.consensus_session.result()?; - Self::set_signing_result(&self.core, &mut *data, Ok(result)); - - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &SchnorrSigningSessionCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - self.data.lock().consensus_session.on_session_completed(sender) - } - - /// Process error from the other node. - fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { - let mut data = self.data.lock(); - let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); - // error is always fatal if coming from this node - if is_self_node_error { - Self::set_signing_result(&self.core, &mut *data, Err(error.clone())); - return Err(error); - } - - match { - match node { - Some(node) => data.consensus_session.on_node_error(node, error.clone()), - None => data.consensus_session.on_session_timeout(), - } - } { - Ok(false) => { - Ok(()) - }, - Ok(true) => { - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let message_hash = data.message_hash.as_ref().cloned() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed"); - let joint_public_and_secret = data.generation_session.as_ref() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed") - .joint_public_and_secret() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed")?; - let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, &version, joint_public_and_secret.0, joint_public_and_secret.1, message_hash); - match disseminate_result { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - } - } - }, - Err(err) => { - warn!("{}: signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - }, - } - } - - /// Set signing session result. - fn set_signing_result(core: &SessionCore, data: &mut SessionData, result: Result<(Secret, Secret), Error>) { - if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { - // error means can't communicate => ignore it - let _ = match result.as_ref() { - Ok(signature) => core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(SchnorrSigningSessionDelegationCompleted { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - signature_c: signature.0.clone().into(), - signature_s: signature.1.clone().into(), - }))), - Err(error) => core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(SchnorrSigningSessionError { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - error: error.clone().into(), - }))), - }; - } - - data.result = Some(result.clone()); - core.completed.send(result); - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionIdWithSubSession; - type CreationData = Requester; - type SuccessfulResult = (Secret, Secret); - - fn type_name() -> &'static str { - "signing" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.consensus_session.state() == ConsensusSessionState::Failed - || data.consensus_session.state() == ConsensusSessionState::Finished - || data.result.is_some() - } - - fn on_node_timeout(&self, node: &NodeId) { - // ignore error, only state matters - let _ = self.process_node_error(Some(node), Error::NodeDisconnected); - } - - fn on_session_timeout(&self) { - // ignore error, only state matters - let _ = self.process_node_error(None, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); - let is_this_node_error = *node == self.core.meta.self_node_id; - if is_fatal || is_this_node_error { - // error in signing session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - let message = Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(SchnorrSigningSessionError { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - - // do not bother processing send error, as we already processing error - let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { - self.core.cluster.broadcast(message) - } else { - self.core.cluster.send(&self.core.meta.master_node_id, message) - }; - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::SchnorrSigning(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl SessionKeyGenerationTransport { - fn map_message(&self, message: Message) -> Result { - match message { - Message::Generation(message) => Ok(Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage { - session: message.session_id().clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: message, - }))), - _ => Err(Error::InvalidMessage), - } - } -} - -impl Cluster for SessionKeyGenerationTransport { - fn broadcast(&self, message: Message) -> Result<(), Error> { - let message = self.map_message(message)?; - for to in &self.other_nodes_ids { - self.cluster.send(to, message.clone())?; - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - debug_assert!(self.other_nodes_ids.contains(to)); - self.cluster.send(to, self.map_message(message)?) - } - - fn is_connected(&self, node: &NodeId) -> bool { - self.cluster.is_connected(node) - } - - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn configured_nodes_count(&self) -> usize { - self.cluster.configured_nodes_count() - } - - fn connected_nodes_count(&self) -> usize { - self.cluster.connected_nodes_count() - } -} - -impl SessionCore { - pub fn signing_transport(&self) -> SigningJobTransport { - SigningJobTransport { - id: self.meta.id.clone(), - access_key: self.access_key.clone(), - nonce: self.nonce, - cluster: self.cluster.clone() - } - } - - pub fn disseminate_jobs(&self, consensus_session: &mut SigningConsensusSession, version: &H256, session_public: Public, session_secret_share: Secret, message_hash: H256) -> Result<(), Error> { - let key_share = match self.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let key_version = key_share.version(version)?.hash.clone(); - let signing_job = SchnorrSigningJob::new_on_master(self.meta.self_node_id.clone(), key_share.clone(), key_version, - session_public, session_secret_share, message_hash)?; - consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false).map(|_| ()) - } -} - -impl JobTransport for SigningConsensusTransport { - type PartialJobRequest=Requester; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Requester) -> Result<(), Error> { - let version = self.version.as_ref() - .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: request.into(), - version: version.clone().into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - -impl JobTransport for SigningJobTransport { - type PartialJobRequest=SchnorrPartialSigningRequest; - type PartialJobResponse=SchnorrPartialSigningResponse; - - fn send_partial_request(&self, node: &NodeId, request: SchnorrPartialSigningRequest) -> Result<(), Error> { - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(SchnorrRequestPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: request.id.into(), - message_hash: request.message_hash.into(), - nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: SchnorrPartialSigningResponse) -> Result<(), Error> { - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(SchnorrPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: response.request_id.into(), - partial_signature: response.partial_signature.into(), - }))) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::str::FromStr; - use std::collections::BTreeMap; - use ethereum_types::{Address, H256}; - use crypto::publickey::{Random, Generator, Public, Secret, public_to_address}; - use acl_storage::DummyAclStorage; - use key_server_cluster::{SessionId, Requester, SessionMeta, Error, KeyStorage}; - use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; - use key_server_cluster::generation_session::tests::MessageLoop as GenerationMessageLoop; - use key_server_cluster::math; - use key_server_cluster::message::{SchnorrSigningMessage, SchnorrSigningConsensusMessage, - ConsensusMessage, ConfirmConsensusInitialization, SchnorrSigningGenerationMessage, GenerationMessage, - ConfirmInitialization, InitializeSession, SchnorrRequestPartialSignature}; - use key_server_cluster::signing_session_schnorr::{SessionImpl, SessionState, SessionParams}; - - #[derive(Debug)] - pub struct MessageLoop(pub ClusterMessageLoop); - - impl MessageLoop { - pub fn new(num_nodes: usize, threshold: usize) -> Result { - let ml = GenerationMessageLoop::new(num_nodes).init(threshold)?; - ml.0.loop_until(|| ml.0.is_empty()); // complete generation session - - Ok(MessageLoop(ml.0)) - } - - pub fn into_session(&self, at_node: usize) -> SessionImpl { - let requester = Some(Requester::Signature( - crypto::publickey::sign(Random.generate().secret(), &SessionId::from([1u8; 32])).unwrap()) - ); - let dummy_doc = [1u8; 32].into(); - SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::from([1u8; 32]), - self_node_id: self.0.node(at_node), - master_node_id: self.0.node(0), - threshold: self.0.key_storage(at_node).get(&dummy_doc).unwrap().unwrap().threshold, - configured_nodes_count: self.0.nodes().len(), - connected_nodes_count: self.0.nodes().len(), - }, - access_key: Random.generate().secret().clone(), - key_share: self.0.key_storage(at_node).get(&dummy_doc).unwrap(), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: self.0.cluster(0).view().unwrap(), - nonce: 0, - }, requester).unwrap().0 - } - - pub fn init_with_version(self, key_version: Option) -> Result<(Self, Public, H256), Error> { - let message_hash = H256::random(); - let requester = Random.generate(); - let signature = crypto::publickey::sign(requester.secret(), &SessionId::from([1u8; 32])).unwrap(); - self.0.cluster(0).client().new_schnorr_signing_session( - SessionId::from([1u8; 32]), - signature.into(), - key_version, - message_hash).map(|_| (self, *requester.public(), message_hash) - ) - } - - pub fn init(self) -> Result<(Self, Public, H256), Error> { - let key_version = self.key_version(); - self.init_with_version(Some(key_version)) - } - - pub fn init_delegated(self) -> Result<(Self, Public, H256), Error> { - let doc = [1u8; 32].into(); - self.0.key_storage(0).remove(&doc).unwrap(); - self.init_with_version(None) - } - - pub fn init_with_isolated(self) -> Result<(Self, Public, H256), Error> { - self.0.isolate(1); - self.init() - } - - pub fn init_without_share(self) -> Result<(Self, Public, H256), Error> { - let key_version = self.key_version(); - let doc = [1u8; 32].into(); - self.0.key_storage(0).remove(&doc).unwrap(); - self.init_with_version(Some(key_version)) - } - - pub fn session_at(&self, idx: usize) -> Arc { - self.0.sessions(idx).schnorr_signing_sessions.first().unwrap() - } - - pub fn ensure_completed(&self) { - self.0.loop_until(|| self.0.is_empty()); - assert!(self.session_at(0).wait().is_ok()); - } - - pub fn key_version(&self) -> H256 { - let doc = [1u8; 32].into(); - self.0.key_storage(0).get(&doc) - .unwrap().unwrap().versions.iter().last().unwrap().hash - } - } - - #[test] - fn schnorr_complete_gen_sign_session() { - let test_cases = [(0, 1), (0, 5), (2, 5), (3, 5)]; - for &(threshold, num_nodes) in &test_cases { - let (ml, _, message) = MessageLoop::new(num_nodes, threshold).unwrap().init().unwrap(); - ml.0.loop_until(|| ml.0.is_empty()); - - let doc = [1u8; 32].into(); - let signer_public = ml.0.key_storage(0).get(&doc).unwrap().unwrap().public; - let signature = ml.session_at(0).wait().unwrap(); - assert!(math::verify_schnorr_signature(&signer_public, &signature, &message).unwrap()); - } - } - - #[test] - fn schnorr_constructs_in_cluster_of_single_node() { - MessageLoop::new(1, 0).unwrap().init().unwrap(); - } - - #[test] - fn schnorr_fails_to_initialize_if_does_not_have_a_share() { - assert!(MessageLoop::new(2, 1).unwrap().init_without_share().is_err()); - } - - #[test] - fn schnorr_fails_to_initialize_if_threshold_is_wrong() { - let mut ml = MessageLoop::new(3, 2).unwrap(); - ml.0.exclude(2); - assert_eq!(ml.init().unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn schnorr_fails_to_initialize_when_already_initialized() { - let (ml, _, _) = MessageLoop::new(1, 0).unwrap().init().unwrap(); - assert_eq!(ml.session_at(0).initialize(ml.key_version(), H256::from_low_u64_be(777)), - Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_does_not_fail_when_consensus_message_received_after_consensus_established() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // consensus is established - let session = ml.session_at(0); - ml.0.loop_until(|| session.state() == SessionState::SessionKeyGeneration); - - // but 3rd node continues to send its messages - // this should not fail session - let consensus_group = session.data.lock().consensus_session.select_consensus_group().unwrap().clone(); - let mut had_3rd_message = false; - while let Some((from, to, message)) = ml.0.take_message() { - if !consensus_group.contains(&from) { - had_3rd_message = true; - ml.0.process_message(from, to, message); - } - } - assert!(had_3rd_message); - } - - #[test] - fn schnorr_fails_when_consensus_message_is_received_when_not_initialized() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(0); - assert_eq!(session.on_consensus_message(&ml.0.node(1), &SchnorrSigningConsensusMessage { - session: SessionId::from([1u8; 32]).into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - }), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_fails_when_generation_message_is_received_when_not_initialized() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(0); - assert_eq!(session.on_generation_message(&ml.0.node(1), &SchnorrSigningGenerationMessage { - session: SessionId::from([1u8; 32]).into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { - session: SessionId::from([1u8; 32]).into(), - session_nonce: 0, - derived_point: Public::default().into(), - }), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_fails_when_generation_sesson_is_initialized_by_slave_node() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - let session = ml.session_at(0); - ml.0.loop_until(|| session.state() == SessionState::SessionKeyGeneration); - - let slave2_id = ml.0.node(2); - let slave1_session = ml.session_at(1); - - assert_eq!(slave1_session.on_generation_message(&slave2_id, &SchnorrSigningGenerationMessage { - session: SessionId::from([1u8; 32]).into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - message: GenerationMessage::InitializeSession(InitializeSession { - session: SessionId::from([1u8; 32]).into(), - session_nonce: 0, - origin: None, - author: Address::zero().into(), - nodes: BTreeMap::new(), - is_zero: false, - threshold: 1, - derived_point: Public::default().into(), - }) - }), Err(Error::InvalidMessage)); - } - - #[test] - fn schnorr_fails_when_signature_requested_when_not_initialized() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(1); - assert_eq!(session.on_partial_signature_requested(&ml.0.node(0), &SchnorrRequestPartialSignature { - session: SessionId::from([1u8; 32]).into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), - message_hash: H256::zero().into(), - nodes: Default::default(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_fails_when_signature_requested_by_slave_node() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(0); - assert_eq!(session.on_partial_signature_requested(&ml.0.node(1), &SchnorrRequestPartialSignature { - session: SessionId::from([1u8; 32]).into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), - message_hash: H256::zero().into(), - nodes: Default::default(), - }), Err(Error::InvalidMessage)); - } - - #[test] - fn schnorr_failed_signing_session() { - let (ml, requester, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // we need at least 2-of-3 nodes to agree to reach consensus - // let's say 2 of 3 nodes disagee - ml.0.acl_storage(1).prohibit(public_to_address(&requester), SessionId::from([1u8; 32])); - ml.0.acl_storage(2).prohibit(public_to_address(&requester), SessionId::from([1u8; 32])); - - // then consensus is unreachable - ml.0.loop_until(|| ml.0.is_empty()); - assert_eq!(ml.session_at(0).wait().unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn schnorr_complete_signing_session_with_single_node_failing() { - let (ml, requester, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // we need at least 2-of-3 nodes to agree to reach consensus - // let's say 1 of 3 nodes disagree - ml.0.acl_storage(1).prohibit(public_to_address(&requester), SessionId::from([1u8; 32])); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn schnorr_complete_signing_session_with_acl_check_failed_on_master() { - let (ml, requester, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // we need at least 2-of-3 nodes to agree to reach consensus - // let's say 1 of 3 nodes disagree - ml.0.acl_storage(0).prohibit(public_to_address(&requester), SessionId::from([1u8; 32])); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn schnorr_signing_message_fails_when_nonce_is_wrong() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(1); - let msg = SchnorrSigningMessage::SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage { - session: SessionId::from([1u8; 32]).into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 10, - message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { - session: SessionId::from([1u8; 32]).into(), - session_nonce: 0, - derived_point: Public::default().into(), - }), - }); - assert_eq!(session.process_message(&ml.0.node(1), &msg), Err(Error::ReplayProtection)); - } - - #[test] - fn schnorr_signing_works_when_delegated_to_other_node() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init_delegated().unwrap(); - ml.ensure_completed(); - } - - #[test] - fn schnorr_signing_works_when_share_owners_are_isolated() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init_with_isolated().unwrap(); - ml.ensure_completed(); - } -} diff --git a/secret-store/src/key_server_cluster/cluster.rs b/secret-store/src/key_server_cluster/cluster.rs deleted file mode 100644 index 4d6c3f5fe..000000000 --- a/secret-store/src/key_server_cluster/cluster.rs +++ /dev/null @@ -1,1266 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeMap, BTreeSet}; -use parking_lot::RwLock; -use crypto::publickey::{Public, Signature, Random, Generator}; -use ethereum_types::{Address, H256}; -use parity_runtime::Executor; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet}; -use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, AdminSession, ClusterSessions, - SessionIdWithSubSession, ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view, - AdminSessionCreationData, ClusterSessionsListener}; -use key_server_cluster::cluster_sessions_creator::ClusterSessionCreator; -use key_server_cluster::cluster_connections::{ConnectionProvider, ConnectionManager}; -use key_server_cluster::cluster_connections_net::{NetConnectionsManager, - NetConnectionsContainer, NetConnectionsManagerConfig}; -use key_server_cluster::cluster_message_processor::{MessageProcessor, SessionsMessageProcessor}; -use key_server_cluster::message::Message; -use key_server_cluster::generation_session::{SessionImpl as GenerationSession}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSession}; -use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSession}; -use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSession}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationSessionTransport, ContinueAction}; -use key_server_cluster::connection_trigger::{ConnectionTrigger, - SimpleConnectionTrigger, ServersSetChangeSessionCreatorConnector}; -use key_server_cluster::connection_trigger_with_migration::ConnectionTriggerWithMigration; - -#[cfg(test)] -use key_server_cluster::cluster_connections::tests::{MessagesQueue, TestConnections, new_test_connections}; - -/// Cluster interface for external clients. -pub trait ClusterClient: Send + Sync { - /// Start new generation session. - fn new_generation_session( - &self, - session_id: SessionId, - origin: Option
, - author: Address, - threshold: usize, - ) -> Result, Error>; - /// Start new encryption session. - fn new_encryption_session( - &self, - session_id: SessionId, - author: Requester, - common_point: Public, - encrypted_point: Public, - ) -> Result, Error>; - /// Start new decryption session. - fn new_decryption_session( - &self, - session_id: SessionId, - origin: Option
, - requester: Requester, - version: Option, - is_shadow_decryption: bool, - is_broadcast_decryption: bool, - ) -> Result, Error>; - /// Start new Schnorr signing session. - fn new_schnorr_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error>; - /// Start new ECDSA session. - fn new_ecdsa_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error>; - /// Start new key version negotiation session. - fn new_key_version_negotiation_session( - &self, - session_id: SessionId, - ) -> Result>, Error>; - /// Start new servers set change session. - fn new_servers_set_change_session( - &self, - session_id: Option, - migration_id: Option, - new_nodes_set: BTreeSet, - old_set_signature: Signature, - new_set_signature: Signature, - ) -> Result, Error>; - - /// Listen for new generation sessions. - fn add_generation_listener(&self, listener: Arc>); - /// Listen for new decryption sessions. - fn add_decryption_listener(&self, listener: Arc>); - /// Listen for new key version negotiation sessions. - fn add_key_version_negotiation_listener(&self, listener: Arc>>); - - /// Ask node to make 'faulty' generation sessions. - #[cfg(test)] - fn make_faulty_generation_sessions(&self); - /// Get active generation session with given id. - #[cfg(test)] - fn generation_session(&self, session_id: &SessionId) -> Option>; - #[cfg(test)] - fn is_fully_connected(&self) -> bool; - /// Try connect to disconnected nodes. - #[cfg(test)] - fn connect(&self); -} - -/// Cluster access for single session participant. -pub trait Cluster: Send + Sync { - /// Broadcast message to all other nodes. - fn broadcast(&self, message: Message) -> Result<(), Error>; - /// Send message to given node. - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error>; - /// Is connected to given node? - fn is_connected(&self, node: &NodeId) -> bool; - /// Get a set of connected nodes. - fn nodes(&self) -> BTreeSet; - /// Get total count of configured key server nodes (valid at the time of ClusterView creation). - fn configured_nodes_count(&self) -> usize; - /// Get total count of connected key server nodes (valid at the time of ClusterView creation). - fn connected_nodes_count(&self) -> usize; -} - -/// Cluster initialization parameters. -#[derive(Clone)] -pub struct ClusterConfiguration { - /// KeyPair this node holds. - pub self_key_pair: Arc, - /// Cluster nodes set. - pub key_server_set: Arc, - /// Reference to key storage - pub key_storage: Arc, - /// Reference to ACL storage - pub acl_storage: Arc, - /// Administrator public key. - pub admin_public: Option, - /// Do not remove sessions from container. - pub preserve_sessions: bool, -} - -/// Network cluster implementation. -pub struct ClusterCore { - /// Cluster data. - data: Arc>, -} - -/// Network cluster client interface implementation. -pub struct ClusterClientImpl { - /// Cluster data. - data: Arc>, -} - -/// Network cluster view. It is a communication channel, required in single session. -pub struct ClusterView { - configured_nodes_count: usize, - connected_nodes: BTreeSet, - connections: Arc, - self_key_pair: Arc, -} - -/// Cross-thread shareable cluster data. -pub struct ClusterData { - /// Cluster configuration. - pub config: ClusterConfiguration, - /// KeyPair this node holds. - pub self_key_pair: Arc, - /// Connections data. - pub connections: C, - /// Active sessions data. - pub sessions: Arc, - // Messages processor. - pub message_processor: Arc, - /// Link between servers set chnage session and the connections manager. - pub servers_set_change_creator_connector: Arc, -} - -/// Create new network-backed cluster. -pub fn new_network_cluster( - executor: Executor, - config: ClusterConfiguration, - net_config: NetConnectionsManagerConfig -) -> Result>, Error> { - let mut nodes = config.key_server_set.snapshot().current_set; - let is_isolated = nodes.remove(config.self_key_pair.public()).is_none(); - let connections_data = Arc::new(RwLock::new(NetConnectionsContainer { - is_isolated, - nodes, - connections: BTreeMap::new(), - })); - - let connection_trigger: Box = match net_config.auto_migrate_enabled { - false => Box::new(SimpleConnectionTrigger::with_config(&config)), - true if config.admin_public.is_none() => Box::new(ConnectionTriggerWithMigration::with_config(&config)), - true => return Err(Error::Internal( - "secret store admininstrator public key is specified with auto-migration enabled".into() - )), - }; - - let servers_set_change_creator_connector = connection_trigger.servers_set_change_creator_connector(); - let sessions = Arc::new(ClusterSessions::new(&config, servers_set_change_creator_connector.clone())); - let message_processor = Arc::new(SessionsMessageProcessor::new( - config.self_key_pair.clone(), - servers_set_change_creator_connector.clone(), - sessions.clone(), - connections_data.clone())); - - let connections = NetConnectionsManager::new( - executor, - message_processor.clone(), - connection_trigger, - connections_data, - &config, - net_config)?; - connections.start()?; - - ClusterCore::new(sessions, message_processor, connections, servers_set_change_creator_connector, config) -} - -/// Create new in-memory backed cluster -#[cfg(test)] -pub fn new_test_cluster( - messages: MessagesQueue, - config: ClusterConfiguration, -) -> Result>>, Error> { - let nodes = config.key_server_set.snapshot().current_set; - let connections = new_test_connections(messages, *config.self_key_pair.public(), nodes.keys().cloned().collect()); - - let connection_trigger = Box::new(SimpleConnectionTrigger::with_config(&config)); - let servers_set_change_creator_connector = connection_trigger.servers_set_change_creator_connector(); - let mut sessions = ClusterSessions::new(&config, servers_set_change_creator_connector.clone()); - if config.preserve_sessions { - sessions.preserve_sessions(); - } - let sessions = Arc::new(sessions); - - let message_processor = Arc::new(SessionsMessageProcessor::new( - config.self_key_pair.clone(), - servers_set_change_creator_connector.clone(), - sessions.clone(), - connections.provider(), - )); - - ClusterCore::new(sessions, message_processor, connections, servers_set_change_creator_connector, config) -} - -impl ClusterCore { - pub fn new( - sessions: Arc, - message_processor: Arc, - connections: C, - servers_set_change_creator_connector: Arc, - config: ClusterConfiguration, - ) -> Result, Error> { - Ok(Arc::new(ClusterCore { - data: Arc::new(ClusterData { - self_key_pair: config.self_key_pair.clone(), - connections, - sessions: sessions.clone(), - config, - message_processor, - servers_set_change_creator_connector - }), - })) - } - - /// Create new client interface. - pub fn client(&self) -> Arc { - Arc::new(ClusterClientImpl::new(self.data.clone())) - } - - /// Run cluster. - pub fn run(&self) -> Result<(), Error> { - self.data.connections.connect(); - Ok(()) - } - - #[cfg(test)] - pub fn view(&self) -> Result, Error> { - let connections = self.data.connections.provider(); - let mut connected_nodes = connections.connected_nodes()?; - let disconnected_nodes = connections.disconnected_nodes(); - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let connected_nodes_count = connected_nodes.len(); - let disconnected_nodes_count = disconnected_nodes.len(); - Ok(Arc::new(ClusterView::new( - self.data.self_key_pair.clone(), - connections, - connected_nodes, - connected_nodes_count + disconnected_nodes_count))) - } -} - -impl ClusterView { - pub fn new( - self_key_pair: Arc, - connections: Arc, - nodes: BTreeSet, - configured_nodes_count: usize - ) -> Self { - ClusterView { - configured_nodes_count: configured_nodes_count, - connected_nodes: nodes, - connections, - self_key_pair, - } - } -} - -impl Cluster for ClusterView { - fn broadcast(&self, message: Message) -> Result<(), Error> { - for node in self.connected_nodes.iter().filter(|n| *n != self.self_key_pair.public()) { - trace!(target: "secretstore_net", "{}: sent message {} to {}", self.self_key_pair.public(), message, node); - let connection = self.connections.connection(node).ok_or(Error::NodeDisconnected)?; - connection.send_message(message.clone()); - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - trace!(target: "secretstore_net", "{}: sent message {} to {}", self.self_key_pair.public(), message, to); - let connection = self.connections.connection(to).ok_or(Error::NodeDisconnected)?; - connection.send_message(message); - Ok(()) - } - - fn is_connected(&self, node: &NodeId) -> bool { - self.connected_nodes.contains(node) - } - - fn nodes(&self) -> BTreeSet { - self.connected_nodes.clone() - } - - fn configured_nodes_count(&self) -> usize { - self.configured_nodes_count - } - - fn connected_nodes_count(&self) -> usize { - self.connected_nodes.len() - } -} - -impl ClusterClientImpl { - pub fn new(data: Arc>) -> Self { - ClusterClientImpl { - data: data, - } - } - - fn create_key_version_negotiation_session( - &self, - session_id: SessionId, - ) -> Result>, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate().secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.negotiation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, None)?; - match session.session.initialize(connected_nodes) { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.negotiation_sessions.remove(&session.session.id()); - Err(error) - } - } - } -} - -impl ClusterClient for ClusterClientImpl { - fn new_generation_session( - &self, - session_id: SessionId, - origin: Option
, - author: Address, - threshold: usize, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), true)?; - let session = self.data.sessions.generation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - process_initialization_result( - session.session.initialize(origin, author, false, threshold, connected_nodes.into()), - session, &self.data.sessions.generation_sessions) - } - - fn new_encryption_session( - &self, - session_id: SessionId, - requester: Requester, - common_point: Public, - encrypted_point: Public, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), true)?; - let session = self.data.sessions.encryption_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - process_initialization_result( - session.session.initialize(requester, common_point, encrypted_point), - session, &self.data.sessions.encryption_sessions) - } - - fn new_decryption_session( - &self, - session_id: SessionId, - origin: Option
, - requester: Requester, - version: Option, - is_shadow_decryption: bool, - is_broadcast_decryption: bool, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate().secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.decryption_sessions.insert(cluster, self.data.self_key_pair.public().clone(), - session_id.clone(), None, false, Some(requester))?; - - let initialization_result = match version { - Some(version) => session.session.initialize(origin, version, is_shadow_decryption, is_broadcast_decryption), - None => { - self.create_key_version_negotiation_session(session_id.id.clone()) - .map(|version_session| { - let continue_action = ContinueAction::Decrypt( - session.session.clone(), - origin, - is_shadow_decryption, - is_broadcast_decryption, - ); - version_session.session.set_continue_action(continue_action); - self.data.message_processor.try_continue_session(Some(version_session.session)); - }) - }, - }; - - process_initialization_result( - initialization_result, - session, &self.data.sessions.decryption_sessions) - } - - fn new_schnorr_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate().secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.schnorr_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requester))?; - - let initialization_result = match version { - Some(version) => session.session.initialize(version, message_hash), - None => { - self.create_key_version_negotiation_session(session_id.id.clone()) - .map(|version_session| { - let continue_action = ContinueAction::SchnorrSign(session.session.clone(), message_hash); - version_session.session.set_continue_action(continue_action); - self.data.message_processor.try_continue_session(Some(version_session.session)); - }) - }, - }; - - process_initialization_result( - initialization_result, - session, &self.data.sessions.schnorr_signing_sessions) - } - - fn new_ecdsa_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate().secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.ecdsa_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requester))?; - - let initialization_result = match version { - Some(version) => session.session.initialize(version, message_hash), - None => { - self.create_key_version_negotiation_session(session_id.id.clone()) - .map(|version_session| { - let continue_action = ContinueAction::EcdsaSign(session.session.clone(), message_hash); - version_session.session.set_continue_action(continue_action); - self.data.message_processor.try_continue_session(Some(version_session.session)); - }) - }, - }; - - process_initialization_result( - initialization_result, - session, &self.data.sessions.ecdsa_signing_sessions) - } - - fn new_key_version_negotiation_session( - &self, - session_id: SessionId, - ) -> Result>, Error> { - self.create_key_version_negotiation_session(session_id) - } - - fn new_servers_set_change_session( - &self, - session_id: Option, - migration_id: Option, - new_nodes_set: BTreeSet, - old_set_signature: Signature, - new_set_signature: Signature, - ) -> Result, Error> { - new_servers_set_change_session( - self.data.self_key_pair.clone(), - &self.data.sessions, - self.data.connections.provider(), - self.data.servers_set_change_creator_connector.clone(), - ServersSetChangeParams { - session_id, - migration_id, - new_nodes_set, - old_set_signature, - new_set_signature, - }) - } - - fn add_generation_listener(&self, listener: Arc>) { - self.data.sessions.generation_sessions.add_listener(listener); - } - - fn add_decryption_listener(&self, listener: Arc>) { - self.data.sessions.decryption_sessions.add_listener(listener); - } - - fn add_key_version_negotiation_listener(&self, listener: Arc>>) { - self.data.sessions.negotiation_sessions.add_listener(listener); - } - - #[cfg(test)] - fn make_faulty_generation_sessions(&self) { - self.data.sessions.make_faulty_generation_sessions(); - } - - #[cfg(test)] - fn generation_session(&self, session_id: &SessionId) -> Option> { - self.data.sessions.generation_sessions.get(session_id, false) - } - - #[cfg(test)] - fn is_fully_connected(&self) -> bool { - self.data.connections.provider().disconnected_nodes().is_empty() - } - - #[cfg(test)] - fn connect(&self) { - self.data.connections.connect() - } -} - -pub struct ServersSetChangeParams { - pub session_id: Option, - pub migration_id: Option, - pub new_nodes_set: BTreeSet, - pub old_set_signature: Signature, - pub new_set_signature: Signature, -} - -pub fn new_servers_set_change_session( - self_key_pair: Arc, - sessions: &ClusterSessions, - connections: Arc, - servers_set_change_creator_connector: Arc, - params: ServersSetChangeParams, -) -> Result, Error> { - let session_id = match params.session_id { - Some(session_id) if session_id == *SERVERS_SET_CHANGE_SESSION_ID => session_id, - Some(_) => return Err(Error::InvalidMessage), - None => *SERVERS_SET_CHANGE_SESSION_ID, - }; - - let cluster = create_cluster_view(self_key_pair.clone(), connections, true)?; - let creation_data = AdminSessionCreationData::ServersSetChange(params.migration_id, params.new_nodes_set.clone()); - let session = sessions.admin_sessions - .insert(cluster, *self_key_pair.public(), session_id, None, true, Some(creation_data))?; - let initialization_result = session.session.as_servers_set_change().expect("servers set change session is created; qed") - .initialize(params.new_nodes_set, params.old_set_signature, params.new_set_signature); - - if initialization_result.is_ok() { - servers_set_change_creator_connector.set_key_servers_set_change_session(session.session.clone()); - } - - process_initialization_result( - initialization_result, - session, &sessions.admin_sessions) -} - -fn process_initialization_result( - result: Result<(), Error>, - session: WaitableSession, - sessions: &ClusterSessionsContainer -) -> Result, Error> - where - S: ClusterSession, - SC: ClusterSessionCreator -{ - match result { - Ok(()) if session.session.is_finished() => { - sessions.remove(&session.session.id()); - Ok(session) - }, - Ok(()) => Ok(session), - Err(error) => { - sessions.remove(&session.session.id()); - Err(error) - }, - } -} - -#[cfg(test)] -pub mod tests { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::collections::{BTreeMap, BTreeSet, VecDeque}; - use futures::Future; - use parking_lot::{Mutex, RwLock}; - use ethereum_types::{Address, H256}; - use crypto::publickey::{Random, Generator, Public, Signature, sign}; - use blockchain::SigningKeyPair; - use key_server_cluster::{NodeId, SessionId, Requester, Error, DummyAclStorage, DummyKeyStorage, - MapKeyServerSet, PlainNodeKeyPair}; - use key_server_cluster::message::Message; - use key_server_cluster::cluster::{new_test_cluster, Cluster, ClusterCore, ClusterConfiguration, ClusterClient}; - use key_server_cluster::cluster_connections::ConnectionManager; - use key_server_cluster::cluster_connections::tests::{MessagesQueue, TestConnections}; - use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, ClusterSessions, AdminSession, - ClusterSessionsListener}; - use key_server_cluster::generation_session::{SessionImpl as GenerationSession, - SessionState as GenerationSessionState}; - use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; - use key_server_cluster::encryption_session::{SessionImpl as EncryptionSession}; - use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSession}; - use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSession}; - use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationSessionTransport}; - - #[derive(Default)] - pub struct DummyClusterClient { - pub generation_requests_count: AtomicUsize, - } - - #[derive(Debug)] - pub struct DummyCluster { - id: NodeId, - data: RwLock, - } - - #[derive(Debug, Default)] - struct DummyClusterData { - nodes: BTreeSet, - messages: VecDeque<(NodeId, Message)>, - } - - impl ClusterClient for DummyClusterClient { - fn new_generation_session( - &self, - _session_id: SessionId, - _origin: Option
, - _author: Address, - _threshold: usize, - ) -> Result, Error> { - self.generation_requests_count.fetch_add(1, Ordering::Relaxed); - Err(Error::Internal("test-error".into())) - } - fn new_encryption_session( - &self, - _session_id: SessionId, - _requester: Requester, - _common_point: Public, - _encrypted_point: Public, - ) -> Result, Error> { - unimplemented!("test-only") - } - fn new_decryption_session( - &self, - _session_id: SessionId, - _origin: Option
, - _requester: Requester, - _version: Option, - _is_shadow_decryption: bool, - _is_broadcast_session: bool, - ) -> Result, Error> { - unimplemented!("test-only") - } - fn new_schnorr_signing_session( - &self, - _session_id: SessionId, - _requester: Requester, - _version: Option, - _message_hash: H256, - ) -> Result, Error> { - unimplemented!("test-only") - } - fn new_ecdsa_signing_session( - &self, - _session_id: SessionId, - _requester: Requester, - _version: Option, - _message_hash: H256, - ) -> Result, Error> { - unimplemented!("test-only") - } - - fn new_key_version_negotiation_session( - &self, - _session_id: SessionId, - ) -> Result>, Error> { - unimplemented!("test-only") - } - fn new_servers_set_change_session( - &self, - _session_id: Option, - _migration_id: Option, - _new_nodes_set: BTreeSet, - _old_set_signature: Signature, - _new_set_signature: Signature, - ) -> Result, Error> { - unimplemented!("test-only") - } - - fn add_generation_listener(&self, _listener: Arc>) {} - fn add_decryption_listener(&self, _listener: Arc>) {} - fn add_key_version_negotiation_listener(&self, _listener: Arc>>) {} - - fn make_faulty_generation_sessions(&self) { unimplemented!("test-only") } - fn generation_session(&self, _session_id: &SessionId) -> Option> { unimplemented!("test-only") } - fn is_fully_connected(&self) -> bool { true } - fn connect(&self) {} - } - - impl DummyCluster { - pub fn new(id: NodeId) -> Self { - DummyCluster { - id: id, - data: RwLock::new(DummyClusterData::default()) - } - } - - pub fn node(&self) -> NodeId { - self.id.clone() - } - - pub fn add_node(&self, node: NodeId) { - self.data.write().nodes.insert(node); - } - - pub fn add_nodes>(&self, nodes: I) { - self.data.write().nodes.extend(nodes) - } - - pub fn remove_node(&self, node: &NodeId) { - self.data.write().nodes.remove(node); - } - - pub fn take_message(&self) -> Option<(NodeId, Message)> { - self.data.write().messages.pop_front() - } - } - - impl Cluster for DummyCluster { - fn broadcast(&self, message: Message) -> Result<(), Error> { - let mut data = self.data.write(); - let all_nodes: Vec<_> = data.nodes.iter().cloned().filter(|n| n != &self.id).collect(); - for node in all_nodes { - data.messages.push_back((node, message.clone())); - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - debug_assert!(&self.id != to); - self.data.write().messages.push_back((to.clone(), message)); - Ok(()) - } - - fn is_connected(&self, node: &NodeId) -> bool { - let data = self.data.read(); - &self.id == node || data.nodes.contains(node) - } - - fn nodes(&self) -> BTreeSet { - self.data.read().nodes.iter().cloned().collect() - } - - fn configured_nodes_count(&self) -> usize { - self.data.read().nodes.len() - } - - fn connected_nodes_count(&self) -> usize { - self.data.read().nodes.len() - } - } - - /// Test message loop. - pub struct MessageLoop { - messages: MessagesQueue, - preserve_sessions: bool, - key_pairs_map: BTreeMap>, - acl_storages_map: BTreeMap>, - key_storages_map: BTreeMap>, - clusters_map: BTreeMap>>>, - } - - impl ::std::fmt::Debug for MessageLoop { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "MessageLoop({})", self.clusters_map.len()) - } - } - - impl MessageLoop { - /// Returns set of all nodes ids. - pub fn nodes(&self) -> BTreeSet { - self.clusters_map.keys().cloned().collect() - } - - /// Returns nodes id by its index. - pub fn node(&self, idx: usize) -> NodeId { - *self.clusters_map.keys().nth(idx).unwrap() - } - - /// Returns key pair of the node by its idx. - pub fn node_key_pair(&self, idx: usize) -> &Arc { - self.key_pairs_map.values().nth(idx).unwrap() - } - - /// Get cluster reference by its index. - pub fn cluster(&self, idx: usize) -> &Arc>> { - self.clusters_map.values().nth(idx).unwrap() - } - - /// Get keys storage reference by its index. - pub fn key_storage(&self, idx: usize) -> &Arc { - self.key_storages_map.values().nth(idx).unwrap() - } - - /// Get keys storage reference by node id. - pub fn key_storage_of(&self, node: &NodeId) -> &Arc { - &self.key_storages_map[node] - } - - /// Replace key storage of the node by its id. - pub fn replace_key_storage_of(&mut self, node: &NodeId, key_storage: Arc) { - *self.key_storages_map.get_mut(node).unwrap() = key_storage; - } - - /// Get ACL storage reference by its index. - pub fn acl_storage(&self, idx: usize) -> &Arc { - self.acl_storages_map.values().nth(idx).unwrap() - } - - /// Get sessions container reference by its index. - pub fn sessions(&self, idx: usize) -> &Arc { - &self.cluster(idx).data.sessions - } - - /// Get sessions container reference by node id. - pub fn sessions_of(&self, node: &NodeId) -> &Arc { - &self.clusters_map[node].data.sessions - } - - /// Isolate node from others. - pub fn isolate(&self, idx: usize) { - let node = self.node(idx); - for (i, cluster) in self.clusters_map.values().enumerate() { - if i == idx { - cluster.data.connections.isolate(); - } else { - cluster.data.connections.disconnect(node); - } - } - } - - /// Exclude node from cluster. - pub fn exclude(&mut self, idx: usize) { - let node = self.node(idx); - for (i, cluster) in self.clusters_map.values().enumerate() { - if i != idx { - cluster.data.connections.exclude(node); - } - } - self.key_storages_map.remove(&node); - self.acl_storages_map.remove(&node); - self.key_pairs_map.remove(&node); - self.clusters_map.remove(&node); - } - - /// Include new node to the cluster. - pub fn include(&mut self, node_key_pair: Arc) -> usize { - let key_storage = Arc::new(DummyKeyStorage::default()); - let acl_storage = Arc::new(DummyAclStorage::default()); - let cluster_params = ClusterConfiguration { - self_key_pair: node_key_pair.clone(), - key_server_set: Arc::new(MapKeyServerSet::new(false, self.nodes().iter() - .chain(::std::iter::once(node_key_pair.public())) - .map(|n| (*n, format!("127.0.0.1:{}", 13).parse().unwrap())) - .collect())), - key_storage: key_storage.clone(), - acl_storage: acl_storage.clone(), - admin_public: None, - preserve_sessions: self.preserve_sessions, - }; - let cluster = new_test_cluster(self.messages.clone(), cluster_params).unwrap(); - - for cluster in self.clusters_map.values(){ - cluster.data.connections.include(node_key_pair.public().clone()); - } - self.acl_storages_map.insert(*node_key_pair.public(), acl_storage); - self.key_storages_map.insert(*node_key_pair.public(), key_storage); - self.clusters_map.insert(*node_key_pair.public(), cluster); - self.key_pairs_map.insert(*node_key_pair.public(), node_key_pair.clone()); - self.clusters_map.keys().position(|k| k == node_key_pair.public()).unwrap() - } - - /// Is empty message queue? - pub fn is_empty(&self) -> bool { - self.messages.lock().is_empty() - } - - /// Takes next message from the queue. - pub fn take_message(&self) -> Option<(NodeId, NodeId, Message)> { - self.messages.lock().pop_front() - } - - /// Process single message. - pub fn process_message(&self, from: NodeId, to: NodeId, message: Message) { - let cluster_data = &self.clusters_map[&to].data; - let connection = cluster_data.connections.provider().connection(&from).unwrap(); - cluster_data.message_processor.process_connection_message(connection, message); - } - - /// Take next message and process it. - pub fn take_and_process_message(&self) -> bool { - let (from, to, message) = match self.take_message() { - Some((from, to, message)) => (from, to, message), - None => return false, - }; - - self.process_message(from, to, message); - true - } - - /// Loops until `predicate` returns `true` or there are no messages in the queue. - pub fn loop_until(&self, predicate: F) where F: Fn() -> bool { - while !predicate() { - if !self.take_and_process_message() { - panic!("message queue is empty but goal is not achieved"); - } - } - } - } - - pub fn make_clusters(num_nodes: usize) -> MessageLoop { - do_make_clusters(num_nodes, false) - } - - pub fn make_clusters_and_preserve_sessions(num_nodes: usize) -> MessageLoop { - do_make_clusters(num_nodes, true) - } - - fn do_make_clusters(num_nodes: usize, preserve_sessions: bool) -> MessageLoop { - let ports_begin = 0; - let messages = Arc::new(Mutex::new(VecDeque::new())); - let key_pairs: Vec<_> = (0..num_nodes) - .map(|_| Arc::new(PlainNodeKeyPair::new(Random.generate()))).collect(); - let key_storages: Vec<_> = (0..num_nodes).map(|_| Arc::new(DummyKeyStorage::default())).collect(); - let acl_storages: Vec<_> = (0..num_nodes).map(|_| Arc::new(DummyAclStorage::default())).collect(); - let cluster_params: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { - self_key_pair: key_pairs[i].clone(), - key_server_set: Arc::new(MapKeyServerSet::new(false, key_pairs.iter().enumerate() - .map(|(j, kp)| (*kp.public(), format!("127.0.0.1:{}", ports_begin + j as u16).parse().unwrap())) - .collect())), - key_storage: key_storages[i].clone(), - acl_storage: acl_storages[i].clone(), - admin_public: None, - preserve_sessions, - }).collect(); - let clusters: Vec<_> = cluster_params.into_iter() - .map(|params| new_test_cluster(messages.clone(), params).unwrap()) - .collect(); - - let clusters_map = clusters.iter().map(|c| (*c.data.config.self_key_pair.public(), c.clone())).collect(); - let key_pairs_map = key_pairs.into_iter().map(|kp| (*kp.public(), kp)).collect(); - let key_storages_map = clusters.iter().zip(key_storages.into_iter()) - .map(|(c, ks)| (*c.data.config.self_key_pair.public(), ks)).collect(); - let acl_storages_map = clusters.iter().zip(acl_storages.into_iter()) - .map(|(c, acls)| (*c.data.config.self_key_pair.public(), acls)).collect(); - MessageLoop { preserve_sessions, messages, key_pairs_map, acl_storages_map, key_storages_map, clusters_map } - } - - #[test] - fn cluster_wont_start_generation_session_if_not_fully_connected() { - let ml = make_clusters(3); - ml.cluster(0).data.connections.disconnect(*ml.cluster(0).data.self_key_pair.public()); - match ml.cluster(0).client().new_generation_session(SessionId::from([1u8; 32]), Default::default(), Default::default(), 1) { - Err(Error::NodeDisconnected) => (), - Err(e) => panic!("unexpected error {:?}", e), - _ => panic!("unexpected success"), - } - } - - #[test] - fn error_in_generation_session_broadcasted_to_all_other_nodes() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // ask one of nodes to produce faulty generation sessions - ml.cluster(1).client().make_faulty_generation_sessions(); - - // start && wait for generation session to fail - let session = ml.cluster(0).client() - .new_generation_session(SessionId::from([1u8; 32]), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(0).client().generation_session(&SessionId::from([1u8; 32])).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - - // check that faulty session is either removed from all nodes, or nonexistent (already removed) - for i in 1..3 { - if let Some(session) = ml.cluster(i).client().generation_session(&SessionId::from([1u8; 32])) { - // wait for both session completion && session removal (session completion event is fired - // before session is removed from its own container by cluster) - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(i).client().generation_session(&SessionId::from([1u8; 32])).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - } - } - } - - #[test] - fn generation_session_completion_signalled_if_failed_on_master() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // ask one of nodes to produce faulty generation sessions - ml.cluster(0).client().make_faulty_generation_sessions(); - - // start && wait for generation session to fail - let session = ml.cluster(0).client() - .new_generation_session(SessionId::from([1u8; 32]), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(0).client().generation_session(&SessionId::from([1u8; 32])).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - - // check that faulty session is either removed from all nodes, or nonexistent (already removed) - for i in 1..3 { - if let Some(session) = ml.cluster(i).client().generation_session(&SessionId::from([1u8; 32])) { - let session = session.clone(); - // wait for both session completion && session removal (session completion event is fired - // before session is removed from its own container by cluster) - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(i).client().generation_session(&SessionId::from([1u8; 32])).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - } - } - } - - #[test] - fn generation_session_is_removed_when_succeeded() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // start && wait for generation session to complete - let session = ml.cluster(0).client() - .new_generation_session(SessionId::from([1u8; 32]), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(0).client().generation_session(&SessionId::from([1u8; 32])).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_ok()); - - // check that on non-master nodes session is either: - // already removed - // or it is removed right after completion - for i in 1..3 { - if let Some(session) = ml.cluster(i).client().generation_session(&SessionId::from([1u8; 32])) { - // run to completion if completion message is still on the way - // AND check that it is actually removed from cluster sessions - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(i).client().generation_session(&SessionId::from([1u8; 32])).is_none()); - } - } - } - - #[test] - fn sessions_are_removed_when_initialization_fails() { - let ml = make_clusters(3); - let client = ml.cluster(0).client(); - - // generation session - { - // try to start generation session => fail in initialization - assert_eq!( - client.new_generation_session(SessionId::from([1u8; 32]), None, Default::default(), 100).map(|_| ()), - Err(Error::NotEnoughNodesForThreshold)); - - // try to start generation session => fails in initialization - assert_eq!( - client.new_generation_session(SessionId::from([1u8; 32]), None, Default::default(), 100).map(|_| ()), - Err(Error::NotEnoughNodesForThreshold)); - - assert!(ml.cluster(0).data.sessions.generation_sessions.is_empty()); - } - - // decryption session - { - // try to start decryption session => fails in initialization - assert_eq!( - client.new_decryption_session( - Default::default(), Default::default(), Default::default(), Some(Default::default()), false, false - ).map(|_| ()), - Err(Error::InvalidMessage)); - - // try to start generation session => fails in initialization - assert_eq!( - client.new_decryption_session( - Default::default(), Default::default(), Default::default(), Some(Default::default()), false, false - ).map(|_| ()), - Err(Error::InvalidMessage)); - - assert!(ml.cluster(0).data.sessions.decryption_sessions.is_empty()); - assert!(ml.cluster(0).data.sessions.negotiation_sessions.is_empty()); - } - } - - #[test] - fn schnorr_signing_session_completes_if_node_does_not_have_a_share() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - let dummy_session_id = SessionId::from([1u8; 32]); - - // start && wait for generation session to complete - let session = ml.cluster(0).client(). - new_generation_session(dummy_session_id, Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(0).client().generation_session(&dummy_session_id).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_ok()); - - // now remove share from node2 - assert!((0..3).all(|i| ml.cluster(i).data.sessions.generation_sessions.is_empty())); - ml.cluster(2).data.config.key_storage.remove(&dummy_session_id).unwrap(); - - // and try to sign message with generated key - let dummy_message = [1u8; 32].into(); - let signature = sign(Random.generate().secret(), &dummy_message).unwrap(); - let session0 = ml.cluster(0).client() - .new_schnorr_signing_session(dummy_session_id, signature.into(), None, Default::default()).unwrap(); - let session = ml.cluster(0).data.sessions.schnorr_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.schnorr_signing_sessions.is_empty())); - session0.into_wait_future().wait().unwrap(); - - // and try to sign message with generated key using node that has no key share - let signature = sign(Random.generate().secret(), &dummy_message).unwrap(); - let session2 = ml.cluster(2).client() - .new_schnorr_signing_session(dummy_session_id, signature.into(), None, Default::default()).unwrap(); - let session = ml.cluster(2).data.sessions.schnorr_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.schnorr_signing_sessions.is_empty())); - session2.into_wait_future().wait().unwrap(); - - // now remove share from node1 - ml.cluster(1).data.config.key_storage.remove(&dummy_session_id).unwrap(); - - // and try to sign message with generated key - let signature = sign(Random.generate().secret(), &dummy_message).unwrap(); - let session1 = ml.cluster(0).client() - .new_schnorr_signing_session(dummy_session_id, signature.into(), None, Default::default()).unwrap(); - let session = ml.cluster(0).data.sessions.schnorr_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished()); - session1.into_wait_future().wait().unwrap_err(); - } - - #[test] - fn ecdsa_signing_session_completes_if_node_does_not_have_a_share() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(4); - let dummy_session_id = SessionId::from([1u8; 32]); - - // start && wait for generation session to complete - let session = ml.cluster(0).client() - .new_generation_session(dummy_session_id, Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(0).client().generation_session(&dummy_session_id).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_ok()); - - // now remove share from node2 - assert!((0..3).all(|i| ml.cluster(i).data.sessions.generation_sessions.is_empty())); - ml.cluster(2).data.config.key_storage.remove(&dummy_session_id).unwrap(); - - // and try to sign message with generated key - let dummy_message = [1u8; 32].into(); - let signature = sign(Random.generate().secret(), &dummy_message).unwrap(); - let session0 = ml.cluster(0).client() - .new_ecdsa_signing_session(dummy_session_id, signature.into(), None, H256::random()).unwrap(); - let session = ml.cluster(0).data.sessions.ecdsa_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.ecdsa_signing_sessions.is_empty())); - session0.into_wait_future().wait().unwrap(); - - // and try to sign message with generated key using node that has no key share - let signature = sign(Random.generate().secret(), &dummy_message).unwrap(); - let session2 = ml.cluster(2).client() - .new_ecdsa_signing_session(dummy_session_id, signature.into(), None, H256::random()).unwrap(); - let session = ml.cluster(2).data.sessions.ecdsa_signing_sessions.first().unwrap(); - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.ecdsa_signing_sessions.is_empty())); - session2.into_wait_future().wait().unwrap(); - - // now remove share from node1 - ml.cluster(1).data.config.key_storage.remove(&dummy_session_id).unwrap(); - - // and try to sign message with generated key - let signature = sign(Random.generate().secret(), &dummy_message).unwrap(); - let session1 = ml.cluster(0).client() - .new_ecdsa_signing_session(dummy_session_id, signature.into(), None, H256::random()).unwrap(); - let session = ml.cluster(0).data.sessions.ecdsa_signing_sessions.first().unwrap(); - ml.loop_until(|| session.is_finished()); - session1.into_wait_future().wait().unwrap_err(); - } -} diff --git a/secret-store/src/key_server_cluster/cluster_connections.rs b/secret-store/src/key_server_cluster/cluster_connections.rs deleted file mode 100644 index 6a5d417d6..000000000 --- a/secret-store/src/key_server_cluster/cluster_connections.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity 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. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::collections::BTreeSet; -use std::sync::Arc; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::message::Message; - -/// Connection to the single node. Provides basic information about connected node and -/// allows sending messages to this node. -pub trait Connection: Send + Sync { - /// Is this inbound connection? This only matters when both nodes are simultaneously establishing - /// two connections to each other. The agreement is that the inbound connection from the node with - /// lower NodeId is used and the other connection is closed. - fn is_inbound(&self) -> bool; - /// Returns id of the connected node. - fn node_id(&self) -> &NodeId; - /// Returns 'address' of the node to use in traces. - fn node_address(&self) -> String; - /// Send message to the connected node. - fn send_message(&self, message: Message); -} - -/// Connections manager. Responsible for keeping us connected to all required nodes. -pub trait ConnectionManager: 'static + Send + Sync { - /// Returns shared reference to connections provider. - fn provider(&self) -> Arc; - /// Try to reach all disconnected nodes immediately. This method is exposed mostly for - /// tests, where all 'nodes' are starting listening for incoming connections first and - /// only after this, they're actually start connecting to each other. - fn connect(&self); -} - -/// Connections provider. Holds all active connections and the set of nodes that we need to -/// connect to. At any moment connection could be lost and the set of connected/disconnected -/// nodes could change (at behalf of the connection manager). -/// Clone operation should be cheap (Arc). -pub trait ConnectionProvider: Send + Sync { - /// Returns the set of currently connected nodes. Error is returned when our node is - /// not a part of the cluster ('isolated' node). - fn connected_nodes(&self) -> Result, Error>; - /// Returns the set of currently disconnected nodes. - fn disconnected_nodes(&self) -> BTreeSet; - /// Returns the reference to the active node connection or None if the node is not connected. - fn connection(&self, node: &NodeId) -> Option>; -} - -#[cfg(test)] -pub mod tests { - use std::collections::{BTreeSet, VecDeque}; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, Ordering}; - use parking_lot::Mutex; - use key_server_cluster::{Error, NodeId}; - use key_server_cluster::message::Message; - use super::{ConnectionManager, Connection, ConnectionProvider}; - - /// Shared messages queue. - pub type MessagesQueue = Arc>>; - - /// Single node connections. - pub struct TestConnections { - node: NodeId, - is_isolated: AtomicBool, - connected_nodes: Mutex>, - disconnected_nodes: Mutex>, - messages: MessagesQueue, - } - - /// Single connection. - pub struct TestConnection { - from: NodeId, - to: NodeId, - messages: MessagesQueue, - } - - impl TestConnections { - pub fn isolate(&self) { - let connected_nodes = ::std::mem::replace(&mut *self.connected_nodes.lock(), Default::default()); - self.is_isolated.store(true, Ordering::Relaxed); - self.disconnected_nodes.lock().extend(connected_nodes) - } - - pub fn disconnect(&self, node: NodeId) { - self.connected_nodes.lock().remove(&node); - self.disconnected_nodes.lock().insert(node); - } - - pub fn exclude(&self, node: NodeId) { - self.connected_nodes.lock().remove(&node); - self.disconnected_nodes.lock().remove(&node); - } - - pub fn include(&self, node: NodeId) { - self.connected_nodes.lock().insert(node); - } - } - - impl ConnectionManager for Arc { - fn provider(&self) -> Arc { - self.clone() - } - - fn connect(&self) {} - } - - impl ConnectionProvider for TestConnections { - fn connected_nodes(&self) -> Result, Error> { - match self.is_isolated.load(Ordering::Relaxed) { - false => Ok(self.connected_nodes.lock().clone()), - true => Err(Error::NodeDisconnected), - } - } - - fn disconnected_nodes(&self) -> BTreeSet { - self.disconnected_nodes.lock().clone() - } - - fn connection(&self, node: &NodeId) -> Option> { - match self.connected_nodes.lock().contains(node) { - true => Some(Arc::new(TestConnection { - from: self.node, - to: *node, - messages: self.messages.clone(), - })), - false => None, - } - } - } - - impl Connection for TestConnection { - fn is_inbound(&self) -> bool { - false - } - - fn node_id(&self) -> &NodeId { - &self.to - } - - fn node_address(&self) -> String { - format!("{}", self.to) - } - - fn send_message(&self, message: Message) { - self.messages.lock().push_back((self.from, self.to, message)) - } - } - - pub fn new_test_connections( - messages: MessagesQueue, - node: NodeId, - mut nodes: BTreeSet - ) -> Arc { - let is_isolated = !nodes.remove(&node); - Arc::new(TestConnections { - node, - is_isolated: AtomicBool::new(is_isolated), - connected_nodes: Mutex::new(nodes), - disconnected_nodes: Default::default(), - messages, - }) - } -} diff --git a/secret-store/src/key_server_cluster/cluster_connections_net.rs b/secret-store/src/key_server_cluster/cluster_connections_net.rs deleted file mode 100644 index cd46b11aa..000000000 --- a/secret-store/src/key_server_cluster/cluster_connections_net.rs +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity 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. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::collections::{BTreeMap, BTreeSet}; -use std::collections::btree_map::Entry; -use std::io; -use std::net::{SocketAddr, IpAddr}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use futures::{future, Future, Stream}; -use parking_lot::{Mutex, RwLock}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::timer::{Interval, timeout::Error as TimeoutError}; -use tokio_io::IoFuture; -use crypto::publickey::KeyPair; -use parity_runtime::Executor; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId, ClusterConfiguration}; -use key_server_cluster::cluster_connections::{ConnectionProvider, Connection, ConnectionManager}; -use key_server_cluster::connection_trigger::{Maintain, ConnectionTrigger}; -use key_server_cluster::cluster_message_processor::MessageProcessor; -use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, - read_encrypted_message, WriteMessage, write_encrypted_message}; -use key_server_cluster::message::{self, ClusterMessage, Message}; -use key_server_cluster::net::{accept_connection as io_accept_connection, - connect as io_connect, Connection as IoConnection}; - -/// Empty future. -pub type BoxedEmptyFuture = Box + Send>; - -/// Maintain interval (seconds). Every MAINTAIN_INTERVAL seconds node: -/// 1) checks if connected nodes are responding to KeepAlive messages -/// 2) tries to connect to disconnected nodes -/// 3) checks if enc/dec sessions are time-outed -const MAINTAIN_INTERVAL: u64 = 10; - -/// When no messages have been received from node within KEEP_ALIVE_SEND_INTERVAL seconds, -/// we must send KeepAlive message to the node to check if it still responds to messages. -const KEEP_ALIVE_SEND_INTERVAL: Duration = Duration::from_secs(30); -/// When no messages have been received from node within KEEP_ALIVE_DISCONNECT_INTERVAL seconds, -/// we must treat this node as non-responding && disconnect from it. -const KEEP_ALIVE_DISCONNECT_INTERVAL: Duration = Duration::from_secs(60); - -/// Network connection manager configuration. -pub struct NetConnectionsManagerConfig { - /// Allow connecting to 'higher' nodes. - pub allow_connecting_to_higher_nodes: bool, - /// Interface to listen to. - pub listen_address: (String, u16), - /// True if we should autostart key servers set change session when servers set changes? - /// This will only work when servers set is configured using KeyServerSet contract. - pub auto_migrate_enabled: bool, -} - -/// Network connections manager. -pub struct NetConnectionsManager { - /// Address we're listening for incoming connections. - listen_address: SocketAddr, - /// Shared cluster connections data reference. - data: Arc, -} - -/// Network connections data. Shared among NetConnectionsManager and spawned futures. -struct NetConnectionsData { - /// Allow connecting to 'higher' nodes. - allow_connecting_to_higher_nodes: bool, - /// Reference to tokio task executor. - executor: Executor, - /// Key pair of this node. - self_key_pair: Arc, - /// Network messages processor. - message_processor: Arc, - /// Connections trigger. - trigger: Mutex>, - /// Mutable connection data. - container: Arc>, -} - -/// Network connections container. This is the only mutable data of NetConnectionsManager. -/// The set of nodes is mutated by the connection trigger and the connections set is also -/// mutated by spawned futures. -pub struct NetConnectionsContainer { - /// Is this node isolated from cluster? - pub is_isolated: bool, - /// Current key servers set. - pub nodes: BTreeMap, - /// Active connections to key servers. - pub connections: BTreeMap>, -} - -/// Network connection to single key server node. -pub struct NetConnection { - executor: Executor, - /// Id of the peer node. - node_id: NodeId, - /// Address of the peer node. - node_address: SocketAddr, - /// Is this inbound (true) or outbound (false) connection? - is_inbound: bool, - /// Key pair that is used to encrypt connection' messages. - key: KeyPair, - /// Last message time. - last_message_time: RwLock, - /// Underlying TCP stream. - stream: SharedTcpStream, -} - -impl NetConnectionsManager { - /// Create new network connections manager. - pub fn new( - executor: Executor, - message_processor: Arc, - trigger: Box, - container: Arc>, - config: &ClusterConfiguration, - net_config: NetConnectionsManagerConfig, - ) -> Result { - let listen_address = make_socket_address( - &net_config.listen_address.0, - net_config.listen_address.1)?; - - Ok(NetConnectionsManager { - listen_address, - data: Arc::new(NetConnectionsData { - allow_connecting_to_higher_nodes: net_config.allow_connecting_to_higher_nodes, - executor, - message_processor, - self_key_pair: config.self_key_pair.clone(), - trigger: Mutex::new(trigger), - container, - }), - }) - } - - /// Start listening for connections and schedule connections maintenance. - pub fn start(&self) -> Result<(), Error> { - net_listen(&self.listen_address, self.data.clone())?; - net_schedule_maintain(self.data.clone()); - Ok(()) - } -} - -impl ConnectionManager for NetConnectionsManager { - fn provider(&self) -> Arc { - self.data.container.clone() - } - - fn connect(&self) { - net_connect_disconnected(self.data.clone()); - } -} - -impl ConnectionProvider for RwLock { - fn connected_nodes(&self) -> Result, Error> { - let connections = self.read(); - if connections.is_isolated { - return Err(Error::NodeDisconnected); - } - - Ok(connections.connections.keys().cloned().collect()) - } - - fn disconnected_nodes(&self) -> BTreeSet { - let connections = self.read(); - connections.nodes.keys() - .filter(|node_id| !connections.connections.contains_key(node_id)) - .cloned() - .collect() - } - - fn connection(&self, node: &NodeId) -> Option> { - match self.read().connections.get(node).cloned() { - Some(connection) => Some(connection), - None => None, - } - } -} - -impl NetConnection { - /// Create new connection. - pub fn new(executor: Executor, is_inbound: bool, connection: IoConnection) -> NetConnection { - NetConnection { - executor, - node_id: connection.node_id, - node_address: connection.address, - is_inbound: is_inbound, - stream: connection.stream, - key: connection.key, - last_message_time: RwLock::new(Instant::now()), - } - } - - /// Get last message time. - pub fn last_message_time(&self) -> Instant { - *self.last_message_time.read() - } - - /// Update last message time - pub fn set_last_message_time(&self, last_message_time: Instant) { - *self.last_message_time.write() = last_message_time - } - - /// Returns future that sends encrypted message over this connection. - pub fn send_message_future(&self, message: Message) -> WriteMessage { - write_encrypted_message(self.stream.clone(), &self.key, message) - } - - /// Returns future that reads encrypted message from this connection. - pub fn read_message_future(&self) -> ReadMessage { - read_encrypted_message(self.stream.clone(), self.key.clone()) - } -} - -impl Connection for NetConnection { - fn is_inbound(&self) -> bool { - self.is_inbound - } - - fn node_id(&self) -> &NodeId { - &self.node_id - } - - fn node_address(&self) -> String { - format!("{}", self.node_address) - } - - fn send_message(&self, message: Message) { - execute(&self.executor, self.send_message_future(message).then(|_| Ok(()))); - } -} - -impl NetConnectionsData { - /// Executes closure for each active connection. - pub fn active_connections(&self) -> Vec> { - self.container.read().connections.values().cloned().collect() - } - - /// Executes closure for each disconnected node. - pub fn disconnected_nodes(&self) -> Vec<(NodeId, SocketAddr)> { - let container = self.container.read(); - container.nodes.iter() - .filter(|(node_id, _)| !container.connections.contains_key(node_id)) - .map(|(node_id, addr)| (*node_id, *addr)) - .collect() - } - - /// Try to insert new connection. Returns true if connection has been inserted. - /// Returns false (and ignores connections) if: - /// - we do not expect connection from this node - /// - we are already connected to the node and existing connection 'supersede' - /// new connection by agreement - pub fn insert(&self, connection: Arc) -> bool { - let node = *connection.node_id(); - let mut container = self.container.write(); - if !container.nodes.contains_key(&node) { - trace!(target: "secretstore_net", "{}: ignoring unknown connection from {} at {}", - self.self_key_pair.public(), node, connection.node_address()); - return false; - } - - if container.connections.contains_key(&node) { - // we have already connected to the same node - // the agreement is that node with lower id must establish connection to node with higher id - if (*self.self_key_pair.public() < node && connection.is_inbound()) - || (*self.self_key_pair.public() > node && !connection.is_inbound()) { - return false; - } - } - - trace!(target: "secretstore_net", - "{}: inserting connection to {} at {}. Connected to {} of {} nodes", - self.self_key_pair.public(), node, connection.node_address(), - container.connections.len() + 1, container.nodes.len()); - container.connections.insert(node, connection); - - true - } - - /// Tries to remove connection. Returns true if connection has been removed. - /// Returns false if we do not know this connection. - pub fn remove(&self, connection: &NetConnection) -> bool { - let node_id = *connection.node_id(); - let is_inbound = connection.is_inbound(); - let mut container = self.container.write(); - if let Entry::Occupied(entry) = container.connections.entry(node_id) { - if entry.get().is_inbound() != is_inbound { - return false; - } - - trace!(target: "secretstore_net", "{}: removing connection to {} at {}", - self.self_key_pair.public(), node_id, entry.get().node_address()); - entry.remove_entry(); - - true - } else { - false - } - } -} - -/// Listen incoming connections. -fn net_listen( - listen_address: &SocketAddr, - data: Arc, -) -> Result<(), Error> { - execute(&data.executor, net_listen_future(listen_address, data.clone())?); - Ok(()) -} - -/// Listen incoming connections future. -fn net_listen_future( - listen_address: &SocketAddr, - data: Arc, -) -> Result { - Ok(Box::new(TcpListener::bind(listen_address)? - .incoming() - .and_then(move |stream| { - net_accept_connection(data.clone(), stream); - Ok(()) - }) - .for_each(|_| Ok(())) - .then(|_| future::ok(())))) -} - -/// Accept incoming connection. -fn net_accept_connection( - data: Arc, - stream: TcpStream, -) { - execute(&data.executor, net_accept_connection_future(data.clone(), stream)); -} - -/// Accept incoming connection future. -fn net_accept_connection_future(data: Arc, stream: TcpStream) -> BoxedEmptyFuture { - Box::new(io_accept_connection(stream, data.self_key_pair.clone()) - .then(move |result| net_process_connection_result(data, None, result)) - .then(|_| future::ok(()))) -} - -/// Connect to remote node. -fn net_connect( - data: Arc, - remote: SocketAddr, -) { - execute(&data.executor, net_connect_future(data.clone(), remote)); -} - -/// Connect to remote node future. -fn net_connect_future( - data: Arc, - remote: SocketAddr, -) -> BoxedEmptyFuture { - let disconnected_nodes = data.container.disconnected_nodes(); - Box::new(io_connect(&remote, data.self_key_pair.clone(), disconnected_nodes) - .then(move |result| net_process_connection_result(data, Some(remote), result)) - .then(|_| future::ok(()))) -} - -/// Process network connection result. -fn net_process_connection_result( - data: Arc, - outbound_addr: Option, - result: Result>, TimeoutError>, -) -> IoFuture> { - match result { - Ok(DeadlineStatus::Meet(Ok(connection))) => { - let connection = Arc::new(NetConnection::new(data.executor.clone(), outbound_addr.is_none(), connection)); - if data.insert(connection.clone()) { - let maintain_action = data.trigger.lock().on_connection_established(connection.node_id()); - maintain_connection_trigger(data.clone(), maintain_action); - - return net_process_connection_messages(data, connection); - } - }, - Ok(DeadlineStatus::Meet(Err(err))) => { - warn!(target: "secretstore_net", "{}: protocol error '{}' when establishing {} connection{}", - data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, - outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); - }, - Ok(DeadlineStatus::Timeout) => { - warn!(target: "secretstore_net", "{}: timeout when establishing {} connection{}", - data.self_key_pair.public(), if outbound_addr.is_some() { "outbound" } else { "inbound" }, - outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); - }, - Err(err) => { - warn!(target: "secretstore_net", "{}: network error '{}' when establishing {} connection{}", - data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, - outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); - }, - } - - Box::new(future::ok(Ok(()))) -} - -/// Process connection messages. -fn net_process_connection_messages( - data: Arc, - connection: Arc, -) -> IoFuture> { - Box::new(connection - .read_message_future() - .then(move |result| - match result { - Ok((_, Ok(message))) => { - connection.set_last_message_time(Instant::now()); - data.message_processor.process_connection_message(connection.clone(), message); - // continue serving connection - let process_messages_future = net_process_connection_messages( - data.clone(), connection).then(|_| Ok(())); - execute(&data.executor, process_messages_future); - Box::new(future::ok(Ok(()))) - }, - Ok((_, Err(err))) => { - warn!(target: "secretstore_net", "{}: protocol error '{}' when reading message from node {}", - data.self_key_pair.public(), err, connection.node_id()); - // continue serving connection - let process_messages_future = net_process_connection_messages( - data.clone(), connection).then(|_| Ok(())); - execute(&data.executor, process_messages_future); - Box::new(future::ok(Err(err))) - }, - Err(err) => { - let node_id = *connection.node_id(); - warn!(target: "secretstore_net", "{}: network error '{}' when reading message from node {}", - data.self_key_pair.public(), err, node_id); - // close connection - if data.remove(&*connection) { - let maintain_action = data.trigger.lock().on_connection_closed(&node_id); - maintain_connection_trigger(data, maintain_action); - } - Box::new(future::err(err)) - }, - } - )) -} - -/// Schedule connections. maintain. -fn net_schedule_maintain(data: Arc) { - let closure_data = data.clone(); - execute(&data.executor, Interval::new_interval(Duration::new(MAINTAIN_INTERVAL, 0)) - .and_then(move |_| Ok(net_maintain(closure_data.clone()))) - .for_each(|_| Ok(())) - .then(|_| future::ok(()))); -} - -/// Maintain network connections. -fn net_maintain(data: Arc) { - trace!(target: "secretstore_net", "{}: executing maintain procedures", data.self_key_pair.public()); - - update_nodes_set(data.clone()); - data.message_processor.maintain_sessions(); - net_keep_alive(data.clone()); - net_connect_disconnected(data); -} - -/// Send keep alive messages to remote nodes. -fn net_keep_alive(data: Arc) { - let active_connections = data.active_connections(); - for connection in active_connections { - // the last_message_time could change after active_connections() call - // => we always need to call Instant::now() after getting last_message_time - let last_message_time = connection.last_message_time(); - let now = Instant::now(); - let last_message_diff = now - last_message_time; - if last_message_diff > KEEP_ALIVE_DISCONNECT_INTERVAL { - warn!(target: "secretstore_net", "{}: keep alive timeout for node {}", - data.self_key_pair.public(), connection.node_id()); - - let node_id = *connection.node_id(); - if data.remove(&*connection) { - let maintain_action = data.trigger.lock().on_connection_closed(&node_id); - maintain_connection_trigger(data.clone(), maintain_action); - } - data.message_processor.process_disconnect(&node_id); - } - else if last_message_diff > KEEP_ALIVE_SEND_INTERVAL { - connection.send_message(Message::Cluster(ClusterMessage::KeepAlive(message::KeepAlive {}))); - } - } -} - -/// Connect disconnected nodes. -fn net_connect_disconnected(data: Arc) { - let disconnected_nodes = data.disconnected_nodes(); - for (node_id, address) in disconnected_nodes { - if data.allow_connecting_to_higher_nodes || *data.self_key_pair.public() < node_id { - net_connect(data.clone(), address); - } - } -} - -/// Schedule future execution. -fn execute + Send + 'static>(executor: &Executor, f: F) { - if let Err(err) = future::Executor::execute(executor, Box::new(f)) { - error!("Secret store runtime unable to spawn task. Runtime is shutting down. ({:?})", err); - } -} - -/// Try to update active nodes set from connection trigger. -fn update_nodes_set(data: Arc) { - let maintain_action = data.trigger.lock().on_maintain(); - maintain_connection_trigger(data, maintain_action); -} - -/// Execute maintain procedures of connections trigger. -fn maintain_connection_trigger(data: Arc, maintain_action: Option) { - if maintain_action == Some(Maintain::SessionAndConnections) || maintain_action == Some(Maintain::Session) { - let session_params = data.trigger.lock().maintain_session(); - if let Some(session_params) = session_params { - let session = data.message_processor.start_servers_set_change_session(session_params); - match session { - Ok(_) => trace!(target: "secretstore_net", "{}: started auto-migrate session", - data.self_key_pair.public()), - Err(err) => trace!(target: "secretstore_net", "{}: failed to start auto-migrate session with: {}", - data.self_key_pair.public(), err), - } - } - } - if maintain_action == Some(Maintain::SessionAndConnections) || maintain_action == Some(Maintain::Connections) { - let mut trigger = data.trigger.lock(); - let mut data = data.container.write(); - trigger.maintain_connections(&mut *data); - } -} - -/// Compose SocketAddr from configuration' address and port. -fn make_socket_address(address: &str, port: u16) -> Result { - let ip_address: IpAddr = address.parse().map_err(|_| Error::InvalidNodeAddress)?; - Ok(SocketAddr::new(ip_address, port)) -} diff --git a/secret-store/src/key_server_cluster/cluster_message_processor.rs b/secret-store/src/key_server_cluster/cluster_message_processor.rs deleted file mode 100644 index df1211f8a..000000000 --- a/secret-store/src/key_server_cluster/cluster_message_processor.rs +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity 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. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::sync::Arc; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::cluster::{ServersSetChangeParams, new_servers_set_change_session}; -use key_server_cluster::cluster_sessions::{AdminSession}; -use key_server_cluster::cluster_connections::{ConnectionProvider, Connection}; -use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, ClusterSessionsContainer, - create_cluster_view}; -use key_server_cluster::cluster_sessions_creator::{ClusterSessionCreator, IntoSessionId}; -use key_server_cluster::message::{self, Message, ClusterMessage}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationSessionTransport, ContinueAction}; -use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; - -/// Something that is able to process signals/messages from other nodes. -pub trait MessageProcessor: Send + Sync { - /// Process disconnect from the remote node. - fn process_disconnect(&self, node: &NodeId); - /// Process single message from the connection. - fn process_connection_message(&self, connection: Arc, message: Message); - - /// Start servers set change session. This is typically used by ConnectionManager when - /// it detects that auto-migration session needs to be started. - fn start_servers_set_change_session(&self, params: ServersSetChangeParams) -> Result, Error>; - /// Try to continue session after key version negotiation session is completed. - fn try_continue_session( - &self, - session: Option>> - ); - /// Maintain active sessions. Typically called by the ConnectionManager at some intervals. - /// Should cancel stalled sessions and send keep-alive messages for sessions that support it. - fn maintain_sessions(&self); -} - -/// Bridge between ConnectionManager and ClusterSessions. -pub struct SessionsMessageProcessor { - self_key_pair: Arc, - servers_set_change_creator_connector: Arc, - sessions: Arc, - connections: Arc, -} - -impl SessionsMessageProcessor { - /// Create new instance of SessionsMessageProcessor. - pub fn new( - self_key_pair: Arc, - servers_set_change_creator_connector: Arc, - sessions: Arc, - connections: Arc, - ) -> Self { - SessionsMessageProcessor { - self_key_pair, - servers_set_change_creator_connector, - sessions, - connections, - } - } - - /// Process single session message from connection. - fn process_message>( - &self, - sessions: &ClusterSessionsContainer, - connection: Arc, - mut message: Message, - ) -> Option> - where - Message: IntoSessionId - { - // get or create new session, if required - let mut sender = *connection.node_id(); - let session = self.prepare_session(sessions, &sender, &message); - // send error if session is not found, or failed to create - let session = match session { - Ok(session) => session, - Err(error) => { - // this is new session => it is not yet in container - warn!(target: "secretstore_net", - "{}: {} session read error '{}' when requested for session from node {}", - self.self_key_pair.public(), S::type_name(), error, sender); - if !message.is_error_message() { - let qed = "session_id only fails for cluster messages; - only session messages are passed to process_message; - qed"; - let session_id = message.into_session_id().expect(qed); - let session_nonce = message.session_nonce().expect(qed); - - connection.send_message(SC::make_error_message(session_id, session_nonce, error)); - } - return None; - }, - }; - - let session_id = session.id(); - let mut is_queued_message = false; - loop { - let message_result = session.on_message(&sender, &message); - match message_result { - Ok(_) => { - // if session is completed => stop - if session.is_finished() { - info!(target: "secretstore_net", - "{}: {} session completed", self.self_key_pair.public(), S::type_name()); - sessions.remove(&session_id); - return Some(session); - } - - // try to dequeue message - match sessions.dequeue_message(&session_id) { - Some((msg_sender, msg)) => { - is_queued_message = true; - sender = msg_sender; - message = msg; - }, - None => return Some(session), - } - }, - Err(Error::TooEarlyForRequest) => { - sessions.enqueue_message(&session_id, sender, message, is_queued_message); - return Some(session); - }, - Err(err) => { - warn!( - target: "secretstore_net", - "{}: {} session error '{}' when processing message {} from node {}", - self.self_key_pair.public(), - S::type_name(), - err, - message, - sender); - session.on_session_error(self.self_key_pair.public(), err); - sessions.remove(&session_id); - return Some(session); - }, - } - } - } - - /// Get or insert new session. - fn prepare_session>( - &self, - sessions: &ClusterSessionsContainer, - sender: &NodeId, - message: &Message - ) -> Result, Error> - where - Message: IntoSessionId - { - fn requires_all_connections(message: &Message) -> bool { - match *message { - Message::Generation(_) => true, - Message::ShareAdd(_) => true, - Message::ServersSetChange(_) => true, - _ => false, - } - } - - // get or create new session, if required - let session_id = message.into_session_id() - .expect("into_session_id fails for cluster messages only; - only session messages are passed to prepare_session; - qed"); - let is_initialization_message = message.is_initialization_message(); - let is_delegation_message = message.is_delegation_message(); - match is_initialization_message || is_delegation_message { - false => sessions.get(&session_id, true).ok_or(Error::NoActiveSessionWithId), - true => { - let creation_data = SC::creation_data_from_message(&message)?; - let master = if is_initialization_message { - *sender - } else { - *self.self_key_pair.public() - }; - let cluster = create_cluster_view( - self.self_key_pair.clone(), - self.connections.clone(), - requires_all_connections(&message))?; - - let nonce = Some(message.session_nonce().ok_or(Error::InvalidMessage)?); - let exclusive = message.is_exclusive_session_message(); - sessions.insert(cluster, master, session_id, nonce, exclusive, creation_data).map(|s| s.session) - }, - } - } - - /// Process single cluster message from the connection. - fn process_cluster_message(&self, connection: Arc, message: ClusterMessage) { - match message { - ClusterMessage::KeepAlive(_) => { - let msg = Message::Cluster(ClusterMessage::KeepAliveResponse(message::KeepAliveResponse { - session_id: None, - })); - connection.send_message(msg) - }, - ClusterMessage::KeepAliveResponse(msg) => if let Some(session_id) = msg.session_id { - self.sessions.on_session_keep_alive(connection.node_id(), session_id.into()); - }, - _ => warn!(target: "secretstore_net", "{}: received unexpected message {} from node {} at {}", - self.self_key_pair.public(), message, connection.node_id(), connection.node_address()), - } - } -} - -impl MessageProcessor for SessionsMessageProcessor { - fn process_disconnect(&self, node: &NodeId) { - self.sessions.on_connection_timeout(node); - } - - fn process_connection_message(&self, connection: Arc, message: Message) { - trace!(target: "secretstore_net", "{}: received message {} from {}", - self.self_key_pair.public(), message, connection.node_id()); - - // error is ignored as we only process errors on session level - match message { - Message::Generation(message) => self - .process_message(&self.sessions.generation_sessions, connection, Message::Generation(message)) - .map(|_| ()).unwrap_or_default(), - Message::Encryption(message) => self - .process_message(&self.sessions.encryption_sessions, connection, Message::Encryption(message)) - .map(|_| ()).unwrap_or_default(), - Message::Decryption(message) => self - .process_message(&self.sessions.decryption_sessions, connection, Message::Decryption(message)) - .map(|_| ()).unwrap_or_default(), - Message::SchnorrSigning(message) => self - .process_message(&self.sessions.schnorr_signing_sessions, connection, Message::SchnorrSigning(message)) - .map(|_| ()).unwrap_or_default(), - Message::EcdsaSigning(message) => self - .process_message(&self.sessions.ecdsa_signing_sessions, connection, Message::EcdsaSigning(message)) - .map(|_| ()).unwrap_or_default(), - Message::ServersSetChange(message) => { - let message = Message::ServersSetChange(message); - let is_initialization_message = message.is_initialization_message(); - let session = self.process_message(&self.sessions.admin_sessions, connection, message); - if is_initialization_message { - if let Some(session) = session { - self.servers_set_change_creator_connector - .set_key_servers_set_change_session(session.clone()); - } - } - }, - Message::KeyVersionNegotiation(message) => { - let session = self.process_message( - &self.sessions.negotiation_sessions, connection, Message::KeyVersionNegotiation(message)); - self.try_continue_session(session); - }, - Message::ShareAdd(message) => self.process_message( - &self.sessions.admin_sessions, connection, Message::ShareAdd(message)) - .map(|_| ()).unwrap_or_default(), - Message::Cluster(message) => self.process_cluster_message(connection, message), - } - } - - fn try_continue_session( - &self, - session: Option>> - ) { - if let Some(session) = session { - let meta = session.meta(); - let is_master_node = meta.self_node_id == meta.master_node_id; - if is_master_node && session.is_finished() { - self.sessions.negotiation_sessions.remove(&session.id()); - match session.result() { - Some(Ok(Some((version, master)))) => match session.take_continue_action() { - Some(ContinueAction::Decrypt( - session, origin, is_shadow_decryption, is_broadcast_decryption - )) => { - let initialization_error = if self.self_key_pair.public() == &master { - session.initialize( - origin, version, is_shadow_decryption, is_broadcast_decryption) - } else { - session.delegate( - master, origin, version, is_shadow_decryption, is_broadcast_decryption) - }; - - if let Err(error) = initialization_error { - session.on_session_error(&meta.self_node_id, error); - self.sessions.decryption_sessions.remove(&session.id()); - } - }, - Some(ContinueAction::SchnorrSign(session, message_hash)) => { - let initialization_error = if self.self_key_pair.public() == &master { - session.initialize(version, message_hash) - } else { - session.delegate(master, version, message_hash) - }; - - if let Err(error) = initialization_error { - session.on_session_error(&meta.self_node_id, error); - self.sessions.schnorr_signing_sessions.remove(&session.id()); - } - }, - Some(ContinueAction::EcdsaSign(session, message_hash)) => { - let initialization_error = if self.self_key_pair.public() == &master { - session.initialize(version, message_hash) - } else { - session.delegate(master, version, message_hash) - }; - - if let Err(error) = initialization_error { - session.on_session_error(&meta.self_node_id, error); - self.sessions.ecdsa_signing_sessions.remove(&session.id()); - } - }, - None => (), - }, - Some(Err(error)) => match session.take_continue_action() { - Some(ContinueAction::Decrypt(session, _, _, _)) => { - session.on_session_error(&meta.self_node_id, error); - self.sessions.decryption_sessions.remove(&session.id()); - }, - Some(ContinueAction::SchnorrSign(session, _)) => { - session.on_session_error(&meta.self_node_id, error); - self.sessions.schnorr_signing_sessions.remove(&session.id()); - }, - Some(ContinueAction::EcdsaSign(session, _)) => { - session.on_session_error(&meta.self_node_id, error); - self.sessions.ecdsa_signing_sessions.remove(&session.id()); - }, - None => (), - }, - None | Some(Ok(None)) => unreachable!("is_master_node; session is finished; - negotiation version always finished with result on master; - qed"), - } - } - } - } - - fn maintain_sessions(&self) { - self.sessions.stop_stalled_sessions(); - self.sessions.sessions_keep_alive(); - } - - fn start_servers_set_change_session(&self, params: ServersSetChangeParams) -> Result, Error> { - new_servers_set_change_session( - self.self_key_pair.clone(), - &*self.sessions, - self.connections.clone(), - self.servers_set_change_creator_connector.clone(), - params, - ).map(|s| s.session) - } -} diff --git a/secret-store/src/key_server_cluster/cluster_sessions.rs b/secret-store/src/key_server_cluster/cluster_sessions.rs deleted file mode 100644 index 7d48b4b48..000000000 --- a/secret-store/src/key_server_cluster/cluster_sessions.rs +++ /dev/null @@ -1,786 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::time::{Duration, Instant}; -use std::sync::{Arc, Weak}; -use std::sync::atomic::AtomicBool; -use std::collections::{VecDeque, BTreeMap, BTreeSet}; -use futures::{oneshot, Oneshot, Complete, Future}; -use parking_lot::{Mutex, RwLock, Condvar}; -use ethereum_types::H256; -use crypto::publickey::Secret; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId, SessionId}; -use key_server_cluster::cluster::{Cluster, ClusterConfiguration, ClusterView}; -use key_server_cluster::cluster_connections::ConnectionProvider; -use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; -use key_server_cluster::message::{self, Message}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl}; -use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSessionImpl}; -use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSessionImpl}; -use key_server_cluster::share_add_session::{SessionImpl as ShareAddSessionImpl, IsolatedSessionTransport as ShareAddTransport}; -use key_server_cluster::servers_set_change_session::{SessionImpl as ServersSetChangeSessionImpl}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, - IsolatedSessionTransport as VersionNegotiationTransport}; - -use key_server_cluster::cluster_sessions_creator::{GenerationSessionCreator, EncryptionSessionCreator, DecryptionSessionCreator, - SchnorrSigningSessionCreator, KeyVersionNegotiationSessionCreator, AdminSessionCreator, SessionCreatorCore, - EcdsaSigningSessionCreator, ClusterSessionCreator}; - -/// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, -/// we must treat this session as stalled && finish it with an error. -/// This timeout is for cases when node is responding to KeepAlive messages, but intentionally ignores -/// session messages. -const SESSION_TIMEOUT_INTERVAL: Duration = Duration::from_secs(60); -/// Interval to send session-level KeepAlive-messages. -const SESSION_KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(30); - -lazy_static! { - /// Servers set change session id (there could be at most 1 session => hardcoded id). - pub static ref SERVERS_SET_CHANGE_SESSION_ID: SessionId = "10b7af423bb551d5dc8645db754163a2145d37d78d468fa7330435ed77064c1c" - .parse() - .expect("hardcoded id should parse without errors; qed"); -} - -/// Session id with sub session. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SessionIdWithSubSession { - /// Key id. - pub id: SessionId, - /// Sub session id. - pub access_key: Secret, -} - -/// Generic cluster session. -pub trait ClusterSession { - /// Session identifier type. - type Id: ::std::fmt::Debug + Ord + Clone; - /// Session creation data type. - type CreationData; - /// Session (successful) result type. - type SuccessfulResult: Send + 'static; - - /// Session type name. - fn type_name() -> &'static str; - /// Get session id. - fn id(&self) -> Self::Id; - /// If session is finished (either with succcess or not). - fn is_finished(&self) -> bool; - /// When it takes too much time to complete session. - fn on_session_timeout(&self); - /// When it takes too much time to receive response from the node. - fn on_node_timeout(&self, node_id: &NodeId); - /// Process error that has occured during session + propagate this error to required nodes. - fn on_session_error(&self, sender: &NodeId, error: Error); - /// Process session message. - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error>; - - /// 'Wait for session completion' helper. - #[cfg(test)] - fn wait_session Option>>( - completion: &CompletionSignal, - session_data: &Mutex, - timeout: Option, - result_reader: F - ) -> Option> { - let mut locked_data = session_data.lock(); - match result_reader(&locked_data) { - Some(result) => Some(result), - None => { - let completion_condvar = completion.completion_condvar.as_ref().expect("created in test mode"); - match timeout { - None => completion_condvar.wait(&mut locked_data), - Some(timeout) => { - completion_condvar.wait_for(&mut locked_data, timeout); - }, - } - - result_reader(&locked_data) - }, - } - } -} - -/// Waitable cluster session. -pub struct WaitableSession { - /// Session handle. - pub session: Arc, - /// Session result oneshot. - pub oneshot: Oneshot>, -} - -/// Session completion signal. -pub struct CompletionSignal { - /// Completion future. - pub completion_future: Mutex>>>, - - /// Completion condvar. - pub completion_condvar: Option, -} - -/// Administrative session. -pub enum AdminSession { - /// Share add session. - ShareAdd(ShareAddSessionImpl), - /// Servers set change session. - ServersSetChange(ServersSetChangeSessionImpl), -} - -/// Administrative session creation data. -pub enum AdminSessionCreationData { - /// Share add session (key id). - ShareAdd(H256), - /// Servers set change session (block id, new_server_set). - ServersSetChange(Option, BTreeSet), -} - -/// Active sessions on this cluster. -pub struct ClusterSessions { - /// Key generation sessions. - pub generation_sessions: ClusterSessionsContainer, - /// Encryption sessions. - pub encryption_sessions: ClusterSessionsContainer, - /// Decryption sessions. - pub decryption_sessions: ClusterSessionsContainer, - /// Schnorr signing sessions. - pub schnorr_signing_sessions: ClusterSessionsContainer, - /// ECDSA signing sessions. - pub ecdsa_signing_sessions: ClusterSessionsContainer, - /// Key version negotiation sessions. - pub negotiation_sessions: ClusterSessionsContainer< - KeyVersionNegotiationSessionImpl, - KeyVersionNegotiationSessionCreator - >, - /// Administrative sessions. - pub admin_sessions: ClusterSessionsContainer, - /// Self node id. - self_node_id: NodeId, - /// Creator core. - creator_core: Arc, -} - -/// Active sessions container listener. -pub trait ClusterSessionsListener: Send + Sync { - /// When new session is inserted to the container. - fn on_session_inserted(&self, _session: Arc) {} - /// When session is removed from the container. - fn on_session_removed(&self, _session: Arc) {} -} - -/// Active sessions container. -pub struct ClusterSessionsContainer> { - /// Sessions creator. - pub creator: SC, - /// Active sessions. - sessions: RwLock>>, - /// Listeners. Lock order: sessions -> listeners. - listeners: Mutex>>>, - /// Sessions container state. - container_state: Arc>, - /// Do not actually remove sessions. - preserve_sessions: bool, -} - -/// Session and its message queue. -pub struct QueuedSession { - /// Session master. - pub master: NodeId, - /// Cluster view. - pub cluster_view: Arc, - /// Last keep alive time. - pub last_keep_alive_time: Instant, - /// Last received message time. - pub last_message_time: Instant, - /// Generation session. - pub session: Arc, - /// Messages queue. - pub queue: VecDeque<(NodeId, Message)>, -} - -/// Cluster sessions container state. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ClusterSessionsContainerState { - /// There's no active sessions => any session can be started. - Idle, - /// There are active sessions => exclusive session can't be started right now. - Active(usize), - /// Exclusive session is active => can't start any other sessions. - Exclusive, -} - -impl ClusterSessions { - /// Create new cluster sessions container. - pub fn new(config: &ClusterConfiguration, servers_set_change_session_creator_connector: Arc) -> Self { - let container_state = Arc::new(Mutex::new(ClusterSessionsContainerState::Idle)); - let creator_core = Arc::new(SessionCreatorCore::new(config)); - ClusterSessions { - self_node_id: config.self_key_pair.public().clone(), - generation_sessions: ClusterSessionsContainer::new(GenerationSessionCreator { - core: creator_core.clone(), - make_faulty_generation_sessions: AtomicBool::new(false), - }, container_state.clone()), - encryption_sessions: ClusterSessionsContainer::new(EncryptionSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - decryption_sessions: ClusterSessionsContainer::new(DecryptionSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - schnorr_signing_sessions: ClusterSessionsContainer::new(SchnorrSigningSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - ecdsa_signing_sessions: ClusterSessionsContainer::new(EcdsaSigningSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - negotiation_sessions: ClusterSessionsContainer::new(KeyVersionNegotiationSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - admin_sessions: ClusterSessionsContainer::new(AdminSessionCreator { - core: creator_core.clone(), - servers_set_change_session_creator_connector: servers_set_change_session_creator_connector, - admin_public: config.admin_public.clone(), - }, container_state), - creator_core: creator_core, - } - } - - #[cfg(test)] - pub fn make_faulty_generation_sessions(&self) { - self.generation_sessions.creator.make_faulty_generation_sessions(); - } - - #[cfg(test)] - pub fn preserve_sessions(&mut self) { - self.generation_sessions.preserve_sessions = true; - self.encryption_sessions.preserve_sessions = true; - self.decryption_sessions.preserve_sessions = true; - self.schnorr_signing_sessions.preserve_sessions = true; - self.ecdsa_signing_sessions.preserve_sessions = true; - self.negotiation_sessions.preserve_sessions = true; - self.admin_sessions.preserve_sessions = true; - } - - /// Send session-level keep-alive messages. - pub fn sessions_keep_alive(&self) { - self.admin_sessions.send_keep_alive(&*SERVERS_SET_CHANGE_SESSION_ID, &self.self_node_id); - } - - /// When session-level keep-alive response is received. - pub fn on_session_keep_alive(&self, sender: &NodeId, session_id: SessionId) { - if session_id == *SERVERS_SET_CHANGE_SESSION_ID { - self.admin_sessions.on_keep_alive(&session_id, sender); - } - } - - /// Stop sessions that are stalling. - pub fn stop_stalled_sessions(&self) { - self.generation_sessions.stop_stalled_sessions(); - self.encryption_sessions.stop_stalled_sessions(); - self.decryption_sessions.stop_stalled_sessions(); - self.schnorr_signing_sessions.stop_stalled_sessions(); - self.ecdsa_signing_sessions.stop_stalled_sessions(); - self.negotiation_sessions.stop_stalled_sessions(); - self.admin_sessions.stop_stalled_sessions(); - } - - /// When connection to node is lost. - pub fn on_connection_timeout(&self, node_id: &NodeId) { - self.generation_sessions.on_connection_timeout(node_id); - self.encryption_sessions.on_connection_timeout(node_id); - self.decryption_sessions.on_connection_timeout(node_id); - self.schnorr_signing_sessions.on_connection_timeout(node_id); - self.ecdsa_signing_sessions.on_connection_timeout(node_id); - self.negotiation_sessions.on_connection_timeout(node_id); - self.admin_sessions.on_connection_timeout(node_id); - self.creator_core.on_connection_timeout(node_id); - } -} - -impl ClusterSessionsContainer where S: ClusterSession, SC: ClusterSessionCreator { - pub fn new(creator: SC, container_state: Arc>) -> Self { - ClusterSessionsContainer { - creator: creator, - sessions: RwLock::new(BTreeMap::new()), - listeners: Mutex::new(Vec::new()), - container_state: container_state, - preserve_sessions: false, - } - } - - pub fn add_listener(&self, listener: Arc>) { - self.listeners.lock().push(Arc::downgrade(&listener)); - } - - #[cfg(test)] - pub fn is_empty(&self) -> bool { - self.sessions.read().is_empty() - } - - pub fn get(&self, session_id: &S::Id, update_last_message_time: bool) -> Option> { - let mut sessions = self.sessions.write(); - sessions.get_mut(session_id) - .map(|s| { - if update_last_message_time { - s.last_message_time = Instant::now(); - } - s.session.clone() - }) - } - - #[cfg(test)] - pub fn first(&self) -> Option> { - self.sessions.read().values().nth(0).map(|s| s.session.clone()) - } - - pub fn insert( - &self, - cluster: Arc, - master: NodeId, - session_id: S::Id, - session_nonce: Option, - is_exclusive_session: bool, - creation_data: Option, - ) -> Result, Error> { - let mut sessions = self.sessions.write(); - if sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // create cluster - // let cluster = create_cluster_view(data, requires_all_connections)?; - // create session - let session = self.creator.create(cluster.clone(), master.clone(), session_nonce, session_id.clone(), creation_data)?; - // check if session can be started - self.container_state.lock().on_session_starting(is_exclusive_session)?; - - // insert session - let queued_session = QueuedSession { - master: master, - cluster_view: cluster, - last_keep_alive_time: Instant::now(), - last_message_time: Instant::now(), - session: session.session.clone(), - queue: VecDeque::new(), - }; - sessions.insert(session_id, queued_session); - self.notify_listeners(|l| l.on_session_inserted(session.session.clone())); - - Ok(session) - } - - pub fn remove(&self, session_id: &S::Id) { - self.do_remove(session_id, &mut *self.sessions.write()); - } - - pub fn enqueue_message(&self, session_id: &S::Id, sender: NodeId, message: Message, is_queued_message: bool) { - self.sessions.write().get_mut(session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_message(&self, session_id: &S::Id) -> Option<(NodeId, Message)> { - self.sessions.write().get_mut(session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn stop_stalled_sessions(&self) { - let mut sessions = self.sessions.write(); - for sid in sessions.keys().cloned().collect::>() { - let remove_session = { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if Instant::now() - session.last_message_time > SESSION_TIMEOUT_INTERVAL { - session.session.on_session_timeout(); - session.session.is_finished() - } else { - false - } - }; - - if remove_session { - self.do_remove(&sid, &mut *sessions); - } - } - } - - pub fn on_connection_timeout(&self, node_id: &NodeId) { - let mut sessions = self.sessions.write(); - for sid in sessions.keys().cloned().collect::>() { - let remove_session = { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - session.session.on_node_timeout(node_id); - session.session.is_finished() - }; - - if remove_session { - self.do_remove(&sid, &mut *sessions); - } - } - } - - fn do_remove(&self, session_id: &S::Id, sessions: &mut BTreeMap>) { - if !self.preserve_sessions { - if let Some(session) = sessions.remove(session_id) { - self.container_state.lock().on_session_completed(); - self.notify_listeners(|l| l.on_session_removed(session.session.clone())); - } - } - } - - fn notify_listeners) -> ()>(&self, callback: F) { - let mut listeners = self.listeners.lock(); - let mut listener_index = 0; - while listener_index < listeners.len() { - match listeners[listener_index].upgrade() { - Some(listener) => { - callback(&*listener); - listener_index += 1; - }, - None => { - listeners.swap_remove(listener_index); - }, - } - } - } -} - -impl ClusterSessionsContainer - where - S: ClusterSession, - SC: ClusterSessionCreator, - SessionId: From, -{ - pub fn send_keep_alive(&self, session_id: &S::Id, self_node_id: &NodeId) { - if let Some(session) = self.sessions.write().get_mut(session_id) { - let now = Instant::now(); - if self_node_id == &session.master && now - session.last_keep_alive_time > SESSION_KEEP_ALIVE_INTERVAL { - session.last_keep_alive_time = now; - // since we send KeepAlive message to prevent nodes from disconnecting - // && worst thing that can happen if node is disconnected is that session is failed - // => ignore error here, because probably this node is not need for the rest of the session at all - let _ = session.cluster_view.broadcast(Message::Cluster(message::ClusterMessage::KeepAliveResponse(message::KeepAliveResponse { - session_id: Some(session_id.clone().into()), - }))); - } - } - } - - pub fn on_keep_alive(&self, session_id: &S::Id, sender: &NodeId) { - if let Some(session) = self.sessions.write().get_mut(session_id) { - let now = Instant::now(); - // we only accept keep alive from master node of ServersSetChange session - if sender == &session.master { - session.last_keep_alive_time = now; - } - } - } -} - -impl ClusterSessionsContainerState { - /// When session is starting. - pub fn on_session_starting(&mut self, is_exclusive_session: bool) -> Result<(), Error> { - match *self { - ClusterSessionsContainerState::Idle if is_exclusive_session => { - ::std::mem::replace(self, ClusterSessionsContainerState::Exclusive); - }, - ClusterSessionsContainerState::Idle => { - ::std::mem::replace(self, ClusterSessionsContainerState::Active(1)); - }, - ClusterSessionsContainerState::Active(_) if is_exclusive_session => - return Err(Error::HasActiveSessions), - ClusterSessionsContainerState::Active(sessions_count) => { - ::std::mem::replace(self, ClusterSessionsContainerState::Active(sessions_count + 1)); - }, - ClusterSessionsContainerState::Exclusive => - return Err(Error::ExclusiveSessionActive), - } - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&mut self) { - match *self { - ClusterSessionsContainerState::Idle => - unreachable!("idle means that there are no active sessions; on_session_completed is only called once after active session is completed; qed"), - ClusterSessionsContainerState::Active(sessions_count) if sessions_count == 1 => { - ::std::mem::replace(self, ClusterSessionsContainerState::Idle); - }, - ClusterSessionsContainerState::Active(sessions_count) => { - ::std::mem::replace(self, ClusterSessionsContainerState::Active(sessions_count - 1)); - } - ClusterSessionsContainerState::Exclusive => { - ::std::mem::replace(self, ClusterSessionsContainerState::Idle); - }, - } - } -} - -impl SessionIdWithSubSession { - /// Create new decryption session Id. - pub fn new(session_id: SessionId, sub_session_id: Secret) -> Self { - SessionIdWithSubSession { - id: session_id, - access_key: sub_session_id, - } - } -} - -impl PartialOrd for SessionIdWithSubSession { - fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for SessionIdWithSubSession { - fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - match self.id.cmp(&other.id) { - ::std::cmp::Ordering::Equal => self.access_key.cmp(&other.access_key), - r @ _ => r, - } - } -} - -impl AdminSession { - pub fn as_servers_set_change(&self) -> Option<&ServersSetChangeSessionImpl> { - match *self { - AdminSession::ServersSetChange(ref session) => Some(session), - _ => None - } - } -} - -impl ClusterSession for AdminSession { - type Id = SessionId; - type CreationData = AdminSessionCreationData; - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "admin" - } - - fn id(&self) -> SessionId { - match *self { - AdminSession::ShareAdd(ref session) => session.id().clone(), - AdminSession::ServersSetChange(ref session) => session.id().clone(), - } - } - - fn is_finished(&self) -> bool { - match *self { - AdminSession::ShareAdd(ref session) => session.is_finished(), - AdminSession::ServersSetChange(ref session) => session.is_finished(), - } - } - - fn on_session_timeout(&self) { - match *self { - AdminSession::ShareAdd(ref session) => session.on_session_timeout(), - AdminSession::ServersSetChange(ref session) => session.on_session_timeout(), - } - } - - fn on_node_timeout(&self, node_id: &NodeId) { - match *self { - AdminSession::ShareAdd(ref session) => session.on_node_timeout(node_id), - AdminSession::ServersSetChange(ref session) => session.on_node_timeout(node_id), - } - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - match *self { - AdminSession::ShareAdd(ref session) => session.on_session_error(node, error), - AdminSession::ServersSetChange(ref session) => session.on_session_error(node, error), - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *self { - AdminSession::ShareAdd(ref session) => session.on_message(sender, message), - AdminSession::ServersSetChange(ref session) => session.on_message(sender, message), - } - } -} - -impl WaitableSession { - pub fn new(session: S, oneshot: Oneshot>) -> Self { - WaitableSession { - session: Arc::new(session), - oneshot, - } - } - - pub fn into_wait_future(self) -> Box + Send> { - Box::new(self.oneshot - .map_err(|e| Error::Internal(e.to_string())) - .and_then(|res| res)) - } -} - -impl CompletionSignal { - pub fn new() -> (Self, Oneshot>) { - let (complete, oneshot) = oneshot(); - let completion_condvar = if cfg!(test) { Some(Condvar::new()) } else { None }; - (CompletionSignal { - completion_future: Mutex::new(Some(complete)), - completion_condvar, - }, oneshot) - } - - pub fn send(&self, result: Result) { - let completion_future = ::std::mem::replace(&mut *self.completion_future.lock(), None); - completion_future.map(|c| c.send(result)); - if let Some(ref completion_condvar) = self.completion_condvar { - completion_condvar.notify_all(); - } - } -} - -pub fn create_cluster_view(self_key_pair: Arc, connections: Arc, requires_all_connections: bool) -> Result, Error> { - let mut connected_nodes = connections.connected_nodes()?; - let disconnected_nodes = connections.disconnected_nodes(); - - let disconnected_nodes_count = disconnected_nodes.len(); - if requires_all_connections { - if disconnected_nodes_count != 0 { - return Err(Error::NodeDisconnected); - } - } - - connected_nodes.insert(self_key_pair.public().clone()); - - let connected_nodes_count = connected_nodes.len(); - Ok(Arc::new(ClusterView::new(self_key_pair, connections, connected_nodes, connected_nodes_count + disconnected_nodes_count))) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - use crypto::publickey::{Random, Generator}; - use key_server_cluster::{Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet, PlainNodeKeyPair}; - use key_server_cluster::cluster::ClusterConfiguration; - use key_server_cluster::connection_trigger::SimpleServersSetChangeSessionCreatorConnector; - use key_server_cluster::cluster::tests::DummyCluster; - use key_server_cluster::generation_session::{SessionImpl as GenerationSession}; - use super::{ClusterSessions, AdminSessionCreationData, ClusterSessionsListener, - ClusterSessionsContainerState, SESSION_TIMEOUT_INTERVAL}; - - pub fn make_cluster_sessions() -> ClusterSessions { - let key_pair = Random.generate(); - let config = ClusterConfiguration { - self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pair.clone())), - key_server_set: Arc::new(MapKeyServerSet::new(false, vec![(key_pair.public().clone(), format!("127.0.0.1:{}", 100).parse().unwrap())].into_iter().collect())), - key_storage: Arc::new(DummyKeyStorage::default()), - acl_storage: Arc::new(DummyAclStorage::default()), - admin_public: Some(Random.generate().public().clone()), - preserve_sessions: false, - }; - ClusterSessions::new(&config, Arc::new(SimpleServersSetChangeSessionCreatorConnector { - admin_public: Some(Random.generate().public().clone()), - })) - } - - #[test] - fn cluster_session_cannot_be_started_if_exclusive_session_is_active() { - let sessions = make_cluster_sessions(); - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - match sessions.admin_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, true, Some(AdminSessionCreationData::ShareAdd(Default::default()))) { - Err(Error::HasActiveSessions) => (), - Err(e) => unreachable!(format!("{}", e)), - Ok(_) => unreachable!("OK"), - } - } - - #[test] - fn exclusive_session_cannot_be_started_if_other_session_is_active() { - let sessions = make_cluster_sessions(); - - sessions.admin_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, true, Some(AdminSessionCreationData::ShareAdd(Default::default()))).unwrap(); - match sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None) { - Err(Error::ExclusiveSessionActive) => (), - Err(e) => unreachable!(format!("{}", e)), - Ok(_) => unreachable!("OK"), - } - } - - #[test] - fn session_listener_works() { - #[derive(Default)] - struct GenerationSessionListener { - inserted: AtomicUsize, - removed: AtomicUsize, - } - - impl ClusterSessionsListener for GenerationSessionListener { - fn on_session_inserted(&self, _session: Arc) { - self.inserted.fetch_add(1, Ordering::Relaxed); - } - - fn on_session_removed(&self, _session: Arc) { - self.removed.fetch_add(1, Ordering::Relaxed); - } - } - - let listener = Arc::new(GenerationSessionListener::default()); - let sessions = make_cluster_sessions(); - sessions.generation_sessions.add_listener(listener.clone()); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(listener.inserted.load(Ordering::Relaxed), 1); - assert_eq!(listener.removed.load(Ordering::Relaxed), 0); - - sessions.generation_sessions.remove(&Default::default()); - assert_eq!(listener.inserted.load(Ordering::Relaxed), 1); - assert_eq!(listener.removed.load(Ordering::Relaxed), 1); - } - - #[test] - fn last_session_removal_sets_container_state_to_idle() { - let sessions = make_cluster_sessions(); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); - - sessions.generation_sessions.remove(&Default::default()); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); - } - - #[test] - fn last_session_removal_by_timeout_sets_container_state_to_idle() { - let sessions = make_cluster_sessions(); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); - - sessions.generation_sessions.sessions.write().get_mut(&Default::default()).unwrap().last_message_time -= SESSION_TIMEOUT_INTERVAL * 2; - - sessions.generation_sessions.stop_stalled_sessions(); - assert_eq!(sessions.generation_sessions.sessions.read().len(), 0); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); - } - - #[test] - fn last_session_removal_by_node_timeout_sets_container_state_to_idle() { - let sessions = make_cluster_sessions(); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); - - sessions.generation_sessions.on_connection_timeout(&Default::default()); - assert_eq!(sessions.generation_sessions.sessions.read().len(), 0); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); - } -} diff --git a/secret-store/src/key_server_cluster/cluster_sessions_creator.rs b/secret-store/src/key_server_cluster/cluster_sessions_creator.rs deleted file mode 100644 index b991afb59..000000000 --- a/secret-store/src/key_server_cluster/cluster_sessions_creator.rs +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::collections::BTreeMap; -use parking_lot::RwLock; -use crypto::publickey::Public; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, DocumentKeyShare, SessionMeta}; -use key_server_cluster::cluster::{Cluster, ClusterConfiguration}; -use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; -use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, SessionIdWithSubSession, - AdminSession, AdminSessionCreationData}; -use key_server_cluster::message::{self, Message, DecryptionMessage, SchnorrSigningMessage, ConsensusMessageOfShareAdd, - ShareAddMessage, ServersSetChangeMessage, ConsensusMessage, ConsensusMessageWithServersSet, EcdsaSigningMessage}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionParams as GenerationSessionParams}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, - SessionParams as DecryptionSessionParams}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams}; -use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSessionImpl, - SessionParams as EcdsaSigningSessionParams}; -use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSessionImpl, - SessionParams as SchnorrSigningSessionParams}; -use key_server_cluster::share_add_session::{SessionImpl as ShareAddSessionImpl, - SessionParams as ShareAddSessionParams, IsolatedSessionTransport as ShareAddTransport}; -use key_server_cluster::servers_set_change_session::{SessionImpl as ServersSetChangeSessionImpl, - SessionParams as ServersSetChangeSessionParams}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, - SessionParams as KeyVersionNegotiationSessionParams, IsolatedSessionTransport as VersionNegotiationTransport, - FastestResultComputer as FastestResultKeyVersionsResultComputer}; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Generic cluster session creator. -pub trait ClusterSessionCreator { - /// Get creation data from message. - fn creation_data_from_message(_message: &Message) -> Result, Error> { - Ok(None) - } - - /// Prepare error message. - fn make_error_message(sid: S::Id, nonce: u64, err: Error) -> Message; - - /// Create cluster session. - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: S::Id, - creation_data: Option, - ) -> Result, Error>; -} - -/// Message with session id. -pub trait IntoSessionId { - /// Get session id. - fn into_session_id(&self) -> Result; -} - -pub struct SessionCreatorCore { - /// Self node id. - self_node_id: NodeId, - /// Reference to key storage - key_storage: Arc, - /// Reference to ACL storage - acl_storage: Arc, - /// Always-increasing sessions counter. Is used as session nonce to prevent replay attacks: - /// 1) during handshake, KeyServers generate new random key to encrypt messages - /// => there's no way to use messages from previous connections for replay attacks - /// 2) when session (of any type) is started, master node increases its own session counter and broadcasts it - /// 3) when slave KeyServer receives session initialization message, it checks that new nonce is larger than previous (from the same master) - /// => there's no way to use messages from previous sessions for replay attacks - /// 4) KeyServer checks that each session message contains the same nonce that initialization message - /// Given that: (A) handshake is secure and (B) session itself is initially replay-protected - /// => this guarantees that sessions are replay-protected. - session_counter: AtomicUsize, - /// Maximal session nonce, received from given connection. - max_nonce: RwLock>, -} - -impl SessionCreatorCore { - /// Create new session creator core. - pub fn new(config: &ClusterConfiguration) -> Self { - SessionCreatorCore { - self_node_id: config.self_key_pair.public().clone(), - acl_storage: config.acl_storage.clone(), - key_storage: config.key_storage.clone(), - session_counter: AtomicUsize::new(0), - max_nonce: RwLock::new(BTreeMap::new()), - } - } - - /// When node has teimtouted. - pub fn on_connection_timeout(&self, node_id: &NodeId) { - self.max_nonce.write().remove(node_id); - } - - /// Check or generate new session nonce. - fn check_session_nonce(&self, master: &NodeId, nonce: Option) -> Result { - // if we're master node of the session, then nonce should be generated - // if we're slave node of the session, then nonce should be passed from outside - match nonce { - Some(nonce) => match nonce > *self.max_nonce.write().entry(master.clone()).or_insert(0) { - true => Ok(nonce), - false => Err(Error::ReplayProtection), - }, - None => Ok(self.session_counter.fetch_add(1, Ordering::Relaxed) as u64 + 1), - } - } - - /// Read key share && remove disconnected nodes. - fn read_key_share(&self, key_id: &SessionId) -> Result, Error> { - self.key_storage.get(key_id) - } -} - -/// Generation session creator. -pub struct GenerationSessionCreator { - /// True if generation sessions must fail. - pub make_faulty_generation_sessions: AtomicBool, - /// Creator core. - pub core: Arc, -} - -impl GenerationSessionCreator { - #[cfg(test)] - pub fn make_faulty_generation_sessions(&self) { - self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); - } -} - -impl ClusterSessionCreator for GenerationSessionCreator { - fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { - message::Message::Generation(message::GenerationMessage::SessionError(message::SessionError { - session: sid.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionId, - _creation_data: Option<()>, - ) -> Result, Error> { - // check that there's no finished encryption session with the same id - if self.core.key_storage.contains(&id) { - return Err(Error::ServerKeyAlreadyGenerated); - } - - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = GenerationSessionImpl::new(GenerationSessionParams { - id: id.clone(), - self_node_id: self.core.self_node_id.clone(), - key_storage: Some(self.core.key_storage.clone()), - cluster: cluster, - nonce: Some(nonce), - }); - - if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { - session.simulate_faulty_behaviour(); - } - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Encryption session creator. -pub struct EncryptionSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for EncryptionSessionCreator { - fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { - message::Message::Encryption(message::EncryptionMessage::EncryptionSessionError(message::EncryptionSessionError { - session: sid.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionId, - _creation_data: Option<()>, - ) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = EncryptionSessionImpl::new(EncryptionSessionParams { - id: id, - self_node_id: self.core.self_node_id.clone(), - encrypted_data: encrypted_data, - key_storage: self.core.key_storage.clone(), - cluster: cluster, - nonce: nonce, - })?; - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Decryption session creator. -pub struct DecryptionSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for DecryptionSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(ref message)) => match &message.message { - &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - }, - Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(ref message)) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::Decryption(message::DecryptionMessage::DecryptionSessionError(message::DecryptionSessionError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionIdWithSubSession, - requester: Option, - ) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = DecryptionSessionImpl::new(DecryptionSessionParams { - meta: SessionMeta { - id: id.id, - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - access_key: id.access_key, - key_share: encrypted_data, - acl_storage: self.core.acl_storage.clone(), - cluster: cluster, - nonce: nonce, - }, requester)?; - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Schnorr signing session creator. -pub struct SchnorrSigningSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for SchnorrSigningSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message)) => match &message.message { - &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - }, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message)) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::SchnorrSigning(message::SchnorrSigningMessage::SchnorrSigningSessionError(message::SchnorrSigningSessionError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionIdWithSubSession, - requester: Option, - ) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = SchnorrSigningSessionImpl::new(SchnorrSigningSessionParams { - meta: SessionMeta { - id: id.id, - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - access_key: id.access_key, - key_share: encrypted_data, - acl_storage: self.core.acl_storage.clone(), - cluster: cluster, - nonce: nonce, - }, requester)?; - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// ECDSA signing session creator. -pub struct EcdsaSigningSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for EcdsaSigningSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message)) => match &message.message { - &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - }, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message)) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::EcdsaSigning(message::EcdsaSigningMessage::EcdsaSigningSessionError(message::EcdsaSigningSessionError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester: Option) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = EcdsaSigningSessionImpl::new(EcdsaSigningSessionParams { - meta: SessionMeta { - id: id.id, - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - access_key: id.access_key, - key_share: encrypted_data, - acl_storage: self.core.acl_storage.clone(), - cluster: cluster, - nonce: nonce, - }, requester)?; - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Key version negotiation session creator. -pub struct KeyVersionNegotiationSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator> for KeyVersionNegotiationSessionCreator { - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::KeyVersionNegotiation(message::KeyVersionNegotiationMessage::KeyVersionsError(message::KeyVersionsError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - // we don't care about continue action here. it only matters when we're completing the session with confirmed - // fatal error from result computer - continue_with: None, - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionIdWithSubSession, - _creation_data: Option<()>, - ) -> Result>, Error> { - let configured_nodes_count = cluster.configured_nodes_count(); - let connected_nodes_count = cluster.connected_nodes_count(); - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let computer = Arc::new(FastestResultKeyVersionsResultComputer::new(self.core.self_node_id.clone(), encrypted_data.as_ref(), - configured_nodes_count, configured_nodes_count)); - let (session, oneshot) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { - meta: ShareChangeSessionMeta { - id: id.id.clone(), - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - configured_nodes_count: configured_nodes_count, - connected_nodes_count: connected_nodes_count, - }, - sub_session: id.access_key.clone(), - key_share: encrypted_data, - result_computer: computer, - transport: VersionNegotiationTransport { - cluster: cluster, - key_id: id.id, - sub_session: id.access_key.clone(), - nonce: nonce, - }, - nonce: nonce, - }); - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Administrative session creator. -pub struct AdminSessionCreator { - /// Creator core. - pub core: Arc, - /// Administrator public. - pub admin_public: Option, - /// Servers set change sessions creator connector. - pub servers_set_change_session_creator_connector: Arc, -} - -impl ClusterSessionCreator for AdminSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref message)) => match &message.message { - &ConsensusMessageWithServersSet::InitializeConsensusSession(ref message) => Ok(Some(AdminSessionCreationData::ServersSetChange( - message.migration_id.clone().map(Into::into), - message.new_nodes_set.clone().into_iter().map(Into::into).collect() - ))), - _ => Err(Error::InvalidMessage), - }, - Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ref message)) => match &message.message { - &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) => Ok(Some(AdminSessionCreationData::ShareAdd(message.version.clone().into()))), - _ => Err(Error::InvalidMessage), - }, - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { - message::Message::ServersSetChange(message::ServersSetChangeMessage::ServersSetChangeError(message::ServersSetChangeError { - session: sid.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionId, - creation_data: Option, - ) -> Result, Error> { - let nonce = self.core.check_session_nonce(&master, nonce)?; - match creation_data { - Some(AdminSessionCreationData::ShareAdd(version)) => { - let (session, oneshot) = ShareAddSessionImpl::new(ShareAddSessionParams { - meta: ShareChangeSessionMeta { - id: id.clone(), - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - transport: ShareAddTransport::new(id.clone(), Some(version), nonce, cluster), - key_storage: self.core.key_storage.clone(), - nonce: nonce, - admin_public: Some(self.admin_public.clone().ok_or(Error::AccessDenied)?), - })?; - Ok(WaitableSession::new(AdminSession::ShareAdd(session), oneshot)) - }, - Some(AdminSessionCreationData::ServersSetChange(migration_id, new_nodes_set)) => { - let admin_public = self.servers_set_change_session_creator_connector.admin_public(migration_id.as_ref(), new_nodes_set) - .map_err(|_| Error::AccessDenied)?; - - let (session, oneshot) = ServersSetChangeSessionImpl::new(ServersSetChangeSessionParams { - meta: ShareChangeSessionMeta { - id: id.clone(), - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - cluster: cluster.clone(), - key_storage: self.core.key_storage.clone(), - nonce: nonce, - all_nodes_set: cluster.nodes(), - admin_public: admin_public, - migration_id: migration_id, - })?; - Ok(WaitableSession::new(AdminSession::ServersSetChange(session), oneshot)) - }, - None => unreachable!("expected to call with non-empty creation data; qed"), - } - } -} - -impl IntoSessionId for Message { - fn into_session_id(&self) -> Result { - match *self { - Message::Generation(ref message) => Ok(message.session_id().clone()), - Message::Encryption(ref message) => Ok(message.session_id().clone()), - Message::Decryption(_) => Err(Error::InvalidMessage), - Message::SchnorrSigning(_) => Err(Error::InvalidMessage), - Message::EcdsaSigning(_) => Err(Error::InvalidMessage), - Message::ServersSetChange(ref message) => Ok(message.session_id().clone()), - Message::ShareAdd(ref message) => Ok(message.session_id().clone()), - Message::KeyVersionNegotiation(_) => Err(Error::InvalidMessage), - Message::Cluster(_) => Err(Error::InvalidMessage), - } - } -} - -impl IntoSessionId for Message { - fn into_session_id(&self) -> Result { - match *self { - Message::Generation(_) => Err(Error::InvalidMessage), - Message::Encryption(_) => Err(Error::InvalidMessage), - Message::Decryption(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::SchnorrSigning(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::EcdsaSigning(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::ServersSetChange(_) => Err(Error::InvalidMessage), - Message::ShareAdd(_) => Err(Error::InvalidMessage), - Message::KeyVersionNegotiation(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::Cluster(_) => Err(Error::InvalidMessage), - } - } -} diff --git a/secret-store/src/key_server_cluster/connection_trigger.rs b/secret-store/src/key_server_cluster/connection_trigger.rs deleted file mode 100644 index b67336780..000000000 --- a/secret-store/src/key_server_cluster/connection_trigger.rs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::collections::btree_map::Entry; -use std::net::SocketAddr; -use std::sync::Arc; -use ethereum_types::H256; -use crypto::publickey::Public; -use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot}; -use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams}; -use key_server_cluster::cluster_sessions::AdminSession; -use key_server_cluster::cluster_connections::{Connection}; -use key_server_cluster::cluster_connections_net::{NetConnectionsContainer}; -use types::{Error, NodeId}; -use blockchain::SigningKeyPair; - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Describes which maintain() call is required. -pub enum Maintain { - /// We need to maintain() both connections && session. - SessionAndConnections, - /// Only call maintain_session. - Session, - /// Only call maintain_connections. - Connections, -} - -/// Connection trigger, which executes necessary actions when set of key servers changes. -pub trait ConnectionTrigger: Send + Sync { - /// On maintain interval. - fn on_maintain(&mut self) -> Option; - /// When connection is established. - fn on_connection_established(&mut self, node: &NodeId) -> Option; - /// When connection is closed. - fn on_connection_closed(&mut self, node: &NodeId) -> Option; - /// Maintain active sessions. Returns Some if servers set session creation required. - fn maintain_session(&mut self) -> Option; - /// Maintain active connections. - fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer); - /// Return connector for the servers set change session creator. - fn servers_set_change_creator_connector(&self) -> Arc; -} - -/// Servers set change session creator connector. -pub trait ServersSetChangeSessionCreatorConnector: Send + Sync { - /// Get actual administrator public key. For manual-migration configuration it is the pre-configured - /// administrator key. For auto-migration configurations it is the key of actual MigrationSession master node. - fn admin_public(&self, migration_id: Option<&H256>, new_server_set: BTreeSet) -> Result; - /// Set active servers set change session. - fn set_key_servers_set_change_session(&self, session: Arc); -} - -/// Simple connection trigger, which only keeps connections to current_set. -pub struct SimpleConnectionTrigger { - /// Key server set cluster. - key_server_set: Arc, - /// Trigger connections. - connections: TriggerConnections, - /// Servers set change session creator connector. - connector: Arc, -} - -/// Simple Servers set change session creator connector, which will just return -/// pre-configured administartor public when asked. -pub struct SimpleServersSetChangeSessionCreatorConnector { - /// Secret store administrator public key. - pub admin_public: Option, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Action with trigger connections. -pub enum ConnectionsAction { - /// Connect to nodes from old set only. - ConnectToCurrentSet, - /// Connect to nodes from migration set. - ConnectToMigrationSet, -} - -/// Trigger connections. -pub struct TriggerConnections { - /// This node key pair. - pub self_key_pair: Arc, -} - -impl SimpleConnectionTrigger { - /// Create new simple from cluster configuration. - pub fn with_config(config: &ClusterConfiguration) -> Self { - Self::new(config.key_server_set.clone(), config.self_key_pair.clone(), config.admin_public) - } - - /// Create new simple connection trigger. - pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self { - SimpleConnectionTrigger { - key_server_set: key_server_set, - connections: TriggerConnections { - self_key_pair: self_key_pair, - }, - connector: Arc::new(SimpleServersSetChangeSessionCreatorConnector { - admin_public: admin_public, - }), - } - } -} - -impl ConnectionTrigger for SimpleConnectionTrigger { - fn on_maintain(&mut self) -> Option { - Some(Maintain::Connections) - } - - fn on_connection_established(&mut self, _node: &NodeId) -> Option { - None - } - - fn on_connection_closed(&mut self, _node: &NodeId) -> Option { - // we do not want to reconnect after every connection close - // because it could be a part of something bigger - None - } - - fn maintain_session(&mut self) -> Option { - None - } - - fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer) { - self.connections.maintain(ConnectionsAction::ConnectToCurrentSet, connections, &self.key_server_set.snapshot()) - } - - fn servers_set_change_creator_connector(&self) -> Arc { - self.connector.clone() - } -} - -impl ServersSetChangeSessionCreatorConnector for SimpleServersSetChangeSessionCreatorConnector { - fn admin_public(&self, _migration_id: Option<&H256>, _new_server_set: BTreeSet) -> Result { - self.admin_public.clone().ok_or(Error::AccessDenied) - } - - fn set_key_servers_set_change_session(&self, _session: Arc) { - } -} - -impl TriggerConnections { - pub fn maintain(&self, action: ConnectionsAction, data: &mut NetConnectionsContainer, server_set: &KeyServerSetSnapshot) { - match action { - ConnectionsAction::ConnectToCurrentSet => { - adjust_connections(self.self_key_pair.public(), data, &server_set.current_set); - }, - ConnectionsAction::ConnectToMigrationSet => { - let migration_set = server_set.migration.as_ref().map(|s| s.set.clone()).unwrap_or_default(); - adjust_connections(self.self_key_pair.public(), data, &migration_set); - }, - } - } -} - -fn adjust_connections( - self_node_id: &NodeId, - data: &mut NetConnectionsContainer, - required_set: &BTreeMap -) { - if !required_set.contains_key(self_node_id) { - if !data.is_isolated { - trace!(target: "secretstore_net", "{}: isolated from cluser", self_node_id); - } - - data.is_isolated = true; - data.connections.clear(); - data.nodes.clear(); - return; - } - - data.is_isolated = false; - for node_to_disconnect in select_nodes_to_disconnect(&data.nodes, required_set) { - if let Entry::Occupied(entry) = data.connections.entry(node_to_disconnect.clone()) { - trace!(target: "secretstore_net", "{}: adjusting connections - removing connection to {} at {}", - self_node_id, entry.get().node_id(), entry.get().node_address()); - entry.remove(); - } - - data.nodes.remove(&node_to_disconnect); - } - - for (node_to_connect, node_addr) in required_set { - if node_to_connect != self_node_id { - data.nodes.insert(node_to_connect.clone(), node_addr.clone()); - } - } -} - -fn select_nodes_to_disconnect(current_set: &BTreeMap, new_set: &BTreeMap) -> Vec { - current_set.iter() - .filter(|&(node_id, node_addr)| match new_set.get(node_id) { - Some(new_node_addr) => node_addr != new_node_addr, - None => true, - }) - .map(|(node_id, _)| node_id.clone()) - .collect() -} - -#[cfg(test)] -mod tests { - use std::collections::BTreeSet; - use std::sync::Arc; - use crypto::publickey::{Random, Generator}; - use key_server_cluster::{MapKeyServerSet, PlainNodeKeyPair, KeyServerSetSnapshot, KeyServerSetMigration}; - use key_server_cluster::cluster_connections_net::NetConnectionsContainer; - use super::{Maintain, TriggerConnections, ConnectionsAction, ConnectionTrigger, SimpleConnectionTrigger, - select_nodes_to_disconnect, adjust_connections}; - - fn default_connection_data() -> NetConnectionsContainer { - NetConnectionsContainer { - is_isolated: false, - nodes: Default::default(), - connections: Default::default(), - } - } - - fn create_connections() -> TriggerConnections { - TriggerConnections { - self_key_pair: Arc::new(PlainNodeKeyPair::new(Random.generate())), - } - } - - #[test] - fn do_not_disconnect_if_set_is_not_changed() { - let node_id = Random.generate().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id, "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![(node_id, "127.0.0.1:8081".parse().unwrap())].into_iter().collect()), - vec![]); - } - - #[test] - fn disconnect_if_address_has_changed() { - let node_id = Random.generate().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![(node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect()), - vec![node_id.clone()]); - } - - #[test] - fn disconnect_if_node_has_removed() { - let node_id = Random.generate().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![].into_iter().collect()), - vec![node_id.clone()]); - } - - #[test] - fn does_not_disconnect_if_node_has_added() { - let node_id = Random.generate().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (Random.generate().public().clone(), "127.0.0.1:8082".parse().unwrap())] - .into_iter().collect()), - vec![]); - } - - #[test] - fn adjust_connections_disconnects_from_all_nodes_if_not_a_part_of_key_server() { - let self_node_id = Random.generate().public().clone(); - let other_node_id = Random.generate().public().clone(); - let mut connection_data = default_connection_data(); - connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8081".parse().unwrap()); - - let required_set = connection_data.nodes.clone(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.is_empty()); - assert!(connection_data.is_isolated); - } - - #[test] - fn adjust_connections_connects_to_new_nodes() { - let self_node_id = Random.generate().public().clone(); - let other_node_id = Random.generate().public().clone(); - let mut connection_data = default_connection_data(); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (other_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.contains_key(&other_node_id)); - assert!(!connection_data.is_isolated); - } - - #[test] - fn adjust_connections_reconnects_from_changed_nodes() { - let self_node_id = Random.generate().public().clone(); - let other_node_id = Random.generate().public().clone(); - let mut connection_data = default_connection_data(); - connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap()); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (other_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert_eq!(connection_data.nodes.get(&other_node_id), Some(&"127.0.0.1:8083".parse().unwrap())); - assert!(!connection_data.is_isolated); - } - - #[test] - fn adjust_connections_disconnects_from_removed_nodes() { - let self_node_id = Random.generate().public().clone(); - let other_node_id = Random.generate().public().clone(); - let mut connection_data = default_connection_data(); - connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap()); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.is_empty()); - assert!(!connection_data.is_isolated); - } - - #[test] - fn adjust_connections_does_not_connects_to_self() { - let self_node_id = Random.generate().public().clone(); - let mut connection_data = default_connection_data(); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.is_empty()); - assert!(!connection_data.is_isolated); - } - - #[test] - fn maintain_connects_to_current_set_works() { - let connections = create_connections(); - let self_node_id = connections.self_key_pair.public().clone(); - let current_node_id = Random.generate().public().clone(); - let migration_node_id = Random.generate().public().clone(); - let new_node_id = Random.generate().public().clone(); - - let mut connections_data = default_connection_data(); - connections.maintain(ConnectionsAction::ConnectToCurrentSet, &mut connections_data, &KeyServerSetSnapshot { - current_set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(), - new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(), - migration: Some(KeyServerSetMigration { - set: vec![(migration_node_id.clone(), "127.0.0.1:8084".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }); - - assert_eq!(vec![current_node_id], connections_data.nodes.keys().cloned().collect::>()); - } - - #[test] - fn maintain_connects_to_migration_set_works() { - let connections = create_connections(); - let self_node_id = connections.self_key_pair.public().clone(); - let current_node_id = Random.generate().public().clone(); - let migration_node_id = Random.generate().public().clone(); - let new_node_id = Random.generate().public().clone(); - - let mut connections_data = default_connection_data(); - connections.maintain(ConnectionsAction::ConnectToMigrationSet, &mut connections_data, &KeyServerSetSnapshot { - current_set: vec![(current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(), - new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(), - migration: Some(KeyServerSetMigration { - set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (migration_node_id.clone(), "127.0.0.1:8084".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }); - - assert_eq!(vec![migration_node_id].into_iter().collect::>(), - connections_data.nodes.keys().cloned().collect::>()); - } - - #[test] - fn simple_connections_trigger_only_maintains_connections() { - let key_server_set = Arc::new(MapKeyServerSet::new(false, Default::default())); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(Random.generate())); - let mut trigger = SimpleConnectionTrigger::new(key_server_set, self_key_pair, None); - assert_eq!(trigger.on_maintain(), Some(Maintain::Connections)); - } -} diff --git a/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs b/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs deleted file mode 100644 index 4c8bcc482..000000000 --- a/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::net::SocketAddr; -use std::sync::Arc; -use ethereum_types::H256; -use crypto::publickey::Public; -use parking_lot::Mutex; -use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot, KeyServerSetMigration, is_migration_required}; -use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams}; -use key_server_cluster::cluster_connections_net::NetConnectionsContainer; -use key_server_cluster::cluster_sessions::{AdminSession, ClusterSession}; -use key_server_cluster::jobs::servers_set_change_access_job::ordered_nodes_hash; -use key_server_cluster::connection_trigger::{Maintain, ConnectionsAction, ConnectionTrigger, - ServersSetChangeSessionCreatorConnector, TriggerConnections}; -use types::{Error, NodeId}; -use blockchain::SigningKeyPair; - -/// Key servers set change trigger with automated migration procedure. -pub struct ConnectionTriggerWithMigration { - /// This node key pair. - self_key_pair: Arc, - /// Key server set. - key_server_set: Arc, - /// Last server set state. - snapshot: KeyServerSetSnapshot, - /// Required connections action. - connections_action: Option, - /// Required session action. - session_action: Option, - /// Currenty connected nodes. - connected: BTreeSet, - /// Trigger migration connections. - connections: TriggerConnections, - /// Trigger migration session. - session: TriggerSession, -} - -#[derive(Default)] -/// Key servers set change session creator connector with migration support. -pub struct ServersSetChangeSessionCreatorConnectorWithMigration { - /// This node id. - self_node_id: NodeId, - /// Active migration state to check when servers set change session is started. - migration: Mutex>, - /// Active servers set change session. - session: Mutex>>, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Migration session action. -enum SessionAction { - /// Start migration (confirm migration transaction). - StartMigration(H256), - /// Start migration session. - Start, - /// Confirm migration and forget migration session. - ConfirmAndDrop(H256), - /// Forget migration session. - Drop, - /// Forget migration session and retry. - DropAndRetry, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Migration session state. -enum SessionState { - /// No active session. - Idle, - /// Session is running with given migration id. - Active(Option), - /// Session is completed successfully. - Finished(Option), - /// Session is completed with an error. - Failed(Option), -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Migration state. -pub enum MigrationState { - /// No migration required. - Idle, - /// Migration is required. - Required, - /// Migration has started. - Started, -} - -/// Migration session. -struct TriggerSession { - /// Servers set change session creator connector. - connector: Arc, - /// This node key pair. - self_key_pair: Arc, - /// Key server set. - key_server_set: Arc, -} - -impl ConnectionTriggerWithMigration { - /// Create new simple from cluster configuration. - pub fn with_config(config: &ClusterConfiguration) -> Self { - Self::new(config.key_server_set.clone(), config.self_key_pair.clone()) - } - - /// Create new trigge with migration. - pub fn new(key_server_set: Arc, self_key_pair: Arc) -> Self { - let snapshot = key_server_set.snapshot(); - let migration = snapshot.migration.clone(); - - ConnectionTriggerWithMigration { - self_key_pair: self_key_pair.clone(), - key_server_set: key_server_set.clone(), - snapshot: snapshot, - connected: BTreeSet::new(), - connections: TriggerConnections { - self_key_pair: self_key_pair.clone(), - }, - session: TriggerSession { - connector: Arc::new(ServersSetChangeSessionCreatorConnectorWithMigration { - self_node_id: self_key_pair.public().clone(), - migration: Mutex::new(migration), - session: Mutex::new(None), - }), - self_key_pair: self_key_pair, - key_server_set: key_server_set, - }, - connections_action: None, - session_action: None, - } - } - - /// Actually do mainteinance. - fn do_maintain(&mut self) -> Option { - loop { - let session_state = session_state(self.session.connector.session.lock().clone()); - let migration_state = migration_state(self.self_key_pair.public(), &self.snapshot); - - let session_action = maintain_session(self.self_key_pair.public(), &self.connected, &self.snapshot, migration_state, session_state); - let session_maintain_required = session_action.map(|session_action| - self.session.process(session_action)).unwrap_or_default(); - self.session_action = session_action; - - let connections_action = maintain_connections(migration_state, session_state); - let connections_maintain_required = connections_action.map(|_| true).unwrap_or_default(); - self.connections_action = connections_action; - - if session_state != SessionState::Idle || migration_state != MigrationState::Idle { - trace!(target: "secretstore_net", "{}: non-idle auto-migration state: {:?} -> {:?}", - self.self_key_pair.public(), (migration_state, session_state), (self.connections_action, self.session_action)); - } - - if session_action != Some(SessionAction::DropAndRetry) { - return match (session_maintain_required, connections_maintain_required) { - (true, true) => Some(Maintain::SessionAndConnections), - (true, false) => Some(Maintain::Session), - (false, true) => Some(Maintain::Connections), - (false, false) => None, - }; - } - } - } -} - -impl ConnectionTrigger for ConnectionTriggerWithMigration { - fn on_maintain(&mut self) -> Option { - self.snapshot = self.key_server_set.snapshot(); - *self.session.connector.migration.lock() = self.snapshot.migration.clone(); - - self.do_maintain() - } - - fn on_connection_established(&mut self, node: &NodeId) -> Option { - self.connected.insert(node.clone()); - self.do_maintain() - } - - fn on_connection_closed(&mut self, node: &NodeId) -> Option { - self.connected.remove(node); - self.do_maintain() - } - - fn maintain_session(&mut self) -> Option { - self.session_action.and_then(|action| self.session.maintain(action, &self.snapshot)) - } - - fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer) { - if let Some(action) = self.connections_action { - self.connections.maintain(action, connections, &self.snapshot); - } - } - - fn servers_set_change_creator_connector(&self) -> Arc { - self.session.connector.clone() - } -} - -impl ServersSetChangeSessionCreatorConnector for ServersSetChangeSessionCreatorConnectorWithMigration { - fn admin_public(&self, migration_id: Option<&H256>, new_server_set: BTreeSet) -> Result { - // the idea is that all nodes are agreed upon a block number and a new set of nodes in this block - // then master node is selected of all nodes set && this master signs the old set && new set - // (signatures are inputs to ServerSetChangeSession) - self.migration.lock().as_ref() - .map(|migration| { - let is_migration_id_same = migration_id.map(|mid| mid == &migration.id).unwrap_or_default(); - let is_migration_set_same = new_server_set == migration.set.keys().cloned().collect(); - if is_migration_id_same && is_migration_set_same { - Ok(migration.master.clone()) - } else { - warn!(target: "secretstore_net", "{}: failed to accept auto-migration session: same_migration_id={}, same_migration_set={}", - self.self_node_id, is_migration_id_same, is_migration_set_same); - - Err(Error::AccessDenied) - } - }) - .unwrap_or_else(|| { - warn!(target: "secretstore_net", "{}: failed to accept non-scheduled auto-migration session", self.self_node_id); - Err(Error::AccessDenied) - }) - } - - fn set_key_servers_set_change_session(&self, session: Arc) { - *self.session.lock() = Some(session); - } -} - -impl TriggerSession { - /// Process session action. - pub fn process(&mut self, action: SessionAction) -> bool { - match action { - SessionAction::ConfirmAndDrop(migration_id) => { - *self.connector.session.lock() = None; - self.key_server_set.confirm_migration(migration_id); - false - }, - SessionAction::Drop | SessionAction::DropAndRetry => { - *self.connector.session.lock() = None; - false - }, - SessionAction::StartMigration(migration_id) => { - self.key_server_set.start_migration(migration_id); - false - }, - SessionAction::Start => true, - } - } - - /// Maintain session. - pub fn maintain( - &mut self, - action: SessionAction, - server_set: &KeyServerSetSnapshot - ) -> Option { - if action != SessionAction::Start { // all other actions are processed in maintain - return None; - } - let migration = server_set.migration.as_ref() - .expect("action is Start only when migration is started (see maintain_session); qed"); - - // we assume that authorities that are removed from the servers set are either offline, or malicious - // => they're not involved in ServersSetChangeSession - // => both sets are the same - let old_set: BTreeSet<_> = migration.set.keys().cloned().collect(); - let new_set = old_set.clone(); - - let signatures = self.self_key_pair.sign(&ordered_nodes_hash(&old_set)) - .and_then(|old_set_signature| self.self_key_pair.sign(&ordered_nodes_hash(&new_set)) - .map(|new_set_signature| (old_set_signature, new_set_signature))); - - match signatures { - Ok((old_set_signature, new_set_signature)) => Some(ServersSetChangeParams { - session_id: None, - migration_id: Some(migration.id), - new_nodes_set: new_set, - old_set_signature, - new_set_signature, - }), - Err(err) => { - trace!( - target: "secretstore_net", - "{}: failed to sign servers set for auto-migrate session with: {}", - self.self_key_pair.public(), err); - None - }, - } - } -} - -fn migration_state(self_node_id: &NodeId, snapshot: &KeyServerSetSnapshot) -> MigrationState { - // if this node is not on current && old set => we do not participate in migration - if !snapshot.current_set.contains_key(self_node_id) && - !snapshot.migration.as_ref().map(|s| s.set.contains_key(self_node_id)).unwrap_or_default() { - return MigrationState::Idle; - } - - // if migration has already started no other states possible - if snapshot.migration.is_some() { - return MigrationState::Started; - } - - // we only require migration if set actually changes - // when only address changes, we could simply adjust connections - if !is_migration_required(&snapshot.current_set, &snapshot.new_set) { - return MigrationState::Idle; - } - - return MigrationState::Required; -} - -fn session_state(session: Option>) -> SessionState { - session - .and_then(|s| match s.as_servers_set_change() { - Some(s) if !s.is_finished() => Some(SessionState::Active(s.migration_id().cloned())), - Some(s) => match s.result() { - Some(Ok(_)) => Some(SessionState::Finished(s.migration_id().cloned())), - Some(Err(_)) => Some(SessionState::Failed(s.migration_id().cloned())), - None => unreachable!("s.is_finished() == true; when session is finished, result is available; qed"), - }, - None => None, - }) - .unwrap_or(SessionState::Idle) -} - -fn maintain_session(self_node_id: &NodeId, connected: &BTreeSet, snapshot: &KeyServerSetSnapshot, migration_state: MigrationState, session_state: SessionState) -> Option { - let migration_data_proof = "migration_state is Started; migration data available when started; qed"; - - match (migration_state, session_state) { - // === NORMAL combinations === - - // having no session when it is not required => ok - (MigrationState::Idle, SessionState::Idle) => None, - // migration is required && no active session => start migration - (MigrationState::Required, SessionState::Idle) => { - match select_master_node(snapshot) == self_node_id { - true => Some(SessionAction::StartMigration(H256::random())), - // we are not on master node - false => None, - } - }, - // migration is active && there's no active session => start it - (MigrationState::Started, SessionState::Idle) => { - match is_connected_to_all_nodes(self_node_id, &snapshot.migration.as_ref().expect(migration_data_proof).set, connected) && - select_master_node(snapshot) == self_node_id { - true => Some(SessionAction::Start), - // we are not connected to all required nodes yet or we are not on master node => wait for it - false => None, - } - }, - // migration is active && session is not yet started/finished => ok - (MigrationState::Started, SessionState::Active(ref session_migration_id)) - if snapshot.migration.as_ref().expect(migration_data_proof).id == session_migration_id.unwrap_or_default() => - None, - // migration has finished => confirm migration - (MigrationState::Started, SessionState::Finished(ref session_migration_id)) - if snapshot.migration.as_ref().expect(migration_data_proof).id == session_migration_id.unwrap_or_default() => - match snapshot.migration.as_ref().expect(migration_data_proof).set.contains_key(self_node_id) { - true => Some(SessionAction::ConfirmAndDrop( - snapshot.migration.as_ref().expect(migration_data_proof).id.clone() - )), - // we are not on migration set => we do not need to confirm - false => Some(SessionAction::Drop), - }, - // migration has failed => it should be dropped && restarted later - (MigrationState::Started, SessionState::Failed(ref session_migration_id)) - if snapshot.migration.as_ref().expect(migration_data_proof).id == session_migration_id.unwrap_or_default() => - Some(SessionAction::Drop), - - // ABNORMAL combinations, which are still possible when contract misbehaves === - - // having active session when it is not required => drop it && wait for other tasks - (MigrationState::Idle, SessionState::Active(_)) | - // no migration required && there's finished session => drop it && wait for other tasks - (MigrationState::Idle, SessionState::Finished(_)) | - // no migration required && there's failed session => drop it && wait for other tasks - (MigrationState::Idle, SessionState::Failed(_)) | - // migration is required && session is active => drop it && wait for other tasks - (MigrationState::Required, SessionState::Active(_)) | - // migration is required && session has failed => we need to forget this obsolete session and retry - (MigrationState::Required, SessionState::Finished(_)) | - // session for other migration is active => we need to forget this obsolete session and retry - // (the case for same id is checked above) - (MigrationState::Started, SessionState::Active(_)) | - // session for other migration has finished => we need to forget this obsolete session and retry - // (the case for same id is checked above) - (MigrationState::Started, SessionState::Finished(_)) | - // session for other migration has failed => we need to forget this obsolete session and retry - // (the case for same id is checked above) - (MigrationState::Started, SessionState::Failed(_)) | - // migration is required && session has failed => we need to forget this obsolete session and retry - (MigrationState::Required, SessionState::Failed(_)) => { - // some of the cases above could happen because of lags (could actually be a non-abnormal behavior) - // => we ony trace here - trace!(target: "secretstore_net", "{}: suspicious auto-migration state: {:?}", - self_node_id, (migration_state, session_state)); - Some(SessionAction::DropAndRetry) - }, - } -} - -fn maintain_connections(migration_state: MigrationState, session_state: SessionState) -> Option { - match (migration_state, session_state) { - // session is active => we do not alter connections when session is active - (_, SessionState::Active(_)) => None, - // when no migration required => we just keep us connected to old nodes set - (MigrationState::Idle, _) => Some(ConnectionsAction::ConnectToCurrentSet), - // when migration is either scheduled, or in progress => connect to both old and migration set. - // this could lead to situation when node is not 'officially' a part of KeyServer (i.e. it is not in current_set) - // but it participates in new key generation session - // it is ok, since 'officialy' here means that this node is a owner of all old shares - (MigrationState::Required, _) | - (MigrationState::Started, _) => Some(ConnectionsAction::ConnectToMigrationSet), - } -} - -fn is_connected_to_all_nodes(self_node_id: &NodeId, nodes: &BTreeMap, connected: &BTreeSet) -> bool { - nodes.keys() - .filter(|n| *n != self_node_id) - .all(|n| connected.contains(n)) -} - -fn select_master_node(snapshot: &KeyServerSetSnapshot) -> &NodeId { - // we want to minimize a number of UnknownSession messages => - // try to select a node which was in SS && will be in SS - match snapshot.migration.as_ref() { - Some(migration) => &migration.master, - None => snapshot.current_set.keys() - .filter(|n| snapshot.new_set.contains_key(n)) - .nth(0) - .or_else(|| snapshot.new_set.keys().nth(0)) - .unwrap_or_else(|| snapshot.current_set.keys().nth(0) - .expect("select_master_node is only called when migration is Required or Started;\ - when Started: migration.is_some() && we return migration.master; qed;\ - when Required: current_set != new_set; this means that at least one set is non-empty; we try to take node from each set; qed")) - } -} - -#[cfg(test)] -mod tests { - use key_server_cluster::{KeyServerSetSnapshot, KeyServerSetMigration}; - use key_server_cluster::connection_trigger::ConnectionsAction; - use super::{MigrationState, SessionState, SessionAction, migration_state, maintain_session, - maintain_connections, select_master_node}; - use ethereum_types::{H256, H512}; - - #[test] - fn migration_state_is_idle_when_required_but_this_node_is_not_on_the_list() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(3), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Idle); - } - - #[test] - fn migration_state_is_idle_when_sets_are_equal() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Idle); - } - - #[test] - fn migration_state_is_idle_when_only_address_changes() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Idle); - } - - #[test] - fn migration_state_is_required_when_node_is_added() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Required); - } - - #[test] - fn migration_state_is_required_when_node_is_removed() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Required); - } - - #[test] - fn migration_state_is_started_when_migration_is_some() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - id: Default::default(), - set: Default::default(), - master: Default::default(), - is_confirmed: Default::default(), - }), - }), MigrationState::Started); - } - - #[test] - fn existing_master_is_selected_when_migration_has_started() { - assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(3), - ..Default::default() - }), - }), &H512::from_low_u64_be(3)); - } - - #[test] - fn persistent_master_is_selected_when_migration_has_not_started_yet() { - assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(4), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: None, - }), &H512::from_low_u64_be(2)); - } - - #[test] - fn new_master_is_selected_in_worst_case() { - assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(3), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(4), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: None, - }), &H512::from_low_u64_be(3)); - } - - #[test] - fn maintain_connections_returns_none_when_session_is_active() { - assert_eq!(maintain_connections(MigrationState::Required, - SessionState::Active(Default::default())), None); - } - - #[test] - fn maintain_connections_connects_to_current_set_when_no_migration() { - assert_eq!(maintain_connections(MigrationState::Idle, - SessionState::Idle), Some(ConnectionsAction::ConnectToCurrentSet)); - } - - #[test] - fn maintain_connections_connects_to_current_and_old_set_when_migration_is_required() { - assert_eq!(maintain_connections(MigrationState::Required, - SessionState::Idle), Some(ConnectionsAction::ConnectToMigrationSet)); - } - - #[test] - fn maintain_connections_connects_to_current_and_old_set_when_migration_is_started() { - assert_eq!(maintain_connections(MigrationState::Started, - SessionState::Idle), Some(ConnectionsAction::ConnectToMigrationSet)); - } - - #[test] - fn maintain_sessions_does_nothing_if_no_session_and_no_migration() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Idle), None); - } - - #[test] - fn maintain_session_does_nothing_when_migration_required_on_slave_node_and_no_session() { - assert_eq!(maintain_session(&H512::from_low_u64_be(2), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: None, - }, MigrationState::Required, SessionState::Idle), None); - } - - #[test] - fn maintain_session_does_nothing_when_migration_started_on_slave_node_and_no_session() { - assert_eq!(maintain_session(&H512::from_low_u64_be(2), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Idle), None); - } - - #[test] - fn maintain_session_does_nothing_when_migration_started_on_master_node_and_no_session_and_not_connected_to_migration_nodes() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &Default::default(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Idle), None); - } - - #[test] - fn maintain_session_starts_session_when_migration_started_on_master_node_and_no_session() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Idle), Some(SessionAction::Start)); - } - - #[test] - fn maintain_session_does_nothing_when_both_migration_and_session_are_started() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Active(Default::default())), None); - } - - #[test] - fn maintain_session_confirms_migration_when_active_and_session_has_finished_on_new_node() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Finished(Default::default())), Some(SessionAction::ConfirmAndDrop(Default::default()))); - } - - #[test] - fn maintain_session_drops_session_when_active_and_session_has_finished_on_removed_node() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(2), - set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Finished(Default::default())), Some(SessionAction::Drop)); - } - - #[test] - fn maintain_session_drops_session_when_active_and_session_has_failed() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Failed(Default::default())), Some(SessionAction::Drop)); - } - - #[test] - fn maintain_session_detects_abnormal_when_no_migration_and_active_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Active(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_no_migration_and_finished_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Finished(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_no_migration_and_failed_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Failed(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_required_migration_and_active_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Required, SessionState::Active(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_required_migration_and_finished_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Required, SessionState::Finished(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_required_migration_and_failed_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Required, SessionState::Failed(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_active_migration_and_active_session_with_different_id() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { - migration: Some(KeyServerSetMigration { - id: H256::zero(), - ..Default::default() - }), - ..Default::default() - }, MigrationState::Started, SessionState::Active(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_active_migration_and_finished_session_with_different_id() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { - migration: Some(KeyServerSetMigration { - id: H256::zero(), - ..Default::default() - }), - ..Default::default() - }, MigrationState::Started, SessionState::Finished(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_active_migration_and_failed_session_with_different_id() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { - migration: Some(KeyServerSetMigration { - id: H256::zero(), - ..Default::default() - }), - ..Default::default() - }, MigrationState::Started, SessionState::Failed(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); - } -} diff --git a/secret-store/src/key_server_cluster/io/deadline.rs b/secret-store/src/key_server_cluster/io/deadline.rs deleted file mode 100644 index cb960a514..000000000 --- a/secret-store/src/key_server_cluster/io/deadline.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use std::time::Duration; -use futures::{Future, Poll}; -use tokio::timer::timeout::{Timeout, Error as TimeoutError}; - -type DeadlineBox = Box::Item>, - Error = TimeoutError<::Error> -> + Send>; - -/// Complete a passed future or fail if it is not completed within timeout. -pub fn deadline(duration: Duration, future: F) -> Result, io::Error> - where F: Future + Send + 'static, T: Send + 'static -{ - let timeout = Box::new(Timeout::new(future, duration) - .then(|res| { - match res { - Ok(fut) => Ok(DeadlineStatus::Meet(fut)), - Err(err) => { - if err.is_elapsed() { - Ok(DeadlineStatus::Timeout) - } else { - Err(err) - } - }, - } - }) - ); - let deadline = Deadline { - future: timeout, - }; - Ok(deadline) -} - -/// Deadline future completion status. -#[derive(Debug, PartialEq)] -pub enum DeadlineStatus { - /// Completed a future. - Meet(T), - /// Faled with timeout. - Timeout, -} - -/// Future, which waits for passed future completion within given period, or fails with timeout. -pub struct Deadline where F: Future { - future: DeadlineBox, -} - -impl Future for Deadline where F: Future { - type Item = DeadlineStatus; - type Error = TimeoutError; - - fn poll(&mut self) -> Poll { - self.future.poll() - } -} - -#[cfg(test)] -mod tests { - use std::time::Duration; - use futures::{Future, done}; - use tokio::reactor::Reactor; - use super::{deadline, DeadlineStatus}; - - #[test] - fn deadline_result_works() { - let mut reactor = Reactor::new().unwrap(); - let deadline = deadline(Duration::from_millis(1000), done(Ok(()))).unwrap(); - reactor.turn(Some(Duration::from_millis(3))).unwrap(); - assert_eq!(deadline.wait().unwrap(), DeadlineStatus::Meet(())); - } -} diff --git a/secret-store/src/key_server_cluster/io/handshake.rs b/secret-store/src/key_server_cluster/io/handshake.rs deleted file mode 100644 index 69c87debc..000000000 --- a/secret-store/src/key_server_cluster/io/handshake.rs +++ /dev/null @@ -1,379 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -///! Given: two nodes each holding its own `self_key_pair`. -///! -///! Handshake process: -///! 1) both nodes are generating random `KeyPair` (`session_key_pair`), which will be used for channel encryption -///! 2) both nodes are generating random H256 (`confirmation_plain`) -///! 3) both nodes are signing `confirmation_plain` using `session_key_pair` to receive `confirmation_signed_session` -///! 4) nodes exchange with `NodePublicKey` messages, containing: `self_key_pair.public`, `confirmation_plain`, `confirmation_signed_session` -///! 5) both nodes are checking that they're configured to communicate to server with received `message.self_key_pair.public`. Connection is closed otherwise -///! 6) both nodes are recovering peer' `session_key_pair.public` from `message.confirmation_plain` and `message.confirmation_signed_session` -///! 7) both nodes are computing shared session key pair using self' `session_key_pair.secret` && peer' `session_key_pair.public`. All following messages are encrypted using this key_pair. -///! 8) both nodes are signing `message.confirmation_plain` with their own `self_key_pair.private` to receive `confirmation_signed` -///! 9) nodes exchange with `NodePrivateKeySignature` messages, containing `confirmation_signed` -///! 10) both nodes are checking that `confirmation_signed` is actually signed with the owner of peer' `self_key_pair.secret` -///! -///! Result of handshake is: -///! 1) belief, that we are connected to the KS from our KS-set -///! 2) session key pair, which is used to enrypt all connection messages - -use std::io; -use std::sync::Arc; -use std::collections::BTreeSet; -use futures::{Future, Poll, Async}; -use tokio_io::{AsyncRead, AsyncWrite}; -use crypto::publickey::ecdh::agree; -use crypto::publickey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover}; -use ethereum_types::H256; -use blockchain::SigningKeyPair; -use key_server_cluster::{NodeId, Error}; -use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; -use key_server_cluster::io::{write_message, write_encrypted_message, WriteMessage, ReadMessage, - read_message, read_encrypted_message, fix_shared_key}; - -/// Start handshake procedure with another node from the cluster. -pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { - let init_data = ( - *Random.generate().secret().clone(), - Random.generate() - ); - handshake_with_init_data(a, Ok(init_data), self_key_pair, trusted_nodes) -} - -/// Start handshake procedure with another node from the cluster and given plain confirmation + session key pair. -pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { - let handshake_input_data = init_data - .and_then(|(cp, kp)| sign(kp.secret(), &cp).map(|sp| (cp, kp, sp)).map_err(Into::into)) - .and_then(|(cp, kp, sp)| Handshake::::make_public_key_message(self_key_pair.public().clone(), cp.clone(), sp).map(|msg| (cp, kp, msg))); - - let (error, cp, kp, state) = match handshake_input_data { - Ok((cp, kp, msg)) => (None, cp, Some(kp), HandshakeState::SendPublicKey(write_message(a, msg))), - Err(err) => (Some((a, Err(err))), Default::default(), None, HandshakeState::Finished), - }; - - Handshake { - is_active: true, - error: error, - state: state, - self_key_pair: self_key_pair, - self_session_key_pair: kp, - self_confirmation_plain: cp, - trusted_nodes: Some(trusted_nodes), - peer_node_id: None, - peer_session_public: None, - peer_confirmation_plain: None, - shared_key: None, - } -} - -/// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { - let self_confirmation_plain = *Random.generate().secret().clone(); - let keypair = Random.generate(); - - Handshake { - is_active: false, - error: None, - state: HandshakeState::ReceivePublicKey(read_message(a)), - self_key_pair, - self_session_key_pair: Some(keypair), - self_confirmation_plain, - trusted_nodes: None, - peer_node_id: None, - peer_session_public: None, - peer_confirmation_plain: None, - shared_key: None, - } -} - -/// Result of handshake procedure. -#[derive(Debug, PartialEq)] -pub struct HandshakeResult { - /// Node id. - pub node_id: NodeId, - /// Shared key. - pub shared_key: KeyPair, -} - -/// Future handshake procedure. -pub struct Handshake { - is_active: bool, - error: Option<(A, Result)>, - state: HandshakeState, - self_key_pair: Arc, - self_session_key_pair: Option, - self_confirmation_plain: H256, - trusted_nodes: Option>, - peer_node_id: Option, - peer_session_public: Option, - peer_confirmation_plain: Option, - shared_key: Option, -} - -/// Active handshake state. -enum HandshakeState { - SendPublicKey(WriteMessage), - ReceivePublicKey(ReadMessage), - SendPrivateKeySignature(WriteMessage), - ReceivePrivateKeySignature(ReadMessage), - Finished, -} - -impl Handshake where A: AsyncRead + AsyncWrite { - #[cfg(test)] - pub fn set_self_confirmation_plain(&mut self, self_confirmation_plain: H256) { - self.self_confirmation_plain = self_confirmation_plain; - } - - #[cfg(test)] - pub fn set_self_session_key_pair(&mut self, self_session_key_pair: KeyPair) { - self.self_session_key_pair = Some(self_session_key_pair); - } - - pub fn make_public_key_message(self_node_id: NodeId, confirmation_plain: H256, confirmation_signed_session: Signature) -> Result { - Ok(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey { - node_id: self_node_id.into(), - confirmation_plain: confirmation_plain.into(), - confirmation_signed_session: confirmation_signed_session.into(), - }))) - } - - fn make_private_key_signature_message(self_key_pair: &dyn SigningKeyPair, confirmation_plain: &H256) -> Result { - Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { - confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), - }))) - } - - fn compute_shared_key(self_session_key_pair: &KeyPair, peer_session_public: &Public) -> Result { - agree(self_session_key_pair.secret(), peer_session_public) - .map_err(Into::into) - .and_then(|s| fix_shared_key(&s)) - } -} - -impl Future for Handshake where A: AsyncRead + AsyncWrite { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - if let Some(error_result) = self.error.take() { - return Ok(error_result.into()); - } - - let (next, result) = match self.state { - HandshakeState::SendPublicKey(ref mut future) => { - let (stream, _) = try_ready!(future.poll()); - - if self.is_active { - (HandshakeState::ReceivePublicKey( - read_message(stream) - ), Async::NotReady) - } else { - let shared_key = Self::compute_shared_key( - self.self_session_key_pair.as_ref().expect( - "self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"), - self.peer_session_public.as_ref().expect( - "we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_session_public is filled in ReceivePublicKey; qed"), - ); - - self.shared_key = match shared_key { - Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), - }; - - let peer_confirmation_plain = self.peer_confirmation_plain.as_ref() - .expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_confirmation_plain is filled in ReceivePublicKey; qed"); - let message = match Handshake::::make_private_key_signature_message(&*self.self_key_pair, peer_confirmation_plain) { - Ok(message) => message, - Err(err) => return Ok((stream, Err(err)).into()), - }; - - (HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream, - self.shared_key.as_ref().expect("filled couple of lines above; qed"), - message)), Async::NotReady) - } - }, - HandshakeState::ReceivePublicKey(ref mut future) => { - let (stream, message) = try_ready!(future.poll()); - - let message = match message { - Ok(message) => match message { - Message::Cluster(ClusterMessage::NodePublicKey(message)) => message, - _ => return Ok((stream, Err(Error::InvalidMessage)).into()), - }, - Err(err) => return Ok((stream, Err(err.into())).into()), - }; - - if !self.trusted_nodes.as_ref().map(|tn| tn.contains(&*message.node_id)).unwrap_or(true) { - return Ok((stream, Err(Error::InvalidNodeId)).into()); - } - - self.peer_node_id = Some(message.node_id.into()); - self.peer_session_public = Some(match recover(&message.confirmation_signed_session, &message.confirmation_plain) { - Ok(peer_session_public) => peer_session_public, - Err(err) => return Ok((stream, Err(err.into())).into()), - }); - self.peer_confirmation_plain = Some(message.confirmation_plain.into()); - if self.is_active { - let shared_key = Self::compute_shared_key( - self.self_session_key_pair.as_ref().expect( - "self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"), - self.peer_session_public.as_ref().expect( - "we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_session_public is filled in ReceivePublicKey; qed"), - ); - - self.shared_key = match shared_key { - Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), - }; - - let peer_confirmation_plain = self.peer_confirmation_plain.as_ref() - .expect("filled couple of lines above; qed"); - let message = match Handshake::::make_private_key_signature_message(&*self.self_key_pair, peer_confirmation_plain) { - Ok(message) => message, - Err(err) => return Ok((stream, Err(err)).into()), - }; - - (HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream, - self.shared_key.as_ref().expect("filled couple of lines above; qed"), - message)), Async::NotReady) - } else { - let self_session_key_pair = self.self_session_key_pair.as_ref() - .expect("self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"); - let confirmation_signed_session = match sign(self_session_key_pair.secret(), &self.self_confirmation_plain).map_err(Into::into) { - Ok(confirmation_signed_session) => confirmation_signed_session, - Err(err) => return Ok((stream, Err(err)).into()), - }; - - let message = match Handshake::::make_public_key_message(self.self_key_pair.public().clone(), self.self_confirmation_plain.clone(), confirmation_signed_session) { - Ok(message) => message, - Err(err) => return Ok((stream, Err(err)).into()), - }; - (HandshakeState::SendPublicKey(write_message(stream, message)), Async::NotReady) - } - }, - HandshakeState::SendPrivateKeySignature(ref mut future) => { - let (stream, _) = try_ready!(future.poll()); - - (HandshakeState::ReceivePrivateKeySignature( - read_encrypted_message(stream, - self.shared_key.as_ref().expect("shared_key is filled in Send/ReceivePublicKey; SendPrivateKeySignature follows Send/ReceivePublicKey; qed").clone() - ) - ), Async::NotReady) - }, - HandshakeState::ReceivePrivateKeySignature(ref mut future) => { - let (stream, message) = try_ready!(future.poll()); - - let message = match message { - Ok(message) => match message { - Message::Cluster(ClusterMessage::NodePrivateKeySignature(message)) => message, - _ => return Ok((stream, Err(Error::InvalidMessage)).into()), - }, - Err(err) => return Ok((stream, Err(err.into())).into()), - }; - - let peer_public = self.peer_node_id.as_ref().expect("peer_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"); - if !verify_public(peer_public, &*message.confirmation_signed, &self.self_confirmation_plain).unwrap_or(false) { - return Ok((stream, Err(Error::InvalidMessage)).into()); - } - - (HandshakeState::Finished, Async::Ready((stream, Ok(HandshakeResult { - node_id: self.peer_node_id.expect("peer_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"), - shared_key: self.shared_key.clone().expect("shared_key is filled in Send/ReceivePublicKey; ReceivePrivateKeySignature follows Send/ReceivePublicKey; qed"), - })))) - }, - HandshakeState::Finished => panic!("poll Handshake after it's done"), - }; - - self.state = next; - match result { - // by polling again, we register new future - Async::NotReady => self.poll(), - result => Ok(result) - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::collections::BTreeSet; - use futures::Future; - use crypto::publickey::{Random, Generator, sign}; - use ethereum_types::H256; - use key_server_cluster::PlainNodeKeyPair; - use key_server_cluster::io::message::tests::TestIo; - use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; - use super::{handshake_with_init_data, accept_handshake, HandshakeResult}; - - fn prepare_test_io() -> (H256, TestIo) { - let mut io = TestIo::new(); - - let self_confirmation_plain = *Random.generate().secret().clone(); - let peer_confirmation_plain = *Random.generate().secret().clone(); - - let self_confirmation_signed = sign(io.peer_key_pair().secret(), &self_confirmation_plain).unwrap(); - let peer_confirmation_signed = sign(io.peer_session_key_pair().secret(), &peer_confirmation_plain).unwrap(); - - let peer_public = io.peer_key_pair().public().clone(); - io.add_input_message(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey { - node_id: peer_public.into(), - confirmation_plain: peer_confirmation_plain.into(), - confirmation_signed_session: peer_confirmation_signed.into(), - }))); - io.add_encrypted_input_message(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { - confirmation_signed: self_confirmation_signed.into(), - }))); - - (self_confirmation_plain, io) - } - - #[test] - fn active_handshake_works() { - let (self_confirmation_plain, io) = prepare_test_io(); - let trusted_nodes: BTreeSet<_> = vec![io.peer_key_pair().public().clone()].into_iter().collect(); - let self_session_key_pair = io.self_session_key_pair().clone(); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(io.self_key_pair().clone())); - let shared_key = io.shared_key_pair().clone(); - - let handshake = handshake_with_init_data(io, Ok((self_confirmation_plain, self_session_key_pair)), self_key_pair, trusted_nodes); - let handshake_result = handshake.wait().unwrap(); - assert_eq!(handshake_result.1, Ok(HandshakeResult { - node_id: handshake_result.0.peer_key_pair().public().clone(), - shared_key: shared_key, - })); - } - - #[test] - fn passive_handshake_works() { - let (self_confirmation_plain, io) = prepare_test_io(); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(io.self_key_pair().clone())); - let self_session_key_pair = io.self_session_key_pair().clone(); - let shared_key = io.shared_key_pair().clone(); - - let mut handshake = accept_handshake(io, self_key_pair); - handshake.set_self_confirmation_plain(self_confirmation_plain); - handshake.set_self_session_key_pair(self_session_key_pair); - - let handshake_result = handshake.wait().unwrap(); - assert_eq!(handshake_result.1, Ok(HandshakeResult { - node_id: handshake_result.0.peer_key_pair().public().clone(), - shared_key: shared_key, - })); - } -} diff --git a/secret-store/src/key_server_cluster/io/message.rs b/secret-store/src/key_server_cluster/io/message.rs deleted file mode 100644 index 9ff45974c..000000000 --- a/secret-store/src/key_server_cluster/io/message.rs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io::Cursor; -use std::u16; -use std::ops::Deref; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use serde_json; -use crypto::publickey::ecies; -use crypto::publickey::{Secret, KeyPair}; -use crypto::publickey::ec_math_utils::CURVE_ORDER; -use ethereum_types::{H256, U256, BigEndianHash}; -use key_server_cluster::Error; -use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, - SchnorrSigningMessage, EcdsaSigningMessage, ServersSetChangeMessage, ShareAddMessage, KeyVersionNegotiationMessage}; - -/// Size of serialized header. -pub const MESSAGE_HEADER_SIZE: usize = 18; -/// Current header version. -pub const CURRENT_HEADER_VERSION: u64 = 1; - -/// Message header. -#[derive(Debug, PartialEq)] -pub struct MessageHeader { - /// Message/Header version. - pub version: u64, - /// Message kind. - pub kind: u64, - /// Message payload size (without header). - pub size: u16, -} - -/// Serialized message. -#[derive(Debug, Clone, PartialEq)] -pub struct SerializedMessage(Vec); - -impl Deref for SerializedMessage { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - &self.0 - } -} - -impl Into> for SerializedMessage { - fn into(self) -> Vec { - self.0 - } -} - -/// Serialize message. -pub fn serialize_message(message: Message) -> Result { - let (message_kind, payload) = match message { - Message::Cluster(ClusterMessage::NodePublicKey(payload)) => (1, serde_json::to_vec(&payload)), - Message::Cluster(ClusterMessage::NodePrivateKeySignature(payload)) => (2, serde_json::to_vec(&payload)), - Message::Cluster(ClusterMessage::KeepAlive(payload)) => (3, serde_json::to_vec(&payload)), - Message::Cluster(ClusterMessage::KeepAliveResponse(payload)) => (4, serde_json::to_vec(&payload)), - - Message::Generation(GenerationMessage::InitializeSession(payload)) => (50, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::ConfirmInitialization(payload)) => (51, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::CompleteInitialization(payload)) => (52, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::KeysDissemination(payload)) => (53, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::PublicKeyShare(payload)) => (54, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::SessionError(payload)) => (55, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::SessionCompleted(payload)) => (56, serde_json::to_vec(&payload)), - - Message::Encryption(EncryptionMessage::InitializeEncryptionSession(payload)) => (100, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(payload)) => (101, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::EncryptionSessionError(payload)) => (102, serde_json::to_vec(&payload)), - - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(payload)) => (150, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::RequestPartialDecryption(payload)) => (151, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (152, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (153, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (154, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(payload)) => (155, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(payload)) - => (156, serde_json::to_vec(&payload)), - - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(payload)) - => (200, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(payload)) - => (201, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(payload)) - => (202, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(payload)) => (203, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(payload)) => (204, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(payload)) - => (205, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(payload)) - => (206, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(payload)) - => (207, serde_json::to_vec(&payload)), - - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(payload)) - => (250, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(payload)) => (251, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::UnknownSessions(payload)) => (252, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(payload)) - => (253, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::InitializeShareChangeSession(payload)) - => (254, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(payload)) - => (255, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegate(payload)) - => (256, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegateResponse(payload)) - => (257, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeShareAddMessage(payload)) - => (258, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(payload)) => (261, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(payload)) - => (262, serde_json::to_vec(&payload)), - - Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(payload)) => (300, serde_json::to_vec(&payload)), - Message::ShareAdd(ShareAddMessage::KeyShareCommon(payload)) => (301, serde_json::to_vec(&payload)), - Message::ShareAdd(ShareAddMessage::NewKeysDissemination(payload)) => (302, serde_json::to_vec(&payload)), - Message::ShareAdd(ShareAddMessage::ShareAddError(payload)) => (303, serde_json::to_vec(&payload)), - - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(payload)) - => (450, serde_json::to_vec(&payload)), - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersions(payload)) - => (451, serde_json::to_vec(&payload)), - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(payload)) - => (452, serde_json::to_vec(&payload)), - - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(payload)) => (500, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(payload)) - => (501, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(payload)) - => (502, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(payload)) - => (503, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(payload)) - => (504, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(payload)) => (505, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(payload)) => (506, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(payload)) => (507, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(payload)) => (508, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(payload)) => (509, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(payload)) - => (510, serde_json::to_vec(&payload)), - }; - - let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; - build_serialized_message(MessageHeader { - kind: message_kind, - version: CURRENT_HEADER_VERSION, - size: 0, - }, payload) -} - -/// Deserialize message. -pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result { - Ok(match header.kind { - 1 => Message::Cluster(ClusterMessage::NodePublicKey(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 2 => Message::Cluster(ClusterMessage::NodePrivateKeySignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 3 => Message::Cluster(ClusterMessage::KeepAlive(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 4 => Message::Cluster(ClusterMessage::KeepAliveResponse(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 50 => Message::Generation(GenerationMessage::InitializeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 51 => Message::Generation(GenerationMessage::ConfirmInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 52 => Message::Generation(GenerationMessage::CompleteInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 53 => Message::Generation(GenerationMessage::KeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 54 => Message::Generation(GenerationMessage::PublicKeyShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 55 => Message::Generation(GenerationMessage::SessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 56 => Message::Generation(GenerationMessage::SessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 100 => Message::Encryption(EncryptionMessage::InitializeEncryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 101 => Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 102 => Message::Encryption(EncryptionMessage::EncryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 150 => Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 151 => Message::Decryption(DecryptionMessage::RequestPartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 152 => Message::Decryption(DecryptionMessage::PartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 153 => Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 154 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 155 => Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 156 => Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 200 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 201 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 202 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 203 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 204 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 205 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 206 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 207 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 250 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 251 => Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 252 => Message::ServersSetChange(ServersSetChangeMessage::UnknownSessions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 253 => Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 254 => Message::ServersSetChange(ServersSetChangeMessage::InitializeShareChangeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 255 => Message::ServersSetChange(ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 256 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegate(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 257 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegateResponse(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 258 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeShareAddMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 261 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 262 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 300 => Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 301 => Message::ShareAdd(ShareAddMessage::KeyShareCommon(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 302 => Message::ShareAdd(ShareAddMessage::NewKeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 303 => Message::ShareAdd(ShareAddMessage::ShareAddError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 450 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 451 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 452 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 500 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 501 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 502 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 503 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 504 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 505 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 506 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 507 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 508 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 509 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 510 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - _ => return Err(Error::Serde(format!("unknown message type {}", header.kind))), - }) -} - -/// Encrypt serialized message. -pub fn encrypt_message(key: &KeyPair, message: SerializedMessage) -> Result { - let mut header: Vec<_> = message.into(); - let payload = header.split_off(MESSAGE_HEADER_SIZE); - let encrypted_payload = ecies::encrypt(key.public(), &[], &payload)?; - - let header = deserialize_header(&header)?; - build_serialized_message(header, encrypted_payload) -} - -/// Decrypt serialized message. -pub fn decrypt_message(key: &KeyPair, payload: Vec) -> Result, Error> { - Ok(ecies::decrypt(key.secret(), &[], &payload)?) -} - -/// Fix shared encryption key. -pub fn fix_shared_key(shared_secret: &Secret) -> Result { - // secret key created in agree function is invalid, as it is not calculated mod EC.field.n - // => let's do it manually - let shared_secret: H256 = (**shared_secret).into(); - let shared_secret: U256 = shared_secret.into_uint(); - let shared_secret: H256 = BigEndianHash::from_uint(&(shared_secret % *CURVE_ORDER)); - let shared_key_pair = KeyPair::from_secret_slice(shared_secret.as_bytes())?; - Ok(shared_key_pair) -} - -/// Serialize message header. -fn serialize_header(header: &MessageHeader) -> Result, Error> { - let mut buffer = Vec::with_capacity(MESSAGE_HEADER_SIZE); - buffer.write_u64::(header.version)?; - buffer.write_u64::(header.kind)?; - buffer.write_u16::(header.size)?; - Ok(buffer) -} - -/// Deserialize message header. -pub fn deserialize_header(data: &[u8]) -> Result { - let mut reader = Cursor::new(data); - let version = reader.read_u64::()?; - if version != CURRENT_HEADER_VERSION { - return Err(Error::InvalidMessageVersion); - } - - Ok(MessageHeader { - version: version, - kind: reader.read_u64::()?, - size: reader.read_u16::()?, - }) -} - -/// Build serialized message from header && payload -fn build_serialized_message(mut header: MessageHeader, payload: Vec) -> Result { - let payload_len = payload.len(); - if payload_len > u16::MAX as usize { - return Err(Error::InvalidMessage); - } - header.size = payload.len() as u16; - - let mut message = serialize_header(&header)?; - message.extend(payload); - Ok(SerializedMessage(message)) -} - -#[cfg(test)] -pub mod tests { - use std::io; - use futures::Poll; - use tokio_io::{AsyncRead, AsyncWrite}; - use crypto::publickey::{Random, Generator, KeyPair}; - use crypto::publickey::ecdh::agree; - use key_server_cluster::Error; - use key_server_cluster::message::Message; - use super::{MESSAGE_HEADER_SIZE, CURRENT_HEADER_VERSION, MessageHeader, fix_shared_key, encrypt_message, - serialize_message, serialize_header, deserialize_header}; - - pub struct TestIo { - self_key_pair: KeyPair, - self_session_key_pair: KeyPair, - peer_key_pair: KeyPair, - peer_session_key_pair: KeyPair, - shared_key_pair: KeyPair, - input_buffer: io::Cursor>, - } - - impl TestIo { - pub fn new() -> Self { - let self_session_key_pair = Random.generate(); - let peer_session_key_pair = Random.generate(); - let self_key_pair = Random.generate(); - let peer_key_pair = Random.generate(); - let shared_key_pair = fix_shared_key(&agree(self_session_key_pair.secret(), peer_session_key_pair.public()).unwrap()).unwrap(); - TestIo { - self_key_pair: self_key_pair, - self_session_key_pair: self_session_key_pair, - peer_key_pair: peer_key_pair, - peer_session_key_pair: peer_session_key_pair, - shared_key_pair: shared_key_pair, - input_buffer: io::Cursor::new(Vec::new()), - } - } - - pub fn self_key_pair(&self) -> &KeyPair { - &self.self_key_pair - } - - pub fn self_session_key_pair(&self) -> &KeyPair { - &self.self_session_key_pair - } - - pub fn peer_key_pair(&self) -> &KeyPair { - &self.peer_key_pair - } - - pub fn peer_session_key_pair(&self) -> &KeyPair { - &self.peer_session_key_pair - } - - pub fn shared_key_pair(&self) -> &KeyPair { - &self.shared_key_pair - } - - pub fn add_input_message(&mut self, message: Message) { - let serialized_message = serialize_message(message).unwrap(); - let serialized_message: Vec<_> = serialized_message.into(); - let input_buffer = self.input_buffer.get_mut(); - for b in serialized_message { - input_buffer.push(b); - } - } - - pub fn add_encrypted_input_message(&mut self, message: Message) { - let serialized_message = encrypt_message(&self.shared_key_pair, serialize_message(message).unwrap()).unwrap(); - let serialized_message: Vec<_> = serialized_message.into(); - let input_buffer = self.input_buffer.get_mut(); - for b in serialized_message { - input_buffer.push(b); - } - } - } - - impl AsyncRead for TestIo {} - - impl AsyncWrite for TestIo { - fn shutdown(&mut self) -> Poll<(), io::Error> { - Ok(().into()) - } - } - - impl io::Read for TestIo { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - io::Read::read(&mut self.input_buffer, buf) - } - } - - impl io::Write for TestIo { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - #[test] - fn header_serialization_works() { - let header = MessageHeader { - kind: 1, - version: CURRENT_HEADER_VERSION, - size: 3, - }; - - let serialized_header = serialize_header(&header).unwrap(); - assert_eq!(serialized_header.len(), MESSAGE_HEADER_SIZE); - - let deserialized_header = deserialize_header(&serialized_header).unwrap(); - assert_eq!(deserialized_header, header); - } - - #[test] - fn deserializing_header_of_wrong_version_fails() { - let header = MessageHeader { - kind: 1, - version: CURRENT_HEADER_VERSION + 1, - size: 3, - }; - - assert_eq!(deserialize_header(&serialize_header(&header).unwrap()).unwrap_err(), Error::InvalidMessageVersion); - } -} diff --git a/secret-store/src/key_server_cluster/io/mod.rs b/secret-store/src/key_server_cluster/io/mod.rs deleted file mode 100644 index 0b5e71144..000000000 --- a/secret-store/src/key_server_cluster/io/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -mod deadline; -mod handshake; -mod message; -mod read_header; -mod read_payload; -mod read_message; -mod shared_tcp_stream; -mod write_message; - -pub use self::deadline::{deadline, Deadline, DeadlineStatus}; -pub use self::handshake::{handshake, accept_handshake, Handshake, HandshakeResult}; -pub use self::message::{MessageHeader, SerializedMessage, serialize_message, deserialize_message, - encrypt_message, fix_shared_key}; -pub use self::read_header::{read_header, ReadHeader}; -pub use self::read_payload::{read_payload, read_encrypted_payload, ReadPayload}; -pub use self::read_message::{read_message, read_encrypted_message, ReadMessage}; -pub use self::shared_tcp_stream::SharedTcpStream; -pub use self::write_message::{write_message, write_encrypted_message, WriteMessage}; diff --git a/secret-store/src/key_server_cluster/io/read_header.rs b/secret-store/src/key_server_cluster/io/read_header.rs deleted file mode 100644 index c484921b7..000000000 --- a/secret-store/src/key_server_cluster/io/read_header.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Future, Poll, Async}; -use tokio_io::AsyncRead; -use tokio_io::io::{ReadExact, read_exact}; -use key_server_cluster::Error; -use key_server_cluster::io::message::{MESSAGE_HEADER_SIZE, MessageHeader, deserialize_header}; - -/// Create future for read single message header from the stream. -pub fn read_header(a: A) -> ReadHeader where A: AsyncRead { - ReadHeader { - reader: read_exact(a, [0; MESSAGE_HEADER_SIZE]), - } -} - -/// Future for read single message header from the stream. -pub struct ReadHeader { - reader: ReadExact, -} - -impl Future for ReadHeader where A: AsyncRead { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (read, data) = try_ready!(self.reader.poll()); - let header = deserialize_header(&data); - Ok(Async::Ready((read, header))) - } -} diff --git a/secret-store/src/key_server_cluster/io/read_message.rs b/secret-store/src/key_server_cluster/io/read_message.rs deleted file mode 100644 index 18402c27e..000000000 --- a/secret-store/src/key_server_cluster/io/read_message.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Poll, Future, Async}; -use tokio_io::AsyncRead; -use crypto::publickey::KeyPair; -use key_server_cluster::Error; -use key_server_cluster::message::Message; -use key_server_cluster::io::{read_header, ReadHeader, read_payload, read_encrypted_payload, ReadPayload}; - -/// Create future for read single message from the stream. -pub fn read_message(a: A) -> ReadMessage where A: AsyncRead { - ReadMessage { - key: None, - state: ReadMessageState::ReadHeader(read_header(a)), - } -} - -/// Create future for read single encrypted message from the stream. -pub fn read_encrypted_message(a: A, key: KeyPair) -> ReadMessage where A: AsyncRead { - ReadMessage { - key: Some(key), - state: ReadMessageState::ReadHeader(read_header(a)), - } -} - -enum ReadMessageState { - ReadHeader(ReadHeader), - ReadPayload(ReadPayload), - Finished, -} - -/// Future for read single message from the stream. -pub struct ReadMessage { - key: Option, - state: ReadMessageState, -} - -impl Future for ReadMessage where A: AsyncRead { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (next, result) = match self.state { - ReadMessageState::ReadHeader(ref mut future) => { - let (read, header) = try_ready!(future.poll()); - let header = match header { - Ok(header) => header, - Err(err) => return Ok((read, Err(err)).into()), - }; - - let future = match self.key.take() { - Some(key) => read_encrypted_payload(read, header, key), - None => read_payload(read, header), - }; - let next = ReadMessageState::ReadPayload(future); - (next, Async::NotReady) - }, - ReadMessageState::ReadPayload(ref mut future) => { - let (read, payload) = try_ready!(future.poll()); - (ReadMessageState::Finished, Async::Ready((read, payload))) - }, - ReadMessageState::Finished => panic!("poll ReadMessage after it's done"), - }; - - self.state = next; - match result { - // by polling again, we register new future - Async::NotReady => self.poll(), - result => Ok(result) - } - } -} diff --git a/secret-store/src/key_server_cluster/io/read_payload.rs b/secret-store/src/key_server_cluster/io/read_payload.rs deleted file mode 100644 index fee466c53..000000000 --- a/secret-store/src/key_server_cluster/io/read_payload.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Poll, Future}; -use tokio_io::AsyncRead; -use tokio_io::io::{read_exact, ReadExact}; -use crypto::publickey::KeyPair; -use key_server_cluster::Error; -use key_server_cluster::message::Message; -use key_server_cluster::io::message::{MessageHeader, deserialize_message, decrypt_message}; - -/// Create future for read single message payload from the stream. -pub fn read_payload(a: A, header: MessageHeader) -> ReadPayload where A: AsyncRead { - ReadPayload { - reader: read_exact(a, vec![0; header.size as usize]), - header: header, - key: None, - } -} - -/// Create future for read single encrypted message payload from the stream. -pub fn read_encrypted_payload(a: A, header: MessageHeader, key: KeyPair) -> ReadPayload where A: AsyncRead { - ReadPayload { - reader: read_exact(a, vec![0; header.size as usize]), - header: header, - key: Some(key), - } -} - -/// Future for read single message payload from the stream. -pub struct ReadPayload { - reader: ReadExact>, - header: MessageHeader, - key: Option, -} - -impl Future for ReadPayload where A: AsyncRead { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (read, data) = try_ready!(self.reader.poll()); - let payload = if let Some(key) = self.key.take() { - decrypt_message(&key, data) - .and_then(|data| deserialize_message(&self.header, data)) - } else { - deserialize_message(&self.header, data) - }; - Ok((read, payload).into()) - } -} diff --git a/secret-store/src/key_server_cluster/io/shared_tcp_stream.rs b/secret-store/src/key_server_cluster/io/shared_tcp_stream.rs deleted file mode 100644 index a6a533a87..000000000 --- a/secret-store/src/key_server_cluster/io/shared_tcp_stream.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::net::Shutdown; -use std::io::{Read, Write, Error}; -use futures::Poll; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio::net::TcpStream; - -/// Read+Write implementation for Arc. -pub struct SharedTcpStream { - io: Arc, -} - -impl SharedTcpStream { - pub fn new(a: Arc) -> Self { - SharedTcpStream { - io: a, - } - } -} - -impl From for SharedTcpStream { - fn from(a: TcpStream) -> Self { - SharedTcpStream::new(Arc::new(a)) - } -} - -impl AsyncRead for SharedTcpStream {} - -impl AsyncWrite for SharedTcpStream { - fn shutdown(&mut self) -> Poll<(), Error> { - self.io.shutdown(Shutdown::Both).map(Into::into) - } -} - -impl Read for SharedTcpStream { - fn read(&mut self, buf: &mut [u8]) -> Result { - Read::read(&mut (&*self.io as &TcpStream), buf) - } -} - -impl Write for SharedTcpStream { - fn write(&mut self, buf: &[u8]) -> Result { - Write::write(&mut (&*self.io as &TcpStream), buf) - } - - fn flush(&mut self) -> Result<(), Error> { - Write::flush(&mut (&*self.io as &TcpStream)) - } -} - -impl Clone for SharedTcpStream { - fn clone(&self) -> Self { - SharedTcpStream::new(self.io.clone()) - } -} diff --git a/secret-store/src/key_server_cluster/io/write_message.rs b/secret-store/src/key_server_cluster/io/write_message.rs deleted file mode 100644 index 05259eb3a..000000000 --- a/secret-store/src/key_server_cluster/io/write_message.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Future, Poll}; -use tokio_io::AsyncWrite; -use tokio_io::io::{WriteAll, write_all}; -use crypto::publickey::KeyPair; -use key_server_cluster::message::Message; -use key_server_cluster::io::{serialize_message, encrypt_message}; - -/// Write plain message to the channel. -pub fn write_message(a: A, message: Message) -> WriteMessage where A: AsyncWrite { - let (error, future) = match serialize_message(message) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) { - Ok(message) => (None, write_all(a, message.into())), - Err(error) => (Some(error), write_all(a, Vec::new())), - }; - WriteMessage { - error: error, - future: future, - } -} - -/// Write encrypted message to the channel. -pub fn write_encrypted_message(a: A, key: &KeyPair, message: Message) -> WriteMessage where A: AsyncWrite { - let (error, future) = match serialize_message(message) - .and_then(|message| encrypt_message(key, message)) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) { - Ok(message) => (None, write_all(a, message.into())), - Err(error) => (Some(error), write_all(a, Vec::new())), - }; - - WriteMessage { - error: error, - future: future, - } -} - -/// Future message write. -pub struct WriteMessage { - error: Option, - future: WriteAll>, -} - -impl Future for WriteMessage where A: AsyncWrite { - type Item = (A, Vec); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - if let Some(err) = self.error.take() { - return Err(err); - } - - self.future.poll() - } -} diff --git a/secret-store/src/key_server_cluster/jobs/consensus_session.rs b/secret-store/src/key_server_cluster/jobs/consensus_session.rs deleted file mode 100644 index 183afd993..000000000 --- a/secret-store/src/key_server_cluster/jobs/consensus_session.rs +++ /dev/null @@ -1,793 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use key_server_cluster::{Error, NodeId, SessionMeta, Requester}; -use key_server_cluster::message::ConsensusMessage; -use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor, JobPartialRequestAction}; - -/// Consensus session state. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ConsensusSessionState { - /// Every node starts in this state. - WaitingForInitialization, - /// Consensus group is establishing. - EstablishingConsensus, - /// Consensus group is established. - /// Master node can start jobs dissemination. - /// Slave node waits for partial job requests. - ConsensusEstablished, - /// Master node waits for partial jobs responses. - WaitingForPartialResults, - /// Consensus session is completed successfully. - /// Master node can call result() to get computation result. - Finished, - /// Consensus session has failed with error. - Failed, -} - -/// Consensus session consists of following states: -/// 1) consensus group is established -/// 2) master node sends partial job requests to every member of consensus group -/// 3) slave nodes are computing partial responses -/// 4) master node computes result from partial responses -pub struct ConsensusSession, - ConsensusTransport: JobTransport, - ComputationExecutor: JobExecutor, - ComputationTransport: JobTransport -> { - /// Current session state. - state: ConsensusSessionState, - /// Session metadata. - meta: SessionMeta, - /// Consensus establish job. - consensus_job: JobSession, - /// Consensus group. - consensus_group: BTreeSet, - /// Computation job. - computation_job: Option>, -} - -/// Consensus session creation parameters. -pub struct ConsensusSessionParams, - ConsensusTransport: JobTransport -> { - /// Session metadata. - pub meta: SessionMeta, - /// ACL storage for access check. - pub consensus_executor: ConsensusExecutor, - /// Transport for consensus establish job. - pub consensus_transport: ConsensusTransport, -} - -impl ConsensusSession - where ConsensusExecutor: JobExecutor>, - ConsensusTransport: JobTransport, - ComputationExecutor: JobExecutor, - ComputationTransport: JobTransport { - /// Create new consensus session. - pub fn new(params: ConsensusSessionParams) -> Result { - let consensus_job = JobSession::new(params.meta.clone(), params.consensus_executor, params.consensus_transport); - debug_assert!(consensus_job.state() == JobSessionState::Inactive); - - Ok(ConsensusSession { - state: ConsensusSessionState::WaitingForInitialization, - meta: params.meta, - consensus_job: consensus_job, - consensus_group: BTreeSet::new(), - computation_job: None, - }) - } - - /// Get consensus job reference. - pub fn consensus_job(&self) -> &JobSession { - &self.consensus_job - } - - /// Get mutable consensus job reference. - pub fn consensus_job_mut(&mut self) -> &mut JobSession { - &mut self.consensus_job - } - - /// Get all nodes, which has not rejected consensus request. - pub fn consensus_non_rejected_nodes(&self) -> BTreeSet { - self.consensus_job.responses().iter() - .filter(|r| *r.1) - .map(|r| r.0) - .chain(self.consensus_job.requests()) - .filter(|n| **n != self.meta.self_node_id) - .cloned() - .collect() - } - - /// Get computation job reference. - pub fn computation_job(&self) -> &JobSession { - self.computation_job.as_ref() - .expect("computation_job must only be called on master nodes") - } - - /// Get consensus session state. - pub fn state(&self) -> ConsensusSessionState { - self.state - } - - /// Get computation result. - pub fn result(&self) -> Result { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - if self.state != ConsensusSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - self.computation_job.as_ref() - .expect("we are on master node in finished state; computation_job is set on master node during initialization; qed") - .result() - } - - /// Initialize session on master node. - pub fn initialize(&mut self, nodes: BTreeSet) -> Result<(), Error> { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - let initialization_result = self.consensus_job.initialize(nodes, None, false); - self.state = ConsensusSessionState::EstablishingConsensus; - self.process_result(initialization_result.map(|_| ())) - } - - /// Process consensus request message. - pub fn on_consensus_partial_request(&mut self, sender: &NodeId, request: ConsensusExecutor::PartialJobRequest) -> Result<(), Error> { - let consensus_result = self.consensus_job.on_partial_request(sender, request); - self.process_result(consensus_result.map(|_| ())) - } - - /// Process consensus message response. - pub fn on_consensus_partial_response(&mut self, sender: &NodeId, response: bool) -> Result<(), Error> { - let consensus_result = self.consensus_job.on_partial_response(sender, response); - self.process_result(consensus_result) - } - - /// Select nodes for processing partial requests. - pub fn select_consensus_group(&mut self) -> Result<&BTreeSet, Error> { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - if self.state != ConsensusSessionState::ConsensusEstablished { - return Err(Error::InvalidStateForRequest); - } - - if self.consensus_group.is_empty() { - let consensus_group = self.consensus_job.result()?; - let is_self_in_consensus = consensus_group.contains(&self.meta.self_node_id); - self.consensus_group = consensus_group.into_iter().take(self.meta.threshold + 1).collect(); - - if is_self_in_consensus { - self.consensus_group.remove(&self.meta.master_node_id); - self.consensus_group.insert(self.meta.master_node_id.clone()); - } - } - - Ok(&self.consensus_group) - } - - /// Disseminate jobs from master node. - pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport, broadcast_self_response: bool) -> Result, Error> { - let consensus_group = self.select_consensus_group()?.clone(); - self.consensus_group.clear(); - - let mut computation_job = JobSession::new(self.meta.clone(), executor, transport); - let computation_result = computation_job.initialize(consensus_group, None, broadcast_self_response); - self.computation_job = Some(computation_job); - self.state = ConsensusSessionState::WaitingForPartialResults; - match computation_result { - Ok(computation_result) => self.process_result(Ok(())).map(|_| computation_result), - Err(error) => Err(self.process_result(Err(error)).unwrap_err()), - } - } - - /// Process job request on slave node. - pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result, Error> { - if &self.meta.master_node_id != node { - return Err(Error::InvalidMessage); - } - if self.state != ConsensusSessionState::ConsensusEstablished { - return Err(Error::InvalidStateForRequest); - } - - JobSession::new(self.meta.clone(), executor, transport).on_partial_request(node, request) - } - - /// Process job response on slave node. - pub fn on_job_response(&mut self, node: &NodeId, response: ComputationExecutor::PartialJobResponse) -> Result<(), Error> { - if self.state != ConsensusSessionState::WaitingForPartialResults { - return Err(Error::InvalidStateForRequest); - } - - let computation_result = self.computation_job.as_mut() - .expect("WaitingForPartialResults is only set when computation_job is created; qed") - .on_partial_response(node, response); - - self.process_result(computation_result) - } - - /// When session is completed on slave node. - pub fn on_session_completed(&mut self, node: &NodeId) -> Result<(), Error> { - if node != &self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.state != ConsensusSessionState::ConsensusEstablished { - return Err(Error::InvalidStateForRequest); - } - - self.state = ConsensusSessionState::Finished; - - Ok(()) - } - - /// When error is received from node. - pub fn on_node_error(&mut self, node: &NodeId, error: Error) -> Result { - let is_self_master = self.meta.master_node_id == self.meta.self_node_id; - let is_node_master = self.meta.master_node_id == *node; - let (is_restart_needed, timeout_result) = match self.state { - ConsensusSessionState::WaitingForInitialization if is_self_master => { - // it is strange to receive error before session is initialized && slave doesn't know access_key - // => unreachable - self.state = ConsensusSessionState::Failed; - (false, Err(Error::ConsensusUnreachable)) - } - ConsensusSessionState::WaitingForInitialization if is_node_master => { - // error from master node before establishing consensus - // => unreachable - self.state = ConsensusSessionState::Failed; - (false, Err(if !error.is_non_fatal() { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - })) - }, - ConsensusSessionState::EstablishingConsensus => { - debug_assert!(is_self_master); - - // consensus still can be established - // => try to live without this node - (false, self.consensus_job.on_node_error(node, error)) - }, - ConsensusSessionState::ConsensusEstablished => { - // we could try to continue without this node, if enough nodes left - (false, self.consensus_job.on_node_error(node, error)) - }, - ConsensusSessionState::WaitingForPartialResults => { - // check if *current* computation job can continue without this node - let is_computation_node = self.computation_job.as_mut() - .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .on_node_error(node, error.clone()) - .is_err(); - if !is_computation_node { - // it is not used by current computation job - // => no restart required - (false, Ok(())) - } else { - // it is used by current computation job - // => restart is required if there are still enough nodes - self.consensus_group.clear(); - self.state = ConsensusSessionState::EstablishingConsensus; - - let consensus_result = self.consensus_job.on_node_error(node, error); - let is_consensus_established = self.consensus_job.state() == JobSessionState::Finished; - (is_consensus_established, consensus_result) - } - }, - // in all other cases - just ignore error - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Failed | ConsensusSessionState::Finished => (false, Ok(())), - }; - self.process_result(timeout_result)?; - Ok(is_restart_needed) - } - - /// When session is timeouted. - pub fn on_session_timeout(&mut self) -> Result { - match self.state { - // if we are waiting for results from slaves, there is a chance to send request to other nodes subset => fall through - ConsensusSessionState::WaitingForPartialResults => (), - // in some states this error is fatal - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => { - let _ = self.consensus_job.on_session_timeout(); - - self.consensus_group.clear(); - self.state = ConsensusSessionState::EstablishingConsensus; - return self.process_result(Err(Error::ConsensusTemporaryUnreachable)).map(|_| unreachable!()); - }, - // in all other cases - just ignore error - ConsensusSessionState::Finished | ConsensusSessionState::Failed => return Ok(false), - }; - - let timeouted_nodes = self.computation_job.as_ref() - .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .requests() - .clone(); - assert!(!timeouted_nodes.is_empty()); // timeout should not ever happen if no requests are active && we are waiting for responses - - self.consensus_group.clear(); - for timeouted_node in timeouted_nodes { - let timeout_result = self.consensus_job.on_node_error(&timeouted_node, Error::NodeDisconnected); - self.state = ConsensusSessionState::EstablishingConsensus; - self.process_result(timeout_result)?; - } - - Ok(self.state == ConsensusSessionState::ConsensusEstablished) - } - - /// Process result of job. - fn process_result(&mut self, result: Result<(), Error>) -> Result<(), Error> { - match self.state { - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => match self.consensus_job.state() { - JobSessionState::Finished => self.state = ConsensusSessionState::ConsensusEstablished, - JobSessionState::Failed => self.state = ConsensusSessionState::Failed, - _ => (), - }, - ConsensusSessionState::WaitingForPartialResults => match self.computation_job.as_ref() - .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .state() { - JobSessionState::Finished => self.state = ConsensusSessionState::Finished, - JobSessionState::Failed => self.state = ConsensusSessionState::Failed, - _ => (), - }, - _ => (), - } - - result - } -} - -impl ConsensusSession - where ConsensusExecutor: JobExecutor>, - ConsensusTransport: JobTransport, - ComputationExecutor: JobExecutor, - ComputationTransport: JobTransport { - /// Process basic consensus message. - pub fn on_consensus_message(&mut self, sender: &NodeId, message: &ConsensusMessage) -> Result<(), Error> { - let consensus_result = match message { - - &ConsensusMessage::InitializeConsensusSession(ref message) => - self.consensus_job.on_partial_request(sender, message.requester.clone().into()).map(|_| ()), - &ConsensusMessage::ConfirmConsensusInitialization(ref message) => - self.consensus_job.on_partial_response(sender, message.is_confirmed), - }; - self.process_result(consensus_result) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use crypto::publickey::{KeyPair, Random, Generator, sign, public_to_address}; - use key_server_cluster::{Error, NodeId, SessionId, Requester, DummyAclStorage}; - use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; - use key_server_cluster::jobs::job_session::tests::{make_master_session_meta, make_slave_session_meta, SquaredSumJobExecutor, DummyJobTransport}; - use key_server_cluster::jobs::key_access_job::KeyAccessJob; - use super::{ConsensusSession, ConsensusSessionParams, ConsensusSessionState}; - - type SquaredSumConsensusSession = ConsensusSession, SquaredSumJobExecutor, DummyJobTransport>; - - fn make_master_consensus_session(threshold: usize, requester: Option, acl_storage: Option) -> SquaredSumConsensusSession { - let secret = requester.map(|kp| kp.secret().clone()).unwrap_or(Random.generate().secret().clone()); - SquaredSumConsensusSession::new(ConsensusSessionParams { - meta: make_master_session_meta(threshold), - consensus_executor: KeyAccessJob::new_on_master(SessionId::from([1u8; 32]), Arc::new(acl_storage.unwrap_or(DummyAclStorage::default())), - sign(&secret, &SessionId::from([1u8; 32])).unwrap().into()), - consensus_transport: DummyJobTransport::default(), - }).unwrap() - } - - fn make_slave_consensus_session(threshold: usize, acl_storage: Option) -> SquaredSumConsensusSession { - SquaredSumConsensusSession::new(ConsensusSessionParams { - meta: make_slave_session_meta(threshold), - consensus_executor: KeyAccessJob::new_on_slave(SessionId::from([1u8; 32]), Arc::new(acl_storage.unwrap_or(DummyAclStorage::default()))), - consensus_transport: DummyJobTransport::default(), - }).unwrap() - } - - #[test] - fn consensus_session_consensus_is_not_reached_when_initializes_with_non_zero_threshold() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_consensus_is_reached_when_initializes_with_zero_threshold() { - let mut session = make_master_consensus_session(0, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_consensus_is_not_reached_when_initializes_with_zero_threshold_and_master_rejects() { - let requester = Random.generate(); - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(public_to_address(requester.public()), SessionId::from([1u8; 32])); - - let mut session = make_master_consensus_session(0, Some(requester), Some(acl_storage)); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_consensus_is_failed_by_master_node() { - let requester = Random.generate(); - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(public_to_address(requester.public()), SessionId::from([1u8; 32])); - - let mut session = make_master_consensus_session(1, Some(requester), Some(acl_storage)); - assert_eq!(session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_consensus_is_failed_by_slave_node() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - assert_eq!(session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: false, - })).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_job_dissemination_fails_if_consensus_is_not_reached() { - let mut session = make_master_consensus_session(1, None, None); - assert_eq!(session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consensus_session_job_dissemination_selects_master_node_if_agreed() { - let mut session = make_master_consensus_session(0, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - assert!(session.computation_job().responses().contains_key(&NodeId::from_low_u64_be(1))); - } - - #[test] - fn consensus_session_job_dissemination_does_not_select_master_node_if_rejected() { - let requester = Random.generate(); - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(public_to_address(requester.public()), SessionId::from([1u8; 32])); - - let mut session = make_master_consensus_session(0, Some(requester), Some(acl_storage)); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - assert!(!session.computation_job().responses().contains_key(&NodeId::from_low_u64_be(1))); - } - - #[test] - fn consensus_session_computation_request_is_rejected_when_received_by_master_node() { - let mut session = make_master_consensus_session(0, None, None); - assert_eq!(session.on_job_request(&NodeId::from_low_u64_be(2), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn consensus_session_computation_request_is_rejected_when_received_before_consensus_is_established() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_job_request(&NodeId::from_low_u64_be(1), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consensus_session_computation_request_is_ignored_when_wrong() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.state(), ConsensusSessionState::WaitingForInitialization); - session.on_consensus_message(&NodeId::from_low_u64_be(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: Requester::Signature(sign(Random.generate().secret(), &SessionId::from([1u8; 32])).unwrap()).into(), - version: Default::default(), - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - assert_eq!(session.on_job_request(&NodeId::from_low_u64_be(1), 20, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidMessage); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_computation_request_is_processed_when_correct() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.state(), ConsensusSessionState::WaitingForInitialization); - session.on_consensus_message(&NodeId::from_low_u64_be(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: Requester::Signature(sign(Random.generate().secret(), &SessionId::from([1u8; 32])).unwrap()).into(), - version: Default::default(), - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.on_job_request(&NodeId::from_low_u64_be(1), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_computation_response_is_ignored_when_consensus_is_not_reached() { - let mut session = make_master_consensus_session(1, None, None); - assert_eq!(session.on_job_response(&NodeId::from_low_u64_be(2), 4).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consessus_session_completion_is_ignored_when_received_from_non_master_node() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_session_completed(&NodeId::from_low_u64_be(3)).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn consessus_session_completion_is_ignored_when_consensus_is_not_established() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_session_completed(&NodeId::from_low_u64_be(1)).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consessus_session_completion_is_accepted() { - let mut session = make_slave_consensus_session(0, None); - session.on_consensus_message(&NodeId::from_low_u64_be(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: Requester::Signature(sign(Random.generate().secret(), &SessionId::from([1u8; 32])).unwrap()).into(), - version: Default::default(), - })).unwrap(); - session.on_session_completed(&NodeId::from_low_u64_be(1)).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_uninitialized_master() { - let mut session = make_master_consensus_session(0, None, None); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_uninitialized_slave_from_master() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(1), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_sessions_fails_with_temp_error_if_node_error_received_by_uninitialized_slave_from_master() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(1), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn consensus_session_continues_if_node_error_received_by_master_during_establish_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Ok(false)); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_master_during_establish_and_not_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_continues_if_node2_error_received_by_master_after_consensus_established_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_continues_if_node3_error_received_by_master_after_consensus_established_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_master_after_consensus_established_and_not_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_continues_if_node_error_received_from_slave_not_participating_in_computation() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied), Ok(false)); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(4), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - } - - #[test] - fn consensus_session_restarts_if_node_error_received_from_slave_participating_in_computation_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Ok(true)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn consensus_session_fails_if_node_error_received_from_slave_participating_in_computation_and_not_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_fails_if_uninitialized_session_timeouts() { - let mut session = make_master_consensus_session(1, None, None); - assert_eq!(session.on_session_timeout(), Err(Error::ConsensusTemporaryUnreachable)); - } - - #[test] - fn consensus_session_continues_if_session_timeouts_and_enough_nodes_left_for_computation() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_session_timeout(), Ok(true)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_session_timeout(), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn consensus_session_continues_if_session_timeouts_and_not_enough_nodes_left_for_computation() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_session_timeout(), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn same_consensus_group_returned_after_second_selection() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - - let consensus_group1 = session.select_consensus_group().unwrap().clone(); - let consensus_group2 = session.select_consensus_group().unwrap().clone(); - assert_eq!(consensus_group1, consensus_group2); - } - - #[test] - fn consensus_session_complete_2_of_4() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - session.on_job_response(&NodeId::from_low_u64_be(2), 16).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - assert_eq!(session.result(), Ok(20)); - } - - #[test] - fn consensus_session_complete_2_of_4_after_restart() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(), true); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(), false); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - - session.on_consensus_message(&NodeId::from_low_u64_be(4), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_job_response(&NodeId::from_low_u64_be(4), 16).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - assert_eq!(session.result(), Ok(20)); - } -} diff --git a/secret-store/src/key_server_cluster/jobs/decryption_job.rs b/secret-store/src/key_server_cluster/jobs/decryption_job.rs deleted file mode 100644 index 94b89dee8..000000000 --- a/secret-store/src/key_server_cluster/jobs/decryption_job.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::H256; -use crypto::publickey::{Public, Secret}; -use crypto::DEFAULT_MAC; -use crypto::publickey::ecies::encrypt; -use key_server_cluster::{Error, NodeId, DocumentKeyShare, EncryptedDocumentKeyShadow}; -use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Decryption job. -pub struct DecryptionJob { - /// This node id. - self_node_id: NodeId, - /// Access key. - access_key: Secret, - /// Requester public key. - requester: Public, - /// Key share. - key_share: DocumentKeyShare, - /// Key version. - key_version: H256, - /// Request id. - request_id: Option, - /// Is shadow decryption requested. - is_shadow_decryption: Option, - /// Is broadcast decryption requested. - is_broadcast_session: Option, -} - -/// Decryption job partial request. -#[derive(Debug)] -pub struct PartialDecryptionRequest { - /// Request id. - pub id: Secret, - /// Is shadow decryption requested. - pub is_shadow_decryption: bool, - /// Is broadcast decryption requested. - pub is_broadcast_session: bool, - /// Id of other nodes, participating in decryption. - pub other_nodes_ids: BTreeSet, -} - -/// Decryption job partial response. -#[derive(Clone)] -pub struct PartialDecryptionResponse { - /// Request id. - pub request_id: Secret, - /// Shadow point. - pub shadow_point: Public, - /// Decryption shadow coefficient, if requested. - pub decrypt_shadow: Option>, -} - -impl DecryptionJob { - pub fn new_on_slave(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, key_version: H256) -> Result { - debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); - Ok(DecryptionJob { - self_node_id: self_node_id, - access_key: access_key, - requester: requester, - key_share: key_share, - key_version: key_version, - request_id: None, - is_shadow_decryption: None, - is_broadcast_session: None, - }) - } - - pub fn new_on_master(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, key_version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result { - debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); - Ok(DecryptionJob { - self_node_id: self_node_id, - access_key: access_key, - requester: requester, - key_share: key_share, - key_version: key_version, - request_id: Some(math::generate_random_scalar()?), - is_shadow_decryption: Some(is_shadow_decryption), - is_broadcast_session: Some(is_broadcast_session), - }) - } - - pub fn request_id(&self) -> &Option { - &self.request_id - } - - pub fn set_request_id(&mut self, request_id: Secret) { - self.request_id = Some(request_id); - } -} - -impl JobExecutor for DecryptionJob { - type PartialJobRequest = PartialDecryptionRequest; - type PartialJobResponse = PartialDecryptionResponse; - type JobResponse = EncryptedDocumentKeyShadow; - - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result { - debug_assert!(nodes.len() == self.key_share.threshold + 1); - - let request_id = self.request_id.as_ref() - .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); - let is_shadow_decryption = self.is_shadow_decryption - .expect("prepare_partial_request is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); - let is_broadcast_session = self.is_broadcast_session - .expect("prepare_partial_request is only called on master nodes; is_broadcast_session is filed in constructor on master nodes; qed"); - let mut other_nodes_ids = nodes.clone(); - other_nodes_ids.remove(node); - - Ok(PartialDecryptionRequest { - id: request_id.clone(), - is_shadow_decryption: is_shadow_decryption, - is_broadcast_session: is_broadcast_session, - other_nodes_ids: other_nodes_ids, - }) - } - - fn process_partial_request(&mut self, partial_request: PartialDecryptionRequest) -> Result, Error> { - let key_version = self.key_share.version(&self.key_version)?; - if partial_request.other_nodes_ids.len() != self.key_share.threshold - || partial_request.other_nodes_ids.contains(&self.self_node_id) - || partial_request.other_nodes_ids.iter().any(|n| !key_version.id_numbers.contains_key(n)) { - return Err(Error::InvalidMessage); - } - - let self_id_number = &key_version.id_numbers[&self.self_node_id]; - let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &key_version.id_numbers[n]); - let node_shadow = math::compute_node_shadow(&key_version.secret_share, &self_id_number, other_id_numbers)?; - let decrypt_shadow = if partial_request.is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; - let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); - let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(&self.access_key, &common_point, &node_shadow, decrypt_shadow)?; - - Ok(JobPartialRequestAction::Respond(PartialDecryptionResponse { - request_id: partial_request.id, - shadow_point: shadow_point, - decrypt_shadow: match decrypt_shadow.clone() { - None => None, - Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, decrypt_shadow.as_bytes())?), - }, - })) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &PartialDecryptionResponse) -> Result { - if Some(&partial_response.request_id) != self.request_id.as_ref() { - return Ok(JobPartialResponseAction::Ignore); - } - if self.is_shadow_decryption != Some(partial_response.decrypt_shadow.is_some()) { - return Ok(JobPartialResponseAction::Reject); - } - Ok(JobPartialResponseAction::Accept) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result { - let is_shadow_decryption = self.is_shadow_decryption - .expect("compute_response is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); - let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); - let encrypted_point = self.key_share.encrypted_point.as_ref().expect("DecryptionJob is only created when encrypted_point is known; qed"); - let joint_shadow_point = math::compute_joint_shadow_point(partial_responses.values().map(|s| &s.shadow_point))?; - let decrypted_secret = math::decrypt_with_joint_shadow(self.key_share.threshold, &self.access_key, encrypted_point, &joint_shadow_point)?; - Ok(EncryptedDocumentKeyShadow { - decrypted_secret: decrypted_secret, - common_point: if is_shadow_decryption { - Some(math::make_common_shadow_point(self.key_share.threshold, common_point.clone())?) - } else { None }, - decrypt_shadows: if is_shadow_decryption { - Some(partial_responses.values().map(|r| r.decrypt_shadow.as_ref() - .expect("is_shadow_decryption == true; decrypt_shadow.is_some() is checked in check_partial_response; qed") - .clone()) - .collect()) - } else { None }, - }) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/dummy_job.rs b/secret-store/src/key_server_cluster/jobs/dummy_job.rs deleted file mode 100644 index 834d0de7d..000000000 --- a/secret-store/src/key_server_cluster/jobs/dummy_job.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeMap, BTreeSet}; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::jobs::job_session::{JobExecutor, JobTransport, JobPartialRequestAction, JobPartialResponseAction}; - -/// No-work job to use in generics (TODO [Refac]: create separate ShareChangeConsensusSession && remove this) -pub struct DummyJob; - -impl JobExecutor for DummyJob { - type PartialJobRequest = (); - type PartialJobResponse = (); - type JobResponse = (); - - fn prepare_partial_request(&self, _n: &NodeId, _nodes: &BTreeSet) -> Result<(), Error> { - unreachable!("dummy job methods are never called") - } - - fn process_partial_request(&mut self, _r: ()) -> Result, Error> { - unreachable!("dummy job methods are never called") - } - - fn check_partial_response(&mut self, _s: &NodeId, _r: &()) -> Result { - unreachable!("dummy job methods are never called") - } - - fn compute_response(&self, _r: &BTreeMap) -> Result<(), Error> { - unreachable!("dummy job methods are never called") - } -} - -/// No-work job transport to use in generics (TODO [Refac]: create separate ShareChangeConsensusSession && remove this) -pub struct DummyJobTransport; - -impl JobTransport for DummyJobTransport { - type PartialJobRequest = (); - type PartialJobResponse = (); - - fn send_partial_request(&self, _node: &NodeId, _request: ()) -> Result<(), Error> { - unreachable!("dummy transport methods are never called") - } - - fn send_partial_response(&self, _node: &NodeId, _response: ()) -> Result<(), Error> { - unreachable!("dummy transport methods are never called") - } -} diff --git a/secret-store/src/key_server_cluster/jobs/job_session.rs b/secret-store/src/key_server_cluster/jobs/job_session.rs deleted file mode 100644 index e042b038e..000000000 --- a/secret-store/src/key_server_cluster/jobs/job_session.rs +++ /dev/null @@ -1,661 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionMeta}; - -/// Partial response action. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum JobPartialResponseAction { - /// Ignore this response. - Ignore, - /// Mark this response as reject. - Reject, - /// Accept this response. - Accept, -} - -/// Partial request action. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum JobPartialRequestAction { - /// Respond with reject. - Reject(PartialJobResponse), - /// Respond with this response. - Respond(PartialJobResponse), -} - -/// Job executor. -pub trait JobExecutor { - type PartialJobRequest; - type PartialJobResponse: Clone; - type JobResponse; - - /// Prepare job request for given node. - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result; - /// Process partial request. - fn process_partial_request(&mut self, partial_request: Self::PartialJobRequest) -> Result, Error>; - /// Check partial response of given node. - fn check_partial_response(&mut self, sender: &NodeId, partial_response: &Self::PartialJobResponse) -> Result; - /// Compute final job response. - fn compute_response(&self, partial_responses: &BTreeMap) -> Result; -} - -/// Jobs transport. -pub trait JobTransport { - type PartialJobRequest; - type PartialJobResponse; - - /// Send partial request to given node. - fn send_partial_request(&self, node: &NodeId, request: Self::PartialJobRequest) -> Result<(), Error>; - /// Send partial request to given node. - fn send_partial_response(&self, node: &NodeId, response: Self::PartialJobResponse) -> Result<(), Error>; -} - -/// Current state of job session. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum JobSessionState { - /// Session is inactive. - Inactive, - /// Session is active. - Active, - /// Session is finished. - Finished, - /// Session has failed. - Failed, -} - -/// Basic request-response session on a set of nodes. -pub struct JobSession where Transport: JobTransport { - /// Session meta. - meta: SessionMeta, - /// Job executor. - executor: Executor, - /// Jobs transport. - transport: Transport, - /// Session data. - data: JobSessionData, -} - -/// Data of job session. -struct JobSessionData { - /// Session state. - state: JobSessionState, - /// Mutable session data. - active_data: Option>, -} - -/// Active job session data. -struct ActiveJobSessionData { - /// Active partial requests. - requests: BTreeSet, - /// Rejects to partial requests (maps to true, if reject is fatal). - rejects: BTreeMap, - /// Received partial responses. - responses: BTreeMap, -} - -impl JobSession where Executor: JobExecutor, Transport: JobTransport { - /// Create new session. - pub fn new(meta: SessionMeta, executor: Executor, transport: Transport) -> Self { - JobSession { - meta: meta, - executor: executor, - transport: transport, - data: JobSessionData { - state: JobSessionState::Inactive, - active_data: None, - }, - } - } - - /// Get transport reference. - #[cfg(test)] - pub fn transport(&self) -> &Transport { - &self.transport - } - - /// Get mutable transport reference. - pub fn transport_mut(&mut self) -> &mut Transport { - &mut self.transport - } - - /// Get executor reference. - pub fn executor(&self) -> &Executor { - &self.executor - } - - /// Get mutable executor reference. - pub fn executor_mut(&mut self) -> &mut Executor { - &mut self.executor - } - - /// Get job state. - pub fn state(&self) -> JobSessionState { - self.data.state - } - - /// Get rejects. - #[cfg(test)] - pub fn rejects(&self) -> &BTreeMap { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - &self.data.active_data.as_ref() - .expect("rejects is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .rejects - } - - /// Get active requests. - pub fn requests(&self) -> &BTreeSet { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - &self.data.active_data.as_ref() - .expect("requests is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .requests - } - - /// Get responses. - pub fn responses(&self) -> &BTreeMap { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - &self.data.active_data.as_ref() - .expect("responses is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .responses - } - - /// Returns true if enough responses are ready to compute result. - pub fn is_result_ready(&self) -> bool { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - self.data.active_data.as_ref() - .expect("is_result_ready is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .responses.len() >= self.meta.threshold + 1 - } - - /// Get job result. - pub fn result(&self) -> Result { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - if self.data.state != JobSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - self.executor.compute_response(&self.data.active_data.as_ref() - .expect("requests is only called on master nodes; on master nodes active_data is filled during initialization; qed") - .responses) - } - - /// Initialize. - pub fn initialize(&mut self, nodes: BTreeSet, self_response: Option, broadcast_self_response: bool) -> Result, Error> { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - if nodes.len() < self.meta.threshold + 1 { - return Err(if self.meta.configured_nodes_count < self.meta.threshold + 1 { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }); - } - - if self.data.state != JobSessionState::Inactive { - return Err(Error::InvalidStateForRequest); - } - - // result from self - let active_data = ActiveJobSessionData { - requests: nodes.clone(), - rejects: BTreeMap::new(), - responses: BTreeMap::new(), - }; - let waits_for_self = active_data.requests.contains(&self.meta.self_node_id); - let self_response = match self_response { - Some(self_response) => Some(self_response), - None if waits_for_self => { - let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id, &active_data.requests)?; - let self_response = self.executor.process_partial_request(partial_request)?; - Some(self_response.take_response()) - }, - None => None, - }; - - // update state - self.data.active_data = Some(active_data); - self.data.state = JobSessionState::Active; - - // if we are waiting for response from self => do it - if let Some(self_response) = self_response.clone() { - let self_node_id = self.meta.self_node_id.clone(); - self.on_partial_response(&self_node_id, self_response)?; - } - - // send requests to save nodes. we only send requests if session is still active. - for node in nodes.iter().filter(|n| **n != self.meta.self_node_id) { - if self.data.state == JobSessionState::Active { - self.transport.send_partial_request(node, self.executor.prepare_partial_request(node, &nodes)?)?; - } - if broadcast_self_response { - if let Some(self_response) = self_response.clone() { - self.transport.send_partial_response(node, self_response)?; - } - } - } - - Ok(self_response) - } - - /// When partial request is received by slave node. - pub fn on_partial_request(&mut self, node: &NodeId, request: Executor::PartialJobRequest) -> Result, Error> { - if node != &self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.meta.self_node_id == self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.data.state != JobSessionState::Inactive && self.data.state != JobSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - let partial_request_action = self.executor.process_partial_request(request)?; - let partial_response = match partial_request_action { - JobPartialRequestAction::Respond(ref partial_response) => { - self.data.state = JobSessionState::Finished; - partial_response.clone() - }, - JobPartialRequestAction::Reject(ref partial_response) => { - self.data.state = JobSessionState::Failed; - partial_response.clone() - }, - }; - self.transport.send_partial_response(node, partial_response)?; - Ok(partial_request_action) - } - - /// When partial request is received by master node. - pub fn on_partial_response(&mut self, node: &NodeId, response: Executor::PartialJobResponse) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.data.state != JobSessionState::Active && self.data.state != JobSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - let active_data = self.data.active_data.as_mut() - .expect("on_partial_response is only called on master nodes; on master nodes active_data is filled during initialization; qed"); - if !active_data.requests.remove(node) { - return Err(Error::InvalidNodeForRequest); - } - - match self.executor.check_partial_response(node, &response)? { - JobPartialResponseAction::Ignore => Ok(()), - JobPartialResponseAction::Reject => { - // direct reject is always considered as fatal - active_data.rejects.insert(node.clone(), true); - if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - Err(consensus_unreachable(&active_data.rejects)) - }, - JobPartialResponseAction::Accept => { - active_data.responses.insert(node.clone(), response); - if active_data.responses.len() < self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Finished; - Ok(()) - }, - } - } - - /// When error from node is received. - pub fn on_node_error(&mut self, node: &NodeId, error: Error) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - if node != &self.meta.master_node_id { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - return Err(if !error.is_non_fatal() { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }); - } - - if let Some(active_data) = self.data.active_data.as_mut() { - if active_data.rejects.contains_key(node) { - return Ok(()); - } - if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { - active_data.rejects.insert(node.clone(), !error.is_non_fatal()); - if self.data.state == JobSessionState::Finished && active_data.responses.len() < self.meta.threshold + 1 { - self.data.state = JobSessionState::Active; - } - if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - return Err(consensus_unreachable(&active_data.rejects)); - } - } - - Ok(()) - } - - /// When session timeouted. - pub fn on_session_timeout(&mut self) -> Result<(), Error> { - if self.data.state == JobSessionState::Finished || self.data.state == JobSessionState::Failed { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - // we have started session => consensus is possible in theory, but now it has failed with timeout - Err(Error::ConsensusTemporaryUnreachable) - } -} - -impl JobPartialRequestAction { - /// Take actual response. - pub fn take_response(self) -> PartialJobResponse { - match self { - JobPartialRequestAction::Respond(response) => response, - JobPartialRequestAction::Reject(response) => response, - } - } -} - -/// Returns appropriate 'consensus unreachable' error. -fn consensus_unreachable(rejects: &BTreeMap) -> Error { - // when >= 50% of nodes have responded with fatal reject => ConsensusUnreachable - if rejects.values().filter(|r| **r).count() >= rejects.len() / 2 { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use parking_lot::Mutex; - use crypto::publickey::Public; - use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; - use super::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor, JobTransport, JobSession, JobSessionState}; - - pub struct SquaredSumJobExecutor; - - impl JobExecutor for SquaredSumJobExecutor { - type PartialJobRequest = u32; - type PartialJobResponse = u32; - type JobResponse = u32; - - fn prepare_partial_request(&self, _n: &NodeId, _nodes: &BTreeSet) -> Result { Ok(2) } - fn process_partial_request(&mut self, r: u32) -> Result, Error> { if r <= 10 { Ok(JobPartialRequestAction::Respond(r * r)) } else { Err(Error::InvalidMessage) } } - fn check_partial_response(&mut self, _s: &NodeId, r: &u32) -> Result { if r % 2 == 0 { Ok(JobPartialResponseAction::Accept) } else { Ok(JobPartialResponseAction::Reject) } } - fn compute_response(&self, r: &BTreeMap) -> Result { Ok(r.values().fold(0, |v1, v2| v1 + v2)) } - } - - #[derive(Default)] - pub struct DummyJobTransport { - pub requests: Mutex>, - pub responses: Mutex>, - } - - impl DummyJobTransport { - pub fn is_empty_response(&self) -> bool { - self.responses.lock().is_empty() - } - - pub fn response(&self) -> (NodeId, U) { - self.responses.lock().pop_front().unwrap() - } - } - - impl JobTransport for DummyJobTransport { - type PartialJobRequest = T; - type PartialJobResponse = U; - - fn send_partial_request(&self, node: &NodeId, request: T) -> Result<(), Error> { self.requests.lock().push_back((node.clone(), request)); Ok(()) } - fn send_partial_response(&self, node: &NodeId, response: U) -> Result<(), Error> { self.responses.lock().push_back((node.clone(), response)); Ok(()) } - } - - pub fn make_master_session_meta(threshold: usize) -> SessionMeta { - SessionMeta { id: SessionId::from([1u8; 32]), master_node_id: NodeId::from_low_u64_be(1), self_node_id: NodeId::from_low_u64_be(1), threshold: threshold, - configured_nodes_count: 5, connected_nodes_count: 5 } - } - - pub fn make_slave_session_meta(threshold: usize) -> SessionMeta { - SessionMeta { id: SessionId::from([1u8; 32]), master_node_id: NodeId::from_low_u64_be(1), self_node_id: NodeId::from_low_u64_be(2), threshold: threshold, - configured_nodes_count: 5, connected_nodes_count: 5 } - } - - #[test] - fn job_initialize_fails_if_not_enough_nodes_for_threshold_total() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.meta.configured_nodes_count = 1; - assert_eq!(job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn job_initialize_fails_if_not_enough_nodes_for_threshold_connected() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.meta.connected_nodes_count = 3; - assert_eq!(job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn job_initialize_fails_if_not_inactive() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn job_initialization_leads_to_finish_if_single_node_is_required() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Finished); - assert!(job.is_result_ready()); - assert_eq!(job.result(), Ok(4)); - } - - #[test] - fn job_initialization_does_not_leads_to_finish_if_single_other_node_is_required() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_request_fails_if_comes_from_non_master_node() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(3), 2).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn job_request_fails_if_comes_to_master_node() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn job_request_fails_if_comes_to_failed_state() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.on_session_timeout().unwrap_err(); - assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn job_request_succeeds_if_comes_to_finished_state() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap(); - assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(1), 4)); - assert_eq!(job.state(), JobSessionState::Finished); - job.on_partial_request(&NodeId::from_low_u64_be(1), 3).unwrap(); - assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(1), 9)); - assert_eq!(job.state(), JobSessionState::Finished); - } - - #[test] - fn job_response_fails_if_comes_to_slave_node() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn job_response_fails_if_comes_to_failed_state() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - job.on_session_timeout().unwrap_err(); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn job_response_fails_if_comes_from_unknown_node() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(3), 2).unwrap_err(), Error::InvalidNodeForRequest); - } - - #[test] - fn job_response_leads_to_failure_if_too_few_nodes_left() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(2), 3).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(job.state(), JobSessionState::Failed); - } - - #[test] - fn job_response_succeeds() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert!(!job.is_result_ready()); - job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert!(!job.is_result_ready()); - } - - #[test] - fn job_response_leads_to_finish() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap(); - assert_eq!(job.state(), JobSessionState::Finished); - } - - #[test] - fn job_node_error_ignored_when_slave_disconnects_from_slave() { - let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.state(), JobSessionState::Inactive); - job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Inactive); - } - - #[test] - fn job_node_error_leads_to_fail_when_slave_disconnects_from_master() { - let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.state(), JobSessionState::Inactive); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(1), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(job.state(), JobSessionState::Failed); - } - - #[test] - fn job_node_error_ignored_when_disconnects_from_rejected() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_partial_response(&NodeId::from_low_u64_be(2), 3).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_node_error_ignored_when_disconnects_from_unknown() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_node_error_ignored_when_disconnects_from_requested_and_enough_nodes_left() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_node_error_leads_to_fail_when_disconnects_from_requested_and_not_enough_nodes_left() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(job.state(), JobSessionState::Failed); - } - - #[test] - fn job_broadcasts_self_response() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, true).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(2), 4)); - } - - #[test] - fn job_does_not_broadcasts_self_response() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert!(job.transport().is_empty_response()); - } - - #[test] - fn job_fails_with_temp_error_if_more_than_half_nodes_respond_with_temp_error() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::NodeDisconnected).unwrap(); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(3), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn job_fails_with_temp_error_if_more_than_half_rejects_are_temp() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::NodeDisconnected).unwrap(); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(3), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn job_fails_if_more_than_half_rejects_are_non_temp() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn job_fails_with_temp_error_when_temp_error_is_reported_by_master_node() { - let mut job = JobSession::new(make_slave_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(1), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } -} diff --git a/secret-store/src/key_server_cluster/jobs/key_access_job.rs b/secret-store/src/key_server_cluster/jobs/key_access_job.rs deleted file mode 100644 index 4a30dd79f..000000000 --- a/secret-store/src/key_server_cluster/jobs/key_access_job.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage}; -use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor}; - -/// Purpose of this job is to construct set of nodes, which have agreed to provide access to the given key for the given requestor. -pub struct KeyAccessJob { - /// Key id. - id: SessionId, - /// Has key share? - has_key_share: bool, - /// ACL storage. - acl_storage: Arc, - /// Requester data. - requester: Option, -} - -impl KeyAccessJob { - pub fn new_on_slave(id: SessionId, acl_storage: Arc) -> Self { - KeyAccessJob { - id: id, - has_key_share: true, - acl_storage: acl_storage, - requester: None, - } - } - - pub fn new_on_master(id: SessionId, acl_storage: Arc, requester: Requester) -> Self { - KeyAccessJob { - id: id, - has_key_share: true, - acl_storage: acl_storage, - requester: Some(requester), - } - } - - pub fn set_has_key_share(&mut self, has_key_share: bool) { - self.has_key_share = has_key_share; - } - - pub fn set_requester(&mut self, requester: Requester) { - self.requester = Some(requester); - } - - pub fn requester(&self) -> Option<&Requester> { - self.requester.as_ref() - } -} - -impl JobExecutor for KeyAccessJob { - type PartialJobRequest = Requester; - type PartialJobResponse = bool; - type JobResponse = BTreeSet; - - fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { - Ok(self.requester.as_ref().expect("prepare_partial_request is only called on master nodes; new_on_master fills the signature; qed").clone()) - } - - fn process_partial_request(&mut self, partial_request: Requester) -> Result, Error> { - if !self.has_key_share { - return Ok(JobPartialRequestAction::Reject(false)); - } - - self.requester = Some(partial_request.clone()); - self.acl_storage.check(partial_request.address(&self.id).map_err(Error::InsufficientRequesterData)?, &self.id) - .map(|is_confirmed| if is_confirmed { JobPartialRequestAction::Respond(true) } else { JobPartialRequestAction::Reject(false) }) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &bool) -> Result { - Ok(if *partial_response { JobPartialResponseAction::Accept } else { JobPartialResponseAction::Reject }) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result, Error> { - Ok(partial_responses.keys().cloned().collect()) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/mod.rs b/secret-store/src/key_server_cluster/jobs/mod.rs deleted file mode 100644 index 4e3b8c0d9..000000000 --- a/secret-store/src/key_server_cluster/jobs/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod consensus_session; -pub mod decryption_job; -pub mod dummy_job; -pub mod job_session; -pub mod key_access_job; -pub mod servers_set_change_access_job; -pub mod signing_job_ecdsa; -pub mod signing_job_schnorr; -pub mod unknown_sessions_job; diff --git a/secret-store/src/key_server_cluster/jobs/servers_set_change_access_job.rs b/secret-store/src/key_server_cluster/jobs/servers_set_change_access_job.rs deleted file mode 100644 index 52e8c95df..000000000 --- a/secret-store/src/key_server_cluster/jobs/servers_set_change_access_job.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::{Public, Signature, recover}; -use tiny_keccak::Keccak; -use key_server_cluster::{Error, NodeId, SessionId}; -use key_server_cluster::message::{InitializeConsensusSessionWithServersSet, InitializeConsensusSessionOfShareAdd}; -use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor}; - -/// Purpose of this job is to check if requestor is administrator of SecretStore (i.e. it have access to change key servers set). -pub struct ServersSetChangeAccessJob { - /// Servers set administrator public key (this could be changed to ACL-based check later). - administrator: Public, - /// Old servers set. - old_servers_set: Option>, - /// New servers set. - new_servers_set: Option>, - /// Old servers set, signed by requester. - old_set_signature: Option, - /// New servers set, signed by requester. - new_set_signature: Option, -} - -/// Servers set change job partial request. -pub struct ServersSetChangeAccessRequest { - /// Old servers set. - pub old_servers_set: BTreeSet, - /// New servers set. - pub new_servers_set: BTreeSet, - /// Hash(old_servers_set), signed by requester. - pub old_set_signature: Signature, - /// Hash(new_servers_set), signed by requester. - pub new_set_signature: Signature, -} - -impl<'a> From<&'a InitializeConsensusSessionWithServersSet> for ServersSetChangeAccessRequest { - fn from(message: &InitializeConsensusSessionWithServersSet) -> Self { - ServersSetChangeAccessRequest { - old_servers_set: message.old_nodes_set.iter().cloned().map(Into::into).collect(), - new_servers_set: message.new_nodes_set.iter().cloned().map(Into::into).collect(), - old_set_signature: message.old_set_signature.clone().into(), - new_set_signature: message.new_set_signature.clone().into(), - } - } -} - -impl<'a> From<&'a InitializeConsensusSessionOfShareAdd> for ServersSetChangeAccessRequest { - fn from(message: &InitializeConsensusSessionOfShareAdd) -> Self { - ServersSetChangeAccessRequest { - old_servers_set: message.old_nodes_set.iter().cloned().map(Into::into).collect(), - new_servers_set: message.new_nodes_map.keys().cloned().map(Into::into).collect(), - old_set_signature: message.old_set_signature.clone().into(), - new_set_signature: message.new_set_signature.clone().into(), - } - } -} - -impl ServersSetChangeAccessJob { - pub fn new_on_slave(administrator: Public) -> Self { - ServersSetChangeAccessJob { - administrator: administrator, - old_servers_set: None, - new_servers_set: None, - old_set_signature: None, - new_set_signature: None, - } - } - - pub fn new_on_master(administrator: Public, old_servers_set: BTreeSet, new_servers_set: BTreeSet, old_set_signature: Signature, new_set_signature: Signature) -> Self { - ServersSetChangeAccessJob { - administrator: administrator, - old_servers_set: Some(old_servers_set), - new_servers_set: Some(new_servers_set), - old_set_signature: Some(old_set_signature), - new_set_signature: Some(new_set_signature), - } - } - - pub fn new_servers_set(&self) -> Option<&BTreeSet> { - self.new_servers_set.as_ref() - } -} - -impl JobExecutor for ServersSetChangeAccessJob { - type PartialJobRequest = ServersSetChangeAccessRequest; - type PartialJobResponse = bool; - type JobResponse = BTreeSet; - - fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { - let explanation = "prepare_partial_request is only called on master nodes; this field is filled on master nodes in constructor; qed"; - Ok(ServersSetChangeAccessRequest { - old_servers_set: self.old_servers_set.clone().expect(explanation), - new_servers_set: self.new_servers_set.clone().expect(explanation), - old_set_signature: self.old_set_signature.clone().expect(explanation), - new_set_signature: self.new_set_signature.clone().expect(explanation), - }) - } - - fn process_partial_request(&mut self, partial_request: ServersSetChangeAccessRequest) -> Result, Error> { - let ServersSetChangeAccessRequest { - old_servers_set, - new_servers_set, - old_set_signature, - new_set_signature, - } = partial_request; - - // check old servers set signature - let old_actual_public = recover(&old_set_signature, &ordered_nodes_hash(&old_servers_set).into())?; - let new_actual_public = recover(&new_set_signature, &ordered_nodes_hash(&new_servers_set).into())?; - let is_administrator = old_actual_public == self.administrator && new_actual_public == self.administrator; - self.new_servers_set = Some(new_servers_set); - - Ok(if is_administrator { JobPartialRequestAction::Respond(true) } else { JobPartialRequestAction::Reject(false) }) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &bool) -> Result { - Ok(if *partial_response { JobPartialResponseAction::Accept } else { JobPartialResponseAction::Reject }) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result, Error> { - Ok(partial_responses.keys().cloned().collect()) - } -} - -pub fn ordered_nodes_hash(nodes: &BTreeSet) -> SessionId { - let mut nodes_keccak = Keccak::new_keccak256(); - for node in nodes { - nodes_keccak.update(node.as_bytes()); - } - - let mut nodes_keccak_value = [0u8; 32]; - nodes_keccak.finalize(&mut nodes_keccak_value); - - nodes_keccak_value.into() -} diff --git a/secret-store/src/key_server_cluster/jobs/signing_job_ecdsa.rs b/secret-store/src/key_server_cluster/jobs/signing_job_ecdsa.rs deleted file mode 100644 index ecb509ad1..000000000 --- a/secret-store/src/key_server_cluster/jobs/signing_job_ecdsa.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::{Public, Secret, Signature}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, DocumentKeyShare}; -use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Signing job. -pub struct EcdsaSigningJob { - /// Key share. - key_share: DocumentKeyShare, - /// Key version. - key_version: H256, - /// Share of inv(nonce). - inv_nonce_share: Secret, - /// Nonce public. - nonce_public: Public, - /// Request id. - request_id: Option, - /// ECDSA reversed-nonce coefficient - inversed_nonce_coeff: Option, - /// Message hash. - message_hash: Option, -} - -/// Signing job partial request. -pub struct EcdsaPartialSigningRequest { - /// Request id. - pub id: Secret, - /// ECDSA reversed-nonce coefficient - pub inversed_nonce_coeff: Secret, - /// Message hash to sign. - pub message_hash: H256, -} - -/// Signing job partial response. -#[derive(Clone)] -pub struct EcdsaPartialSigningResponse { - /// Request id. - pub request_id: Secret, - /// Partial signature' s share. - pub partial_signature_s: Secret, -} - -impl EcdsaSigningJob { - pub fn new_on_slave(key_share: DocumentKeyShare, key_version: H256, nonce_public: Public, inv_nonce_share: Secret) -> Result { - Ok(EcdsaSigningJob { - key_share: key_share, - key_version: key_version, - nonce_public: nonce_public, - inv_nonce_share: inv_nonce_share, - request_id: None, - inversed_nonce_coeff: None, - message_hash: None, - }) - } - - pub fn new_on_master(key_share: DocumentKeyShare, key_version: H256, nonce_public: Public, inv_nonce_share: Secret, inversed_nonce_coeff: Secret, message_hash: H256) -> Result { - Ok(EcdsaSigningJob { - key_share: key_share, - key_version: key_version, - nonce_public: nonce_public, - inv_nonce_share: inv_nonce_share, - request_id: Some(math::generate_random_scalar()?), - inversed_nonce_coeff: Some(inversed_nonce_coeff), - message_hash: Some(message_hash), - }) - } -} - -impl JobExecutor for EcdsaSigningJob { - type PartialJobRequest = EcdsaPartialSigningRequest; - type PartialJobResponse = EcdsaPartialSigningResponse; - type JobResponse = Signature; - - fn prepare_partial_request(&self, _node: &NodeId, nodes: &BTreeSet) -> Result { - debug_assert!(nodes.len() == self.key_share.threshold * 2 + 1); - - let request_id = self.request_id.as_ref() - .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); - let inversed_nonce_coeff = self.inversed_nonce_coeff.as_ref() - .expect("prepare_partial_request is only called on master nodes; inversed_nonce_coeff is filed in constructor on master nodes; qed"); - let message_hash = self.message_hash.as_ref() - .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); - - Ok(EcdsaPartialSigningRequest { - id: request_id.clone(), - inversed_nonce_coeff: inversed_nonce_coeff.clone(), - message_hash: message_hash.clone(), - }) - } - - fn process_partial_request(&mut self, partial_request: EcdsaPartialSigningRequest) -> Result, Error> { - let inversed_nonce_coeff_mul_nonce = math::compute_secret_mul(&partial_request.inversed_nonce_coeff, &self.inv_nonce_share)?; - let key_version = self.key_share.version(&self.key_version)?; - let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; - let inv_nonce_mul_secret = math::compute_secret_mul(&inversed_nonce_coeff_mul_nonce, &key_version.secret_share)?; - let partial_signature_s = math::compute_ecdsa_s_share( - &inversed_nonce_coeff_mul_nonce, - &inv_nonce_mul_secret, - &signature_r, - &math::to_scalar(partial_request.message_hash)?, - )?; - - Ok(JobPartialRequestAction::Respond(EcdsaPartialSigningResponse { - request_id: partial_request.id, - partial_signature_s: partial_signature_s, - })) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &EcdsaPartialSigningResponse) -> Result { - if Some(&partial_response.request_id) != self.request_id.as_ref() { - return Ok(JobPartialResponseAction::Ignore); - } - // TODO [Trust]: check_ecdsa_signature_share() - - Ok(JobPartialResponseAction::Accept) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result { - let key_version = self.key_share.version(&self.key_version)?; - if partial_responses.keys().any(|n| !key_version.id_numbers.contains_key(n)) { - return Err(Error::InvalidMessage); - } - - let id_numbers: Vec<_> = partial_responses.keys().map(|n| key_version.id_numbers[n].clone()).collect(); - let signature_s_shares: Vec<_> = partial_responses.values().map(|r| r.partial_signature_s.clone()).collect(); - let signature_s = math::compute_ecdsa_s(self.key_share.threshold, &signature_s_shares, &id_numbers)?; - let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; - - let signature = math::serialize_ecdsa_signature(&self.nonce_public, signature_r, signature_s); - - Ok(signature) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/signing_job_schnorr.rs b/secret-store/src/key_server_cluster/jobs/signing_job_schnorr.rs deleted file mode 100644 index 678d9f32a..000000000 --- a/secret-store/src/key_server_cluster/jobs/signing_job_schnorr.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::{Public, Secret}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, DocumentKeyShare}; -use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Signing job. -pub struct SchnorrSigningJob { - /// This node id. - self_node_id: NodeId, - /// Key share. - key_share: DocumentKeyShare, - /// Key version. - key_version: H256, - /// Session public key. - session_public: Public, - /// Session secret coefficient. - session_secret_coeff: Secret, - /// Request id. - request_id: Option, - /// Message hash. - message_hash: Option, -} - -/// Signing job partial request. -pub struct SchnorrPartialSigningRequest { - /// Request id. - pub id: Secret, - /// Message hash. - pub message_hash: H256, - /// Id of other nodes, participating in signing. - pub other_nodes_ids: BTreeSet, -} - -/// Signing job partial response. -#[derive(Clone)] -pub struct SchnorrPartialSigningResponse { - /// Request id. - pub request_id: Secret, - /// Partial signature. - pub partial_signature: Secret, -} - -impl SchnorrSigningJob { - pub fn new_on_slave(self_node_id: NodeId, key_share: DocumentKeyShare, key_version: H256, session_public: Public, session_secret_coeff: Secret) -> Result { - Ok(SchnorrSigningJob { - self_node_id: self_node_id, - key_share: key_share, - key_version: key_version, - session_public: session_public, - session_secret_coeff: session_secret_coeff, - request_id: None, - message_hash: None, - }) - } - - pub fn new_on_master(self_node_id: NodeId, key_share: DocumentKeyShare, key_version: H256, session_public: Public, session_secret_coeff: Secret, message_hash: H256) -> Result { - Ok(SchnorrSigningJob { - self_node_id: self_node_id, - key_share: key_share, - key_version: key_version, - session_public: session_public, - session_secret_coeff: session_secret_coeff, - request_id: Some(math::generate_random_scalar()?), - message_hash: Some(message_hash), - }) - } -} - -impl JobExecutor for SchnorrSigningJob { - type PartialJobRequest = SchnorrPartialSigningRequest; - type PartialJobResponse = SchnorrPartialSigningResponse; - type JobResponse = (Secret, Secret); - - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result { - debug_assert!(nodes.len() == self.key_share.threshold + 1); - - let request_id = self.request_id.as_ref() - .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); - let message_hash = self.message_hash.as_ref() - .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); - let mut other_nodes_ids = nodes.clone(); - other_nodes_ids.remove(node); - - Ok(SchnorrPartialSigningRequest { - id: request_id.clone(), - message_hash: message_hash.clone(), - other_nodes_ids: other_nodes_ids, - }) - } - - fn process_partial_request(&mut self, partial_request: SchnorrPartialSigningRequest) -> Result, Error> { - let key_version = self.key_share.version(&self.key_version)?; - if partial_request.other_nodes_ids.len() != self.key_share.threshold - || partial_request.other_nodes_ids.contains(&self.self_node_id) - || partial_request.other_nodes_ids.iter().any(|n| !key_version.id_numbers.contains_key(n)) { - return Err(Error::InvalidMessage); - } - - let self_id_number = &key_version.id_numbers[&self.self_node_id]; - let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &key_version.id_numbers[n]); - let combined_hash = math::combine_message_hash_with_public(&partial_request.message_hash, &self.session_public)?; - Ok(JobPartialRequestAction::Respond(SchnorrPartialSigningResponse { - request_id: partial_request.id, - partial_signature: math::compute_schnorr_signature_share( - self.key_share.threshold, - &combined_hash, - &self.session_secret_coeff, - &key_version.secret_share, - self_id_number, - other_id_numbers - )?, - })) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &SchnorrPartialSigningResponse) -> Result { - if Some(&partial_response.request_id) != self.request_id.as_ref() { - return Ok(JobPartialResponseAction::Ignore); - } - // TODO [Trust]: check_schnorr_signature_share() - - Ok(JobPartialResponseAction::Accept) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result<(Secret, Secret), Error> { - let message_hash = self.message_hash.as_ref() - .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); - - let signature_c = math::combine_message_hash_with_public(message_hash, &self.session_public)?; - let signature_s = math::compute_schnorr_signature(partial_responses.values().map(|r| &r.partial_signature))?; - - Ok((signature_c, signature_s)) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/unknown_sessions_job.rs b/secret-store/src/key_server_cluster/jobs/unknown_sessions_job.rs deleted file mode 100644 index b9a366ee2..000000000 --- a/secret-store/src/key_server_cluster/jobs/unknown_sessions_job.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage}; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Unknown sessions report job. -pub struct UnknownSessionsJob { - /// Target node id. - target_node_id: Option, - /// Keys storage. - key_storage: Arc, -} - -impl UnknownSessionsJob { - pub fn new_on_slave(key_storage: Arc) -> Self { - UnknownSessionsJob { - target_node_id: None, - key_storage: key_storage, - } - } - - pub fn new_on_master(key_storage: Arc, self_node_id: NodeId) -> Self { - UnknownSessionsJob { - target_node_id: Some(self_node_id), - key_storage: key_storage, - } - } -} - -impl JobExecutor for UnknownSessionsJob { - type PartialJobRequest = NodeId; - type PartialJobResponse = BTreeSet; - type JobResponse = BTreeMap>; - - fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { - Ok(self.target_node_id.clone().expect("prepare_partial_request is only called on master nodes; this field is filled on master nodes in constructor; qed")) - } - - fn process_partial_request(&mut self, partial_request: NodeId) -> Result>, Error> { - Ok(JobPartialRequestAction::Respond(self.key_storage.iter() - .filter(|&(_, ref key_share)| !key_share.versions.last().map(|v| v.id_numbers.contains_key(&partial_request)).unwrap_or(true)) - .map(|(id, _)| id.clone()) - .collect())) - } - - fn check_partial_response(&mut self, _sender: &NodeId, _partial_response: &BTreeSet) -> Result { - Ok(JobPartialResponseAction::Accept) - } - - // TODO [Opt]: - // currently ALL unknown sessions are sent at once - it is better to limit messages by size/len => add partial-partial responses - fn compute_response(&self, partial_responses: &BTreeMap>) -> Result>, Error> { - let mut result: BTreeMap> = BTreeMap::new(); - for (node_id, node_sessions) in partial_responses { - for node_session in node_sessions { - result.entry(node_session.clone()) - .or_insert_with(Default::default) - .insert(node_id.clone()); - } - } - - Ok(result) - } -} diff --git a/secret-store/src/key_server_cluster/math.rs b/secret-store/src/key_server_cluster/math.rs deleted file mode 100644 index 8412b2ea9..000000000 --- a/secret-store/src/key_server_cluster/math.rs +++ /dev/null @@ -1,1102 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use crypto::publickey::{Public, Secret, Signature, Random, Generator, ec_math_utils}; -use ethereum_types::{H256, U256, BigEndianHash}; -use hash::keccak; -use key_server_cluster::Error; - -/// Encryption result. -#[derive(Debug)] -pub struct EncryptedSecret { - /// Common encryption point. - pub common_point: Public, - /// Ecnrypted point. - pub encrypted_point: Public, -} - -/// Calculate the inversion of a Secret key (in place) using the `libsecp256k1` crate. -fn invert_secret(s: &mut Secret) -> Result<(), Error> { - *s = secp256k1::SecretKey::parse(&s.0)?.inv().serialize().into(); - Ok(()) -} - -/// Create zero scalar. -pub fn zero_scalar() -> Secret { - Secret::zero() -} - -/// Convert hash to EC scalar (modulo curve order). -pub fn to_scalar(hash: H256) -> Result { - let scalar: U256 = hash.into_uint(); - let scalar: H256 = BigEndianHash::from_uint(&(scalar % *ec_math_utils::CURVE_ORDER)); - let scalar = Secret::from(scalar.0); - scalar.check_validity()?; - Ok(scalar) -} - -/// Generate random scalar. -pub fn generate_random_scalar() -> Result { - Ok(Random.generate().secret().clone()) -} - -/// Generate random point. -pub fn generate_random_point() -> Result { - Ok(Random.generate().public().clone()) -} - -/// Get X coordinate of point. -fn public_x(public: &Public) -> H256 { - H256::from_slice(&public.as_bytes()[0..32]) -} - -/// Get Y coordinate of point. -fn public_y(public: &Public) -> H256 { - H256::from_slice(&public.as_bytes()[32..64]) -} - -/// Compute publics sum. -pub fn compute_public_sum<'a, I>(mut publics: I) -> Result where I: Iterator { - let mut sum = publics.next().expect("compute_public_sum is called when there's at least one public; qed").clone(); - while let Some(public) = publics.next() { - ec_math_utils::public_add(&mut sum, &public)?; - } - Ok(sum) -} - -/// Compute secrets sum. -pub fn compute_secret_sum<'a, I>(mut secrets: I) -> Result where I: Iterator { - let mut sum = secrets.next().expect("compute_secret_sum is called when there's at least one secret; qed").clone(); - while let Some(secret) = secrets.next() { - sum.add(secret)?; - } - Ok(sum) -} - -/// Compute secrets multiplication. -pub fn compute_secret_mul(secret1: &Secret, secret2: &Secret) -> Result { - let mut secret_mul = secret1.clone(); - secret_mul.mul(secret2)?; - Ok(secret_mul) -} - -/// Compute secrets 'shadow' multiplication: coeff * multiplication(s[j] / (s[i] - s[j])) for every i != j -pub fn compute_shadow_mul<'a, I>(coeff: &Secret, self_secret: &Secret, mut other_secrets: I) -> Result where I: Iterator { - // when there are no other secrets, only coeff is left - let other_secret = match other_secrets.next() { - Some(other_secret) => other_secret, - None => return Ok(coeff.clone()), - }; - - let mut shadow_mul = self_secret.clone(); - shadow_mul.sub(other_secret)?; - invert_secret(&mut shadow_mul)?; - - shadow_mul.mul(other_secret)?; - while let Some(other_secret) = other_secrets.next() { - let mut shadow_mul_element = self_secret.clone(); - shadow_mul_element.sub(other_secret)?; - invert_secret(&mut shadow_mul_element)?; - shadow_mul_element.mul(other_secret)?; - shadow_mul.mul(&shadow_mul_element)?; - } - - shadow_mul.mul(coeff)?; - Ok(shadow_mul) -} - -/// Update point by multiplying to random scalar -pub fn update_random_point(point: &mut Public) -> Result<(), Error> { - Ok(ec_math_utils::public_mul_secret(point, &generate_random_scalar()?)?) -} - -/// Generate random polynom of threshold degree -pub fn generate_random_polynom(threshold: usize) -> Result, Error> { - (0..threshold + 1) - .map(|_| generate_random_scalar()) - .collect() -} - -/// Compute value of polynom, using `node_number` as argument -pub fn compute_polynom(polynom: &[Secret], node_number: &Secret) -> Result { - debug_assert!(!polynom.is_empty()); - - let mut result = polynom[0].clone(); - for i in 1..polynom.len() { - // calculate pow(node_number, i) - let mut appendum = node_number.clone(); - appendum.pow(i)?; - - // calculate coeff * pow(point, i) - appendum.mul(&polynom[i])?; - - // calculate result + coeff * pow(point, i) - result.add(&appendum)?; - } - - Ok(result) -} - -/// Generate public keys for other participants. -pub fn public_values_generation(threshold: usize, derived_point: &Public, polynom1: &[Secret], polynom2: &[Secret]) -> Result, Error> { - debug_assert_eq!(polynom1.len(), threshold + 1); - debug_assert_eq!(polynom2.len(), threshold + 1); - - // compute t+1 public values - let mut publics = Vec::with_capacity(threshold + 1); - for i in 0..threshold + 1 { - let coeff1 = &polynom1[i]; - - let mut multiplication1 = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut multiplication1, &coeff1)?; - - let coeff2 = &polynom2[i]; - let mut multiplication2 = derived_point.clone(); - ec_math_utils::public_mul_secret(&mut multiplication2, &coeff2)?; - - ec_math_utils::public_add(&mut multiplication1, &multiplication2)?; - - publics.push(multiplication1); - } - debug_assert_eq!(publics.len(), threshold + 1); - - Ok(publics) -} - -/// Check keys passed by other participants. -pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &Secret, secret1: &Secret, secret2: &Secret, publics: &[Public]) -> Result { - // calculate left part - let mut multiplication1 = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut multiplication1, secret1)?; - - let mut multiplication2 = derived_point.clone(); - ec_math_utils::public_mul_secret(&mut multiplication2, secret2)?; - - ec_math_utils::public_add(&mut multiplication1, &multiplication2)?; - let left = multiplication1; - - // calculate right part - let mut right = publics[0].clone(); - for i in 1..threshold + 1 { - let mut secret_pow = number_id.clone(); - secret_pow.pow(i)?; - - let mut public_k = publics[i].clone(); - ec_math_utils::public_mul_secret(&mut public_k, &secret_pow)?; - - ec_math_utils::public_add(&mut right, &public_k)?; - } - - Ok(left == right) -} - -/// Compute secret subshare from passed secret value. -pub fn compute_secret_subshare<'a, I>(threshold: usize, secret_value: &Secret, sender_id_number: &Secret, other_id_numbers: I) -> Result where I: Iterator { - let mut subshare = compute_shadow_mul(secret_value, sender_id_number, other_id_numbers)?; - if threshold % 2 != 0 { - subshare.neg()?; - } - - Ok(subshare) -} - -/// Compute secret share. -pub fn compute_secret_share<'a, I>(secret_values: I) -> Result where I: Iterator { - compute_secret_sum(secret_values) -} - -/// Compute public key share. -pub fn compute_public_share(self_secret_value: &Secret) -> Result { - let mut public_share = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut public_share, self_secret_value)?; - Ok(public_share) -} - -/// Compute joint public key. -pub fn compute_joint_public<'a, I>(public_shares: I) -> Result where I: Iterator { - compute_public_sum(public_shares) -} - -/// Compute joint secret key from N secret coefficients. -#[cfg(test)] -pub fn compute_joint_secret<'a, I>(secret_coeffs: I) -> Result where I: Iterator { - compute_secret_sum(secret_coeffs) -} - -/// Compute joint secret key from t+1 secret shares. -pub fn compute_joint_secret_from_shares<'a>(t: usize, secret_shares: &[&'a Secret], id_numbers: &[&'a Secret]) -> Result { - let secret_share_0 = secret_shares[0]; - let id_number_0 = id_numbers[0]; - let other_nodes_numbers = id_numbers.iter().skip(1).cloned(); - let mut result = compute_node_shadow(secret_share_0, id_number_0, other_nodes_numbers)?; - for i in 1..secret_shares.len() { - let secret_share_i = secret_shares[i]; - let id_number_i = id_numbers[i]; - let other_nodes_numbers = id_numbers.iter().enumerate().filter(|&(j, _)| j != i).map(|(_, n)| n).cloned(); - let addendum = compute_node_shadow(secret_share_i, id_number_i, other_nodes_numbers)?; - result.add(&addendum)?; - } - - if t % 2 != 0 { - result.neg()?; - } - - Ok(result) -} - -/// Encrypt secret with joint public key. -pub fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result { - // this is performed by KS-cluster client (or KS master) - let key_pair = Random.generate(); - - // k * T - let mut common_point = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut common_point, key_pair.secret())?; - - // M + k * y - let mut encrypted_point = joint_public.clone(); - ec_math_utils::public_mul_secret(&mut encrypted_point, key_pair.secret())?; - ec_math_utils::public_add(&mut encrypted_point, secret)?; - - Ok(EncryptedSecret { - common_point: common_point, - encrypted_point: encrypted_point, - }) -} - -/// Compute shadow for the node. -pub fn compute_node_shadow<'a, I>(node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) -> Result where I: Iterator { - compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers) -} - -/// Compute shadow point for the node. -pub fn compute_node_shadow_point(access_key: &Secret, common_point: &Public, node_shadow: &Secret, decrypt_shadow: Option) -> Result<(Public, Option), Error> { - let mut shadow_key = node_shadow.clone(); - let decrypt_shadow = match decrypt_shadow { - None => None, - Some(mut decrypt_shadow) => { - // update shadow key - shadow_key.mul(&decrypt_shadow)?; - // now udate decrypt shadow itself - decrypt_shadow.dec()?; - decrypt_shadow.mul(node_shadow)?; - Some(decrypt_shadow) - } - }; - shadow_key.mul(access_key)?; - - let mut node_shadow_point = common_point.clone(); - ec_math_utils::public_mul_secret(&mut node_shadow_point, &shadow_key)?; - Ok((node_shadow_point, decrypt_shadow)) -} - -/// Compute joint shadow point. -pub fn compute_joint_shadow_point<'a, I>(nodes_shadow_points: I) -> Result where I: Iterator { - compute_public_sum(nodes_shadow_points) -} - -/// Compute joint shadow point (version for tests). -#[cfg(test)] -pub fn compute_joint_shadow_point_test<'a, I>(access_key: &Secret, common_point: &Public, nodes_shadows: I) -> Result where I: Iterator { - let mut joint_shadow = compute_secret_sum(nodes_shadows)?; - joint_shadow.mul(access_key)?; - - let mut joint_shadow_point = common_point.clone(); - ec_math_utils::public_mul_secret(&mut joint_shadow_point, &joint_shadow)?; - Ok(joint_shadow_point) -} - -/// Decrypt data using joint shadow point. -pub fn decrypt_with_joint_shadow(threshold: usize, access_key: &Secret, encrypted_point: &Public, joint_shadow_point: &Public) -> Result { - let mut inv_access_key = access_key.clone(); - invert_secret(&mut inv_access_key)?; - let mut mul = joint_shadow_point.clone(); - ec_math_utils::public_mul_secret(&mut mul, &inv_access_key)?; - - let mut decrypted_point = encrypted_point.clone(); - if threshold % 2 != 0 { - ec_math_utils::public_add(&mut decrypted_point, &mul)?; - } else { - ec_math_utils::public_sub(&mut decrypted_point, &mul)?; - } - - Ok(decrypted_point) -} - -/// Prepare common point for shadow decryption. -pub fn make_common_shadow_point(threshold: usize, mut common_point: Public) -> Result { - if threshold % 2 != 1 { - Ok(common_point) - } else { - ec_math_utils::public_negate(&mut common_point)?; - Ok(common_point) - } -} - -/// Decrypt shadow-encrypted secret. -#[cfg(test)] -pub fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_shadow_point: Public, shadow_coefficients: Vec) -> Result { - let shadow_coefficients_sum = compute_secret_sum(shadow_coefficients.iter())?; - ec_math_utils::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum)?; - ec_math_utils::public_add(&mut decrypted_shadow, &common_shadow_point)?; - Ok(decrypted_shadow) -} - -/// Decrypt data using joint secret (version for tests). -#[cfg(test)] -pub fn decrypt_with_joint_secret(encrypted_point: &Public, common_point: &Public, joint_secret: &Secret) -> Result { - let mut common_point_mul = common_point.clone(); - ec_math_utils::public_mul_secret(&mut common_point_mul, joint_secret)?; - - let mut decrypted_point = encrypted_point.clone(); - ec_math_utils::public_sub(&mut decrypted_point, &common_point_mul)?; - - Ok(decrypted_point) -} - -/// Combine message hash with public key X coordinate. -pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> Result { - // buffer is just [message_hash | public.x] - let mut buffer = [0; 64]; - buffer[0..32].copy_from_slice(&message_hash[0..32]); - buffer[32..64].copy_from_slice(&public[0..32]); - - // calculate hash of buffer - let hash = keccak(&buffer[..]); - - // map hash to EC finite field value - to_scalar(hash) -} - -/// Compute Schnorr signature share. -pub fn compute_schnorr_signature_share<'a, I>(threshold: usize, combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) - -> Result where I: Iterator { - let mut sum = one_time_secret_coeff.clone(); - let mut subtrahend = compute_shadow_mul(combined_hash, node_number, other_nodes_numbers)?; - subtrahend.mul(node_secret_share)?; - if threshold % 2 == 0 { - sum.sub(&subtrahend)?; - } else { - sum.add(&subtrahend)?; - } - Ok(sum) -} - -/// Check Schnorr signature share. -pub fn _check_schnorr_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_numbers: I) - -> Result where I: Iterator { - // TODO [Trust]: in paper partial signature is checked using comparison: - // sig[i] * T = r[i] - c * lagrange_coeff(i) * y[i] - // => (k[i] - c * lagrange_coeff(i) * s[i]) * T = r[i] - c * lagrange_coeff(i) * y[i] - // => k[i] * T - c * lagrange_coeff(i) * s[i] * T = k[i] * T - c * lagrange_coeff(i) * y[i] - // => this means that y[i] = s[i] * T - // but when verifying signature (for t = 1), nonce public (r) is restored using following expression: - // r = (sig[0] + sig[1]) * T - c * y - // r = (k[0] - c * lagrange_coeff(0) * s[0] + k[1] - c * lagrange_coeff(1) * s[1]) * T - c * y - // r = (k[0] + k[1]) * T - c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - c * y - // r = r - c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - c * y - // => -c * y = c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - // => -y = (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - // => y[i] != s[i] * T - // => some other way is required - Ok(true) -} - -/// Compute Schnorr signature. -pub fn compute_schnorr_signature<'a, I>(signature_shares: I) -> Result where I: Iterator { - compute_secret_sum(signature_shares) -} - -/// Locally compute Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Signing. -#[cfg(test)] -pub fn local_compute_schnorr_signature(nonce: &Secret, secret: &Secret, message_hash: &Secret) -> Result<(Secret, Secret), Error> { - let mut nonce_public = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut nonce_public, &nonce).unwrap(); - - let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; - - let mut sig_subtrahend = combined_hash.clone(); - sig_subtrahend.mul(secret)?; - let mut sig = nonce.clone(); - sig.sub(&sig_subtrahend)?; - - Ok((combined_hash, sig)) -} - -/// Verify Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. -#[cfg(test)] -pub fn verify_schnorr_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { - let mut addendum = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut addendum, &signature.1)?; - let mut nonce_public = public.clone(); - ec_math_utils::public_mul_secret(&mut nonce_public, &signature.0)?; - ec_math_utils::public_add(&mut nonce_public, &addendum)?; - - let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; - Ok(combined_hash == signature.0) -} - -/// Compute R part of ECDSA signature. -pub fn compute_ecdsa_r(nonce_public: &Public) -> Result { - to_scalar(public_x(nonce_public)) -} - -/// Compute share of S part of ECDSA signature. -pub fn compute_ecdsa_s_share(inv_nonce_share: &Secret, inv_nonce_mul_secret: &Secret, signature_r: &Secret, message_hash: &Secret) -> Result { - let mut nonce_inv_share_mul_message_hash = inv_nonce_share.clone(); - nonce_inv_share_mul_message_hash.mul(&message_hash.clone().into())?; - - let mut nonce_inv_share_mul_secret_share_mul_r = inv_nonce_mul_secret.clone(); - nonce_inv_share_mul_secret_share_mul_r.mul(signature_r)?; - - let mut signature_s_share = nonce_inv_share_mul_message_hash; - signature_s_share.add(&nonce_inv_share_mul_secret_share_mul_r)?; - - Ok(signature_s_share) -} - -/// Compute S part of ECDSA signature from shares. -pub fn compute_ecdsa_s(t: usize, signature_s_shares: &[Secret], id_numbers: &[Secret]) -> Result { - let double_t = t * 2; - debug_assert!(id_numbers.len() >= double_t + 1); - debug_assert_eq!(signature_s_shares.len(), id_numbers.len()); - - compute_joint_secret_from_shares(double_t, - &signature_s_shares.iter().take(double_t + 1).collect::>(), - &id_numbers.iter().take(double_t + 1).collect::>()) -} - -/// Serialize ECDSA signature to [r][s]v form. -pub fn serialize_ecdsa_signature(nonce_public: &Public, signature_r: Secret, mut signature_s: Secret) -> Signature { - // compute recovery param - let mut signature_v = { - let nonce_public_x = public_x(nonce_public); - let nonce_public_y: U256 = public_y(nonce_public).into_uint(); - let nonce_public_y_is_odd = !(nonce_public_y % 2).is_zero(); - let bit0 = if nonce_public_y_is_odd { 1u8 } else { 0u8 }; - let bit1 = if nonce_public_x != *signature_r { 2u8 } else { 0u8 }; - bit0 | bit1 - }; - - // fix high S - let curve_order_half = *ec_math_utils::CURVE_ORDER / 2; - let s_numeric: U256 = (*signature_s).into_uint(); - if s_numeric > curve_order_half { - let signature_s_hash: H256 = BigEndianHash::from_uint(&(*ec_math_utils::CURVE_ORDER - s_numeric)); - signature_s = signature_s_hash.into(); - signature_v ^= 1; - } - - // serialize as [r][s]v - let mut signature = [0u8; 65]; - signature[..32].copy_from_slice(signature_r.as_bytes()); - signature[32..64].copy_from_slice(signature_s.as_bytes()); - signature[64] = signature_v; - - signature.into() -} - -/// Compute share of ECDSA reversed-nonce coefficient. Result of this_coeff * secret_share gives us a share of inv(nonce). -pub fn compute_ecdsa_inversed_secret_coeff_share(secret_share: &Secret, nonce_share: &Secret, zero_share: &Secret) -> Result { - let mut coeff = secret_share.clone(); - coeff.mul(nonce_share).unwrap(); - coeff.add(zero_share).unwrap(); - Ok(coeff) -} - -/// Compute ECDSA reversed-nonce coefficient from its shares. Result of this_coeff * secret_share gives us a share of inv(nonce). -pub fn compute_ecdsa_inversed_secret_coeff_from_shares(t: usize, id_numbers: &[Secret], shares: &[Secret]) -> Result { - debug_assert_eq!(shares.len(), 2 * t + 1); - debug_assert_eq!(shares.len(), id_numbers.len()); - - let u_shares = (0..2*t+1).map(|i| compute_shadow_mul(&shares[i], &id_numbers[i], id_numbers.iter().enumerate() - .filter(|&(j, _)| i != j) - .map(|(_, id)| id) - .take(2 * t))).collect::, _>>()?; - - // compute u - let u = compute_secret_sum(u_shares.iter())?; - - // compute inv(u) - let mut u_inv = u; - invert_secret(&mut u_inv)?; - Ok(u_inv) -} - -#[cfg(test)] -pub mod tests { - use std::iter::once; - use crypto::publickey::{KeyPair, Secret, recover, verify_public}; - use super::*; - - #[derive(Clone)] - struct KeyGenerationArtifacts { - id_numbers: Vec, - polynoms1: Vec>, - secrets1: Vec>, - public_shares: Vec, - secret_shares: Vec, - joint_public: Public, - } - - struct ZeroGenerationArtifacts { - polynoms1: Vec>, - secret_shares: Vec, - } - - fn prepare_polynoms1(t: usize, n: usize, secret_required: Option) -> Vec> { - let mut polynoms1: Vec<_> = (0..n).map(|_| generate_random_polynom(t).unwrap()).collect(); - // if we need specific secret to be shared, update polynoms so that sum of their free terms = required secret - if let Some(mut secret_required) = secret_required { - for polynom1 in polynoms1.iter_mut().take(n - 1) { - let secret_coeff1 = generate_random_scalar().unwrap(); - secret_required.sub(&secret_coeff1).unwrap(); - polynom1[0] = secret_coeff1; - } - - polynoms1[n - 1][0] = secret_required; - } - polynoms1 - } - - fn run_key_generation(t: usize, n: usize, id_numbers: Option>, secret_required: Option) -> KeyGenerationArtifacts { - // === PART1: DKG === - - // data, gathered during initialization - let derived_point = Random.generate().public().clone(); - let id_numbers: Vec<_> = match id_numbers { - Some(id_numbers) => id_numbers, - None => (0..n).map(|_| generate_random_scalar().unwrap()).collect(), - }; - - // data, generated during keys dissemination - let polynoms1 = prepare_polynoms1(t, n, secret_required); - let secrets1: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms1[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - - // following data is used only on verification step - let polynoms2: Vec<_> = (0..n).map(|_| generate_random_polynom(t).unwrap()).collect(); - let secrets2: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms2[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - let publics: Vec<_> = (0..n).map(|i| public_values_generation(t, &derived_point, &polynoms1[i], &polynoms2[i]).unwrap()).collect(); - - // keys verification - (0..n).for_each(|i| { - (0..n) - .filter(|&j| i != j) - .for_each(|j| { - assert!(keys_verification(t, &derived_point, &id_numbers[i], &secrets1[j][i], &secrets2[j][i], &publics[j]).unwrap()); - }) - }); - - // data, generated during keys generation - let public_shares: Vec<_> = (0..n).map(|i| compute_public_share(&polynoms1[i][0]).unwrap()).collect(); - let secret_shares: Vec<_> = (0..n).map(|i| compute_secret_share(secrets1.iter().map(|s| &s[i])).unwrap()).collect(); - - // joint public key, as a result of DKG - let joint_public = compute_joint_public(public_shares.iter()).unwrap(); - - KeyGenerationArtifacts { - id_numbers: id_numbers, - polynoms1: polynoms1, - secrets1: secrets1, - public_shares: public_shares, - secret_shares: secret_shares, - joint_public: joint_public, - } - } - - fn run_zero_key_generation(t: usize, n: usize, id_numbers: &[Secret]) -> ZeroGenerationArtifacts { - // data, generated during keys dissemination - let polynoms1 = prepare_polynoms1(t, n, Some(zero_scalar())); - let secrets1: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms1[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - - // data, generated during keys generation - let secret_shares: Vec<_> = (0..n).map(|i| compute_secret_share(secrets1.iter().map(|s| &s[i])).unwrap()).collect(); - - ZeroGenerationArtifacts { - polynoms1: polynoms1, - secret_shares: secret_shares, - } - } - - fn run_key_share_refreshing(old_t: usize, new_t: usize, new_n: usize, old_artifacts: &KeyGenerationArtifacts) -> KeyGenerationArtifacts { - // === share refreshing protocol from - // === based on "Verifiable Secret Redistribution for Threshold Sharing Schemes" - // === http://www.cs.cmu.edu/~wing/publications/CMU-CS-02-114.pdf - - // generate new id_numbers for new nodes - let new_nodes = new_n.saturating_sub(old_artifacts.id_numbers.len()); - let id_numbers: Vec<_> = old_artifacts.id_numbers.iter().take(new_n).cloned() - .chain((0..new_nodes).map(|_| generate_random_scalar().unwrap())) - .collect(); - - // on every authorized node: generate random polynomial ai(j) = si + ... + ai[new_t - 1] * j^(new_t - 1) - let mut subshare_polynoms = Vec::new(); - for i in 0..old_t+1 { - let mut subshare_polynom = generate_random_polynom(new_t).unwrap(); - subshare_polynom[0] = old_artifacts.secret_shares[i].clone(); - subshare_polynoms.push(subshare_polynom); - } - - // on every authorized node: calculate subshare for every new node - let mut subshares = Vec::new(); - for j in 0..new_n { - let mut subshares_to_j = Vec::new(); - for i in 0..old_t+1 { - let subshare_from_i_to_j = compute_polynom(&subshare_polynoms[i], &id_numbers[j]).unwrap(); - subshares_to_j.push(subshare_from_i_to_j); - } - subshares.push(subshares_to_j); - } - - // on every new node: generate new share using Lagrange interpolation - // on every node: generate new share using Lagrange interpolation - let mut new_secret_shares = Vec::new(); - for j in 0..new_n { - let mut subshares_to_j = Vec::new(); - for i in 0..old_t+1 { - let subshare_from_i = &subshares[j][i]; - let id_number_i = &id_numbers[i]; - let other_id_numbers = (0usize..old_t+1).filter(|j| *j != i).map(|j| &id_numbers[j]); - let mut subshare_from_i = compute_shadow_mul(subshare_from_i, id_number_i, other_id_numbers).unwrap(); - if old_t % 2 != 0 { - subshare_from_i.neg().unwrap(); - } - subshares_to_j.push(subshare_from_i); - } - new_secret_shares.push(compute_secret_sum(subshares_to_j.iter()).unwrap()); - } - - let mut result = old_artifacts.clone(); - result.id_numbers = id_numbers; - result.secret_shares = new_secret_shares; - result - } - - fn run_multiplication_protocol(t: usize, secret_shares1: &[Secret], secret_shares2: &[Secret]) -> Vec { - let n = secret_shares1.len(); - assert!(t * 2 + 1 <= n); - - // shares of secrets multiplication = multiplication of secrets shares - let mul_shares: Vec<_> = (0..n).map(|i| { - let share1 = secret_shares1[i].clone(); - let share2 = secret_shares2[i].clone(); - let mut mul_share = share1; - mul_share.mul(&share2).unwrap(); - mul_share - }).collect(); - - mul_shares - } - - fn run_reciprocal_protocol(t: usize, artifacts: &KeyGenerationArtifacts) -> Vec { - // === Given a secret x mod r which is shared among n players, it is - // === required to generate shares of inv(x) mod r with out revealing - // === any information about x or inv(x). - // === https://www.researchgate.net/publication/280531698_Robust_Threshold_Elliptic_Curve_Digital_Signature - - // generate shared random secret e for given t - let n = artifacts.id_numbers.len(); - assert!(t * 2 + 1 <= n); - let e_artifacts = run_key_generation(t, n, Some(artifacts.id_numbers.clone()), None); - - // generate shares of zero for 2 * t threshold - let z_artifacts = run_zero_key_generation(2 * t, n, &artifacts.id_numbers); - - // each player computes && broadcast u[i] = x[i] * e[i] + z[i] - let ui: Vec<_> = (0..n).map(|i| compute_ecdsa_inversed_secret_coeff_share(&artifacts.secret_shares[i], - &e_artifacts.secret_shares[i], - &z_artifacts.secret_shares[i]).unwrap()).collect(); - - // players can interpolate the polynomial of degree 2t and compute u && inv(u): - let u_inv = compute_ecdsa_inversed_secret_coeff_from_shares(t, - &artifacts.id_numbers.iter().take(2*t + 1).cloned().collect::>(), - &ui.iter().take(2*t + 1).cloned().collect::>()).unwrap(); - - // each player Pi computes his share of inv(x) as e[i] * inv(u) - let x_inv_shares: Vec<_> = (0..n).map(|i| { - let mut x_inv_share = e_artifacts.secret_shares[i].clone(); - x_inv_share.mul(&u_inv).unwrap(); - x_inv_share - }).collect(); - - x_inv_shares - } - - pub fn do_encryption_and_decryption(t: usize, joint_public: &Public, id_numbers: &[Secret], secret_shares: &[Secret], joint_secret: Option<&Secret>, document_secret_plain: Public) -> (Public, Public) { - // === PART2: encryption using joint public key === - - // the next line is executed on KeyServer-client - let encrypted_secret = encrypt_secret(&document_secret_plain, &joint_public).unwrap(); - - // === PART3: decryption === - - // next line is executed on KeyServer client - let access_key = generate_random_scalar().unwrap(); - - // use t + 1 nodes to compute joint shadow point - let nodes_shadows: Vec<_> = (0..t + 1).map(|i| - compute_node_shadow(&secret_shares[i], &id_numbers[i], id_numbers.iter() - .enumerate() - .filter(|&(j, _)| j != i) - .take(t) - .map(|(_, id_number)| id_number)).unwrap()).collect(); - - let nodes_shadow_points: Vec<_> = nodes_shadows.iter() - .map(|s| compute_node_shadow_point(&access_key, &encrypted_secret.common_point, s, None).unwrap()) - .map(|sp| sp.0) - .collect(); - - assert_eq!(nodes_shadows.len(), t + 1); - assert_eq!(nodes_shadow_points.len(), t + 1); - - let joint_shadow_point = compute_joint_shadow_point(nodes_shadow_points.iter()).unwrap(); - let joint_shadow_point_test = compute_joint_shadow_point_test(&access_key, &encrypted_secret.common_point, nodes_shadows.iter()).unwrap(); - assert_eq!(joint_shadow_point, joint_shadow_point_test); - - // decrypt encrypted secret using joint shadow point - let document_secret_decrypted = decrypt_with_joint_shadow(t, &access_key, &encrypted_secret.encrypted_point, &joint_shadow_point).unwrap(); - - // decrypt encrypted secret using joint secret [just for test] - let document_secret_decrypted_test = match joint_secret { - Some(joint_secret) => decrypt_with_joint_secret(&encrypted_secret.encrypted_point, &encrypted_secret.common_point, joint_secret).unwrap(), - None => document_secret_decrypted.clone(), - }; - - (document_secret_decrypted, document_secret_decrypted_test) - } - - #[test] - fn full_encryption_math_session() { - let test_cases = [(0, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5), (4, 5), - (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10)]; - for &(t, n) in &test_cases { - let artifacts = run_key_generation(t, n, None, None); - - // compute joint private key [just for test] - let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let joint_key_pair = KeyPair::from_secret(joint_secret.clone()).unwrap(); - assert_eq!(&artifacts.joint_public, joint_key_pair.public()); - - // check secret shares computation [just for test] - let secret_shares_polynom: Vec<_> = (0..t + 1).map(|k| compute_secret_share(artifacts.polynoms1.iter().map(|p| &p[k])).unwrap()).collect(); - let secret_shares_calculated_from_polynom: Vec<_> = artifacts.id_numbers.iter().map(|id_number| compute_polynom(&*secret_shares_polynom, id_number).unwrap()).collect(); - assert_eq!(artifacts.secret_shares, secret_shares_calculated_from_polynom); - - // now encrypt and decrypt data - let document_secret_plain = generate_random_point().unwrap(); - let (document_secret_decrypted, document_secret_decrypted_test) = - do_encryption_and_decryption(t, &artifacts.joint_public, &artifacts.id_numbers, &artifacts.secret_shares, Some(&joint_secret), document_secret_plain.clone()); - - assert_eq!(document_secret_plain, document_secret_decrypted_test); - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - - #[test] - fn local_signature_works() { - let key_pair = Random.generate(); - let message_hash = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); - let nonce = generate_random_scalar().unwrap(); - let signature = local_compute_schnorr_signature(&nonce, key_pair.secret(), &message_hash).unwrap(); - assert_eq!(verify_schnorr_signature(key_pair.public(), &signature, &message_hash), Ok(true)); - } - - #[test] - fn full_schnorr_signature_math_session() { - let test_cases = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5), (4, 5), - (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10)]; - for &(t, n) in &test_cases { - // hash of the message to be signed - let message_hash: Secret = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); - - // === MiDS-S algorithm === - // setup: all nodes share master secret key && every node knows master public key - let artifacts = run_key_generation(t, n, None, None); - - // in this gap (not related to math): - // master node should ask every other node if it is able to do a signing - // if there are < than t+1 nodes, able to sign => error - // select t+1 nodes for signing session - // all steps below are for this subset of nodes - let n = t + 1; - - // step 1: run DKG to generate one-time secret key (nonce) - let id_numbers = artifacts.id_numbers.iter().cloned().take(n).collect(); - let one_time_artifacts = run_key_generation(t, n, Some(id_numbers), None); - - // step 2: message hash && x coordinate of one-time public value are combined - let combined_hash = combine_message_hash_with_public(&message_hash, &one_time_artifacts.joint_public).unwrap(); - - // step 3: compute signature shares - let partial_signatures: Vec<_> = (0..n) - .map(|i| compute_schnorr_signature_share( - t, - &combined_hash, - &one_time_artifacts.polynoms1[i][0], - &artifacts.secret_shares[i], - &artifacts.id_numbers[i], - artifacts.id_numbers.iter() - .enumerate() - .filter(|&(j, _)| i != j) - .map(|(_, n)| n) - .take(t) - ).unwrap()) - .collect(); - - // step 4: receive and verify signatures shares from other nodes - let received_signatures: Vec> = (0..n) - .map(|i| (0..n) - .filter(|j| i != *j) - .map(|j| { - let signature_share = partial_signatures[j].clone(); - assert!(_check_schnorr_signature_share(&combined_hash, - &signature_share, - &artifacts.public_shares[j], - &one_time_artifacts.public_shares[j], - artifacts.id_numbers.iter().take(t)).unwrap_or(false)); - signature_share - }) - .collect()) - .collect(); - - // step 5: compute signature - let signatures: Vec<_> = (0..n) - .map(|i| (combined_hash.clone(), compute_schnorr_signature(received_signatures[i].iter().chain(once(&partial_signatures[i]))).unwrap())) - .collect(); - - // === verify signature === - let master_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let nonce = compute_joint_secret(one_time_artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let local_signature = local_compute_schnorr_signature(&nonce, &master_secret, &message_hash).unwrap(); - for signature in &signatures { - assert_eq!(signature, &local_signature); - assert_eq!(verify_schnorr_signature(&artifacts.joint_public, signature, &message_hash), Ok(true)); - } - } - } - - #[test] - fn full_ecdsa_signature_math_session() { - let test_cases = [(2, 5), (2, 6), (3, 11), (4, 11)]; - for &(t, n) in &test_cases { - // values that can be hardcoded - let joint_secret: Secret = Random.generate().secret().clone(); - let joint_nonce: Secret = Random.generate().secret().clone(); - let message_hash: H256 = H256::random(); - - // convert message hash to EC scalar - let message_hash_scalar = to_scalar(message_hash.clone()).unwrap(); - - // generate secret key shares - let artifacts = run_key_generation(t, n, None, Some(joint_secret)); - - // generate nonce shares - let nonce_artifacts = run_key_generation(t, n, Some(artifacts.id_numbers.clone()), Some(joint_nonce)); - - // compute nonce public - // x coordinate (mapped to EC field) of this public is the r-portion of signature - let nonce_public_shares: Vec<_> = (0..n).map(|i| compute_public_share(&nonce_artifacts.polynoms1[i][0]).unwrap()).collect(); - let nonce_public = compute_joint_public(nonce_public_shares.iter()).unwrap(); - let signature_r = compute_ecdsa_r(&nonce_public).unwrap(); - - // compute shares of inv(nonce) so that both nonce && inv(nonce) are still unknown to all nodes - let nonce_inv_shares = run_reciprocal_protocol(t, &nonce_artifacts); - - // compute multiplication of secret-shares * inv-nonce-shares - let mul_shares = run_multiplication_protocol(t, &artifacts.secret_shares, &nonce_inv_shares); - - // compute shares for s portion of signature: nonce_inv * (message_hash + secret * signature_r) - // every node broadcasts this share - let double_t = 2 * t; - let signature_s_shares: Vec<_> = (0..double_t+1).map(|i| compute_ecdsa_s_share( - &nonce_inv_shares[i], - &mul_shares[i], - &signature_r, - &message_hash_scalar - ).unwrap()).collect(); - - // compute signature_s from received shares - let signature_s = compute_ecdsa_s(t, - &signature_s_shares, - &artifacts.id_numbers.iter().take(double_t + 1).cloned().collect::>() - ).unwrap(); - - // check signature - let signature_actual = serialize_ecdsa_signature(&nonce_public, signature_r, signature_s); - let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let joint_secret_pair = KeyPair::from_secret(joint_secret).unwrap(); - assert_eq!(recover(&signature_actual, &message_hash).unwrap(), *joint_secret_pair.public()); - assert!(verify_public(joint_secret_pair.public(), &signature_actual, &message_hash).unwrap()); - } - } - - #[test] - fn full_generation_math_session_with_refreshing_shares() { - let test_cases = vec![(1, 4), (6, 10)]; - for (t, n) in test_cases { - // generate key using t-of-n session - let artifacts1 = run_key_generation(t, n, None, None); - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p1| &p1[0])).unwrap(); - - // let's say we want to refresh existing secret shares - // by doing this every T seconds, and assuming that in each T-second period adversary KS is not able to collect t+1 secret shares - // we can be sure that the scheme is secure - let artifacts2 = run_key_share_refreshing(t, t, n, &artifacts1); - let joint_secret2 = compute_joint_secret_from_shares(t, &artifacts2.secret_shares.iter().take(t + 1).collect::>(), - &artifacts2.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret2); - - // refresh again - let artifacts3 = run_key_share_refreshing(t, t, n, &artifacts2); - let joint_secret3 = compute_joint_secret_from_shares(t, &artifacts3.secret_shares.iter().take(t + 1).collect::>(), - &artifacts3.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret3); - } - } - - #[test] - fn full_generation_math_session_with_adding_new_nodes() { - let test_cases = vec![(1, 3), (1, 4), (6, 10)]; - for (t, n) in test_cases { - // generate key using t-of-n session - let artifacts1 = run_key_generation(t, n, None, None); - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p1| &p1[0])).unwrap(); - - // let's say we want to include additional couple of servers to the set - // so that scheme becames t-of-n+2 - let artifacts2 = run_key_share_refreshing(t, t, n + 2, &artifacts1); - let joint_secret2 = compute_joint_secret_from_shares(t, &artifacts2.secret_shares.iter().take(t + 1).collect::>(), - &artifacts2.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret2); - - // include another server (t-of-n+3) - let artifacts3 = run_key_share_refreshing(t, t, n + 3, &artifacts2); - let joint_secret3 = compute_joint_secret_from_shares(t, &artifacts3.secret_shares.iter().take(t + 1).collect::>(), - &artifacts3.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret3); - } - } - - #[test] - fn full_generation_math_session_with_decreasing_threshold() { - let (t, n) = (3, 5); - - // generate key using t-of-n session - let artifacts1 = run_key_generation(t, n, None, None); - - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p1| &p1[0])).unwrap(); - - // let's say we want to decrease threshold so that it becames (t-1)-of-n - let new_t = t - 1; - let artifacts2 = run_key_share_refreshing(t, new_t, n, &artifacts1); - let joint_secret2 = compute_joint_secret_from_shares(new_t, &artifacts2.secret_shares.iter().take(new_t + 1).collect::>(), - &artifacts2.id_numbers.iter().take(new_t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret2); - - // let's say we want to decrease threshold once again so that it becames (t-2)-of-n - let t = t - 1; - let new_t = t - 2; - let artifacts3 = run_key_share_refreshing(t, new_t, n, &artifacts2); - let joint_secret3 = compute_joint_secret_from_shares(new_t, &artifacts3.secret_shares.iter().take(new_t + 1).collect::>(), - &artifacts3.id_numbers.iter().take(new_t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret3); - } - - #[test] - fn full_zero_secret_generation_math_session() { - let test_cases = vec![(1, 4), (2, 4)]; - for (t, n) in test_cases { - // run joint zero generation session - let id_numbers: Vec<_> = (0..n).map(|_| generate_random_scalar().unwrap()).collect(); - let artifacts = run_zero_key_generation(t, n, &id_numbers); - - // check that zero secret is generated - // we can't compute secrets sum here, because result will be zero (invalid secret, unsupported by SECP256k1) - // so just use complement trick: x + (-x) = 0 - // TODO [Refac]: switch to SECP256K1-free scalar EC arithmetic - let partial_joint_secret = compute_secret_sum(artifacts.polynoms1.iter().take(n - 1).map(|p| &p[0])).unwrap(); - let mut partial_joint_secret_complement = artifacts.polynoms1[n - 1][0].clone(); - partial_joint_secret_complement.neg().unwrap(); - assert_eq!(partial_joint_secret, partial_joint_secret_complement); - } - } - - #[test] - fn full_generation_with_multiplication() { - let test_cases = vec![(1, 3), (2, 5), (2, 7), (3, 8)]; - for (t, n) in test_cases { - // generate two shared secrets - let artifacts1 = run_key_generation(t, n, None, None); - let artifacts2 = run_key_generation(t, n, Some(artifacts1.id_numbers.clone()), None); - - // multiplicate original secrets - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p| &p[0])).unwrap(); - let joint_secret2 = compute_joint_secret(artifacts2.polynoms1.iter().map(|p| &p[0])).unwrap(); - let mut expected_joint_secret_mul = joint_secret1; - expected_joint_secret_mul.mul(&joint_secret2).unwrap(); - - // run multiplication protocol - let joint_secret_mul_shares = run_multiplication_protocol(t, &artifacts1.secret_shares, &artifacts2.secret_shares); - - // calculate actual secrets multiplication - let double_t = t * 2; - let actual_joint_secret_mul = compute_joint_secret_from_shares(double_t, - &joint_secret_mul_shares.iter().take(double_t + 1).collect::>(), - &artifacts1.id_numbers.iter().take(double_t + 1).collect::>()).unwrap(); - - assert_eq!(actual_joint_secret_mul, expected_joint_secret_mul); - } - } - - #[test] - fn full_generation_with_reciprocal() { - let test_cases = vec![(1, 3), (2, 5), (2, 7), (2, 7), (3, 8)]; - for (t, n) in test_cases { - // generate shared secret - let artifacts = run_key_generation(t, n, None, None); - - // calculate inversion of original shared secret - let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let mut expected_joint_secret_inv = joint_secret.clone(); - invert_secret(&mut expected_joint_secret_inv).unwrap(); - - // run inversion protocol - let reciprocal_shares = run_reciprocal_protocol(t, &artifacts); - - // calculate actual secret inversion - let double_t = t * 2; - let actual_joint_secret_inv = compute_joint_secret_from_shares(double_t, - &reciprocal_shares.iter().take(double_t + 1).collect::>(), - &artifacts.id_numbers.iter().take(double_t + 1).collect::>()).unwrap(); - - assert_eq!(actual_joint_secret_inv, expected_joint_secret_inv); - } - } - - #[test] - fn multiplying_secret_inversion_with_secret_gives_one() { - use std::str::FromStr; - let secret = Random.generate().secret().clone(); - let mut inversion = secret.clone(); - invert_secret(&mut inversion).unwrap(); - inversion.mul(&secret).unwrap(); - assert_eq!( - inversion, - Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap() - ); - } - -} diff --git a/secret-store/src/key_server_cluster/message.rs b/secret-store/src/key_server_cluster/message.rs deleted file mode 100644 index 396c5a993..000000000 --- a/secret-store/src/key_server_cluster/message.rs +++ /dev/null @@ -1,1557 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::Secret; -use key_server_cluster::SessionId; -use super::{Error, SerializableH256, SerializablePublic, SerializableSecret, - SerializableSignature, SerializableMessageHash, SerializableRequester, SerializableAddress}; - -pub type MessageSessionId = SerializableH256; -pub type MessageNodeId = SerializablePublic; - -/// All possible messages that can be sent during encryption/decryption sessions. -#[derive(Clone, Debug)] -pub enum Message { - /// Cluster message. - Cluster(ClusterMessage), - /// Key generation message. - Generation(GenerationMessage), - /// Encryption message. - Encryption(EncryptionMessage), - /// Decryption message. - Decryption(DecryptionMessage), - /// Schnorr signing message. - SchnorrSigning(SchnorrSigningMessage), - /// ECDSA signing message. - EcdsaSigning(EcdsaSigningMessage), - /// Key version negotiation message. - KeyVersionNegotiation(KeyVersionNegotiationMessage), - /// Share add message. - ShareAdd(ShareAddMessage), - /// Servers set change message. - ServersSetChange(ServersSetChangeMessage), -} - -/// All possible cluster-level messages. -#[derive(Clone, Debug)] -pub enum ClusterMessage { - /// Introduce node public key. - NodePublicKey(NodePublicKey), - /// Confirm that node owns its private key. - NodePrivateKeySignature(NodePrivateKeySignature), - /// Keep alive message. - KeepAlive(KeepAlive), - /// Keep alive message response. - KeepAliveResponse(KeepAliveResponse), -} - -/// All possible messages that can be sent during key generation session. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum GenerationMessage { - /// Initialize new DKG session. - InitializeSession(InitializeSession), - /// Confirm DKG session initialization. - ConfirmInitialization(ConfirmInitialization), - /// Broadcast data, calculated during session initialization phase. - CompleteInitialization(CompleteInitialization), - /// Generated keys are sent to every node. - KeysDissemination(KeysDissemination), - /// Broadcast self public key portion. - PublicKeyShare(PublicKeyShare), - /// When session error has occured. - SessionError(SessionError), - /// When session is completed. - SessionCompleted(SessionCompleted), -} - -/// All possible messages that can be sent during encryption session. -#[derive(Clone, Debug)] -pub enum EncryptionMessage { - /// Initialize encryption session. - InitializeEncryptionSession(InitializeEncryptionSession), - /// Confirm/reject encryption session initialization. - ConfirmEncryptionInitialization(ConfirmEncryptionInitialization), - /// When encryption session error has occured. - EncryptionSessionError(EncryptionSessionError), -} - -/// All possible messages that can be sent during consensus establishing. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ConsensusMessage { - /// Initialize consensus session. - InitializeConsensusSession(InitializeConsensusSession), - /// Confirm/reject consensus session initialization. - ConfirmConsensusInitialization(ConfirmConsensusInitialization), -} - -/// All possible messages that can be sent during servers-set consensus establishing. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ConsensusMessageWithServersSet { - /// Initialize consensus session. - InitializeConsensusSession(InitializeConsensusSessionWithServersSet), - /// Confirm/reject consensus session initialization. - ConfirmConsensusInitialization(ConfirmConsensusInitialization), -} - -/// All possible messages that can be sent during share add consensus establishing. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ConsensusMessageOfShareAdd { - /// Initialize consensus session. - InitializeConsensusSession(InitializeConsensusSessionOfShareAdd), - /// Confirm/reject consensus session initialization. - ConfirmConsensusInitialization(ConfirmConsensusInitialization), -} - -/// All possible messages that can be sent during decryption session. -#[derive(Clone, Debug)] -pub enum DecryptionMessage { - /// Consensus establishing message. - DecryptionConsensusMessage(DecryptionConsensusMessage), - /// Request partial decryption from node. - RequestPartialDecryption(RequestPartialDecryption), - /// Partial decryption is completed. - PartialDecryption(PartialDecryption), - /// When decryption session error has occured. - DecryptionSessionError(DecryptionSessionError), - /// When decryption session is completed. - DecryptionSessionCompleted(DecryptionSessionCompleted), - /// When decryption session is delegated to another node. - DecryptionSessionDelegation(DecryptionSessionDelegation), - /// When delegated decryption session is completed. - DecryptionSessionDelegationCompleted(DecryptionSessionDelegationCompleted), -} - -/// All possible messages that can be sent during Schnorr signing session. -#[derive(Clone, Debug)] -pub enum SchnorrSigningMessage { - /// Consensus establishing message. - SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage), - /// Session key generation message. - SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage), - /// Request partial signature from node. - SchnorrRequestPartialSignature(SchnorrRequestPartialSignature), - /// Partial signature is generated. - SchnorrPartialSignature(SchnorrPartialSignature), - /// Signing error occured. - SchnorrSigningSessionError(SchnorrSigningSessionError), - /// Signing session completed. - SchnorrSigningSessionCompleted(SchnorrSigningSessionCompleted), - /// When signing session is delegated to another node. - SchnorrSigningSessionDelegation(SchnorrSigningSessionDelegation), - /// When delegated signing session is completed. - SchnorrSigningSessionDelegationCompleted(SchnorrSigningSessionDelegationCompleted), -} - -/// All possible messages that can be sent during ECDSA signing session. -#[derive(Clone, Debug)] -pub enum EcdsaSigningMessage { - /// Consensus establishing message. - EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage), - /// Signature nonce generation message. - EcdsaSignatureNonceGenerationMessage(EcdsaSignatureNonceGenerationMessage), - /// Inversion nonce generation message. - EcdsaInversionNonceGenerationMessage(EcdsaInversionNonceGenerationMessage), - /// Inversion zero generation message. - EcdsaInversionZeroGenerationMessage(EcdsaInversionZeroGenerationMessage), - /// Inversed nonce coefficient share. - EcdsaSigningInversedNonceCoeffShare(EcdsaSigningInversedNonceCoeffShare), - /// Request partial signature from node. - EcdsaRequestPartialSignature(EcdsaRequestPartialSignature), - /// Partial signature is generated. - EcdsaPartialSignature(EcdsaPartialSignature), - /// Signing error occured. - EcdsaSigningSessionError(EcdsaSigningSessionError), - /// Signing session completed. - EcdsaSigningSessionCompleted(EcdsaSigningSessionCompleted), - /// When signing session is delegated to another node. - EcdsaSigningSessionDelegation(EcdsaSigningSessionDelegation), - /// When delegated signing session is completed. - EcdsaSigningSessionDelegationCompleted(EcdsaSigningSessionDelegationCompleted), -} - -/// All possible messages that can be sent during servers set change session. -#[derive(Clone, Debug)] -pub enum ServersSetChangeMessage { - /// Consensus establishing message. - ServersSetChangeConsensusMessage(ServersSetChangeConsensusMessage), - /// Unknown sessions ids request. - UnknownSessionsRequest(UnknownSessionsRequest), - /// Unknown sessions ids. - UnknownSessions(UnknownSessions), - /// Negotiating key version to use as a base for ShareAdd session. - ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation), - /// Initialize share change session(s). - InitializeShareChangeSession(InitializeShareChangeSession), - /// Confirm share change session(s) initialization. - ConfirmShareChangeSessionInitialization(ConfirmShareChangeSessionInitialization), - /// Share change session delegation. - ServersSetChangeDelegate(ServersSetChangeDelegate), - /// Share change session delegation response. - ServersSetChangeDelegateResponse(ServersSetChangeDelegateResponse), - /// Share add message. - ServersSetChangeShareAddMessage(ServersSetChangeShareAddMessage), - /// Servers set change session completed. - ServersSetChangeError(ServersSetChangeError), - /// Servers set change session completed. - ServersSetChangeCompleted(ServersSetChangeCompleted), -} - -/// All possible messages that can be sent during share add session. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ShareAddMessage { - /// Consensus establishing message. - ShareAddConsensusMessage(ShareAddConsensusMessage), - /// Common key share data is sent to new node. - KeyShareCommon(KeyShareCommon), - /// Generated keys are sent to every node. - NewKeysDissemination(NewKeysDissemination), - /// When session error has occured. - ShareAddError(ShareAddError), -} - -/// All possible messages that can be sent during key version negotiation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum KeyVersionNegotiationMessage { - /// Request key versions. - RequestKeyVersions(RequestKeyVersions), - /// Key versions. - KeyVersions(KeyVersions), - /// When session error has occured. - KeyVersionsError(KeyVersionsError), -} - -/// Introduce node public key. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NodePublicKey { - /// Node identifier (aka node public key). - pub node_id: MessageNodeId, - /// Random data, which must be signed by peer to prove that he owns the corresponding private key. - pub confirmation_plain: SerializableH256, - /// The same random `confirmation_plain`, signed with one-time session key. - pub confirmation_signed_session: SerializableSignature, -} - -/// Confirm that node owns the private key of previously passed public key (aka node id). -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NodePrivateKeySignature { - /// Previously passed `confirmation_plain`, signed with node private key. - pub confirmation_signed: SerializableSignature, -} - -/// Ask if the node is still alive. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeepAlive { -} - -/// Confirm that the node is still alive. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeepAliveResponse { - /// Session id, if used for session-level keep alive. - pub session_id: Option, -} - -/// Initialize new DKG session. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeSession { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Session origin address (if any). - pub origin: Option, - /// Session author. - pub author: SerializableAddress, - /// All session participants along with their identification numbers. - pub nodes: BTreeMap, - /// Is zero secret generation session? - pub is_zero: bool, - /// Decryption threshold. During decryption threshold-of-route.len() nodes must came to - /// consensus to successfully decrypt message. - pub threshold: usize, - /// Derived generation point. Starting from originator, every node must multiply this - /// point by random scalar (unknown by other nodes). At the end of initialization - /// `point` will be some (k1 * k2 * ... * kn) * G = `point` where `(k1 * k2 * ... * kn)` - /// is unknown for every node. - pub derived_point: SerializablePublic, -} - -/// Confirm DKG session initialization. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmInitialization { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Derived generation point. - pub derived_point: SerializablePublic, -} - -/// Broadcast generated point to every other node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CompleteInitialization { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Derived generation point. - pub derived_point: SerializablePublic, -} - -/// Generated keys are sent to every node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeysDissemination { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Secret 1. - pub secret1: SerializableSecret, - /// Secret 2. - pub secret2: SerializableSecret, - /// Public values. - pub publics: Vec, -} - -/// Node is sharing its public key share. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PublicKeyShare { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Public key share. - pub public_share: SerializablePublic, -} - -/// When session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SessionError { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// When session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SessionCompleted { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Node is requested to prepare for saving encrypted data. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeEncryptionSession { - /// Encryption session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Requester. - pub requester: SerializableRequester, - /// Common point. - pub common_point: SerializablePublic, - /// Encrypted data. - pub encrypted_point: SerializablePublic, -} - -/// Node is responding to encryption initialization request. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmEncryptionInitialization { - /// Encryption session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When encryption session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EncryptionSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// Node is asked to be part of consensus group. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeConsensusSession { - /// Requester. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, -} - -/// Node is responding to consensus initialization request. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmConsensusInitialization { - /// Is node confirmed consensus participation. - pub is_confirmed: bool, -} - -/// Node is asked to be part of servers-set consensus group. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeConsensusSessionWithServersSet { - /// Migration id (if any). - pub migration_id: Option, - /// Old nodes set. - pub old_nodes_set: BTreeSet, - /// New nodes set. - pub new_nodes_set: BTreeSet, - /// Old server set, signed by requester. - pub old_set_signature: SerializableSignature, - /// New server set, signed by requester. - pub new_set_signature: SerializableSignature, -} - -/// Node is asked to be part of servers-set consensus group. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeConsensusSessionOfShareAdd { - /// Key version. - pub version: SerializableH256, - /// Nodes that have reported version ownership. - pub version_holders: BTreeSet, - /// threshold+1 nodes from old_nodes_set selected for shares redistribution. - pub consensus_group: BTreeSet, - /// Old nodes set: all non-isolated owners of selected key share version. - pub old_nodes_set: BTreeSet, - /// New nodes map: node id => node id number. - pub new_nodes_map: BTreeMap, - /// Old server set, signed by requester. - pub old_set_signature: SerializableSignature, - /// New server set, signed by requester. - pub new_set_signature: SerializableSignature, -} - -/// Consensus-related Schnorr signing message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningConsensusMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessage, -} - -/// Session key generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// Request partial Schnorr signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrRequestPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Message hash. - pub message_hash: SerializableMessageHash, - /// Selected nodes. - pub nodes: BTreeSet, -} - -/// Partial Schnorr signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// S part of signature. - pub partial_signature: SerializableSecret, -} - -/// When Schnorr signing session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// Schnorr signing session completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionCompleted { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When Schnorr signing session is delegated to another node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionDelegation { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Requester. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, - /// Message hash. - pub message_hash: SerializableH256, -} - -/// When delegated Schnorr signing session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionDelegationCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// S-portion of signature. - pub signature_s: SerializableSecret, - /// C-portion of signature. - pub signature_c: SerializableSecret, -} - -/// Consensus-related ECDSA signing message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningConsensusMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessage, -} - -/// ECDSA signature nonce generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSignatureNonceGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// ECDSA inversion nonce generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaInversionNonceGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// ECDSA inversed nonce share message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningInversedNonceCoeffShare { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Inversed nonce coefficient share. - pub inversed_nonce_coeff_share: SerializableSecret, -} - -/// ECDSA inversion zero generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaInversionZeroGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// Request partial ECDSA signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaRequestPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// ECDSA reversed-nonce coefficient - pub inversed_nonce_coeff: SerializableSecret, - /// Message hash. - pub message_hash: SerializableMessageHash, -} - -/// Partial ECDSA signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Partial S part of signature. - pub partial_signature_s: SerializableSecret, -} - -/// When ECDSA signing session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// ECDSA signing session completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionCompleted { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When ECDSA signing session is delegated to another node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionDelegation { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Requestor signature. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, - /// Message hash. - pub message_hash: SerializableH256, -} - -/// When delegated ECDSA signing session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionDelegationCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Signature. - pub signature: SerializableSignature, -} - -/// Consensus-related decryption message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionConsensusMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Session origin (in consensus initialization message). - pub origin: Option, - /// Consensus message. - pub message: ConsensusMessage, -} - -/// Node is requested to do a partial decryption. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RequestPartialDecryption { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Is shadow decryption requested? When true, decryption result - /// will be visible to the owner of requestor public key only. - pub is_shadow_decryption: bool, - /// Decryption result must be reconstructed on all participating nodes. This is useful - /// for service contract API so that all nodes from consensus group can confirm decryption. - pub is_broadcast_session: bool, - /// Nodes that are agreed to do a decryption. - pub nodes: BTreeSet, -} - -/// Node has partially decrypted the secret. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PartialDecryption { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Partially decrypted secret. - pub shadow_point: SerializablePublic, - /// Decrypt shadow coefficient (if requested), encrypted with requestor public. - pub decrypt_shadow: Option>, -} - -/// When decryption session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// When decryption session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When decryption session is delegated to another node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionDelegation { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Session origin. - pub origin: Option, - /// Requester. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, - /// Is shadow decryption requested? When true, decryption result - /// will be visible to the owner of requestor public key only. - pub is_shadow_decryption: bool, - /// Decryption result must be reconstructed on all participating nodes. This is useful - /// for service contract API so that all nodes from consensus group can confirm decryption. - pub is_broadcast_session: bool, -} - -/// When delegated decryption session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionDelegationCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Decrypted secret point. It is partially decrypted if shadow decrpytion was requested. - pub decrypted_secret: SerializablePublic, - /// Shared common point. - pub common_point: Option, - /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. - pub decrypt_shadows: Option>>, -} - -/// Consensus-related servers set change message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeConsensusMessage { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessageWithServersSet, -} - -/// Unknown sessions ids request. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct UnknownSessionsRequest { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Unknown session ids. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct UnknownSessions { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Unknown session id. - pub unknown_sessions: BTreeSet, -} - -/// Key version negotiation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShareChangeKeyVersionNegotiation { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key version negotiation message. - pub message: KeyVersionNegotiationMessage, -} - -/// Master node opens share initialize session on other nodes. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeShareChangeSession { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key id. - pub key_id: MessageSessionId, - /// Key vesion to use in ShareAdd session. - pub version: SerializableH256, - /// Nodes that have confirmed version ownership. - pub version_holders: BTreeSet, - /// Master node. - pub master_node_id: MessageNodeId, - /// Consensus group to use in ShareAdd session. - pub consensus_group: BTreeSet, - /// Shares to add. Values are filled for new nodes only. - pub new_nodes_map: BTreeMap>, -} - -/// Slave node confirms session initialization. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmShareChangeSessionInitialization { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Sessions that are confirmed. - pub key_id: MessageSessionId, -} - -/// Share change is requested. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeDelegate { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key id. - pub key_id: MessageSessionId, -} - -/// Share change is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeDelegateResponse { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key id. - pub key_id: MessageSessionId, -} - -/// Servers set change share add message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeShareAddMessage { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Unknown session id. - pub message: ShareAddMessage, -} - -/// When servers set change session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeError { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// When servers set change session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeCompleted { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Consensus-related share add session message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShareAddConsensusMessage { - /// Share add session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessageOfShareAdd, -} - -/// Key share common data is passed to new node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyShareCommon { - /// Generation session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Common key data. - pub key_common: CommonKeyData, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Selected version id numbers. - pub id_numbers: BTreeMap, -} - -/// Generated keys are sent to every node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NewKeysDissemination { - /// Generation session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Sub share of rcevier' secret share. - pub secret_subshare: SerializableSecret, -} - -/// When share add session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShareAddError { - /// Generation session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// Key versions are requested. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RequestKeyVersions { - /// Generation session id. - pub session: MessageSessionId, - /// Version negotiation session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Key versions are sent. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyVersions { - /// Generation session id. - pub session: MessageSessionId, - /// Version negotiation session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Common key data, shared by all versions. - pub key_common: Option, - /// Key versions. - pub versions: Vec, -} - -/// Common key data. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CommonKeyData { - /// Key threshold. - pub threshold: usize, - /// Author of the key entry. - pub author: SerializableAddress, - /// Joint public. - pub public: SerializablePublic, -} - -/// When key versions error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyVersionsError { - /// Generation session id. - pub session: MessageSessionId, - /// Version negotiation session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, - /// Continue action from failed node (if any). This field is oly filled - /// when error has occured when trying to compute result on master node. - pub continue_with: Option, -} - -/// Key version continue action from failed node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum FailedKeyVersionContinueAction { - /// Decryption session: origin + requester. - Decrypt(Option, SerializableAddress), -} - -impl Message { - pub fn is_initialization_message(&self) -> bool { - match *self { - Message::Generation(GenerationMessage::InitializeSession(_)) => true, - Message::Encryption(EncryptionMessage::InitializeEncryptionSession(_)) => true, - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(ref msg)) => match msg.message { - ConsensusMessage::InitializeConsensusSession(_) => true, - _ => false - }, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg)) => match msg.message { - ConsensusMessage::InitializeConsensusSession(_) => true, - _ => false - }, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg)) => match msg.message { - ConsensusMessage::InitializeConsensusSession(_) => true, - _ => false - }, - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(_)) => true, - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(ref msg)) if msg.continue_with.is_some() => true, - Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ref msg)) => match msg.message { - ConsensusMessageOfShareAdd::InitializeConsensusSession(_) => true, - _ => false - }, - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref msg)) => match msg.message { - ConsensusMessageWithServersSet::InitializeConsensusSession(_) => true, - _ => false - }, - _ => false, - } - } - - pub fn is_delegation_message(&self) -> bool { - match *self { - Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(_)) => true, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(_)) => true, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(_)) => true, - _ => false, - } - } - - pub fn is_error_message(&self) -> bool { - match *self { - Message::Generation(GenerationMessage::SessionError(_)) => true, - Message::Encryption(EncryptionMessage::EncryptionSessionError(_)) => true, - Message::Decryption(DecryptionMessage::DecryptionSessionError(_)) => true, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(_)) => true, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(_)) => true, - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(_)) => true, - Message::ShareAdd(ShareAddMessage::ShareAddError(_)) => true, - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(_)) => true, - _ => false, - } - } - - pub fn is_exclusive_session_message(&self) -> bool { - match *self { - Message::ServersSetChange(_) => true, - _ => false, - } - } - - pub fn session_nonce(&self) -> Option { - match *self { - Message::Cluster(_) => None, - Message::Generation(ref message) => Some(message.session_nonce()), - Message::Encryption(ref message) => Some(message.session_nonce()), - Message::Decryption(ref message) => Some(message.session_nonce()), - Message::SchnorrSigning(ref message) => Some(message.session_nonce()), - Message::EcdsaSigning(ref message) => Some(message.session_nonce()), - Message::ShareAdd(ref message) => Some(message.session_nonce()), - Message::ServersSetChange(ref message) => Some(message.session_nonce()), - Message::KeyVersionNegotiation(ref message) => Some(message.session_nonce()), - } - } -} - -impl GenerationMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - GenerationMessage::InitializeSession(ref msg) => &msg.session, - GenerationMessage::ConfirmInitialization(ref msg) => &msg.session, - GenerationMessage::CompleteInitialization(ref msg) => &msg.session, - GenerationMessage::KeysDissemination(ref msg) => &msg.session, - GenerationMessage::PublicKeyShare(ref msg) => &msg.session, - GenerationMessage::SessionError(ref msg) => &msg.session, - GenerationMessage::SessionCompleted(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - GenerationMessage::InitializeSession(ref msg) => msg.session_nonce, - GenerationMessage::ConfirmInitialization(ref msg) => msg.session_nonce, - GenerationMessage::CompleteInitialization(ref msg) => msg.session_nonce, - GenerationMessage::KeysDissemination(ref msg) => msg.session_nonce, - GenerationMessage::PublicKeyShare(ref msg) => msg.session_nonce, - GenerationMessage::SessionError(ref msg) => msg.session_nonce, - GenerationMessage::SessionCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl EncryptionMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - EncryptionMessage::InitializeEncryptionSession(ref msg) => &msg.session, - EncryptionMessage::ConfirmEncryptionInitialization(ref msg) => &msg.session, - EncryptionMessage::EncryptionSessionError(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - EncryptionMessage::InitializeEncryptionSession(ref msg) => msg.session_nonce, - EncryptionMessage::ConfirmEncryptionInitialization(ref msg) => msg.session_nonce, - EncryptionMessage::EncryptionSessionError(ref msg) => msg.session_nonce, - } - } -} - -impl DecryptionMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref msg) => &msg.session, - DecryptionMessage::RequestPartialDecryption(ref msg) => &msg.session, - DecryptionMessage::PartialDecryption(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionError(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionCompleted(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionDelegation(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionDelegationCompleted(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref msg) => &msg.sub_session, - DecryptionMessage::RequestPartialDecryption(ref msg) => &msg.sub_session, - DecryptionMessage::PartialDecryption(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionError(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionCompleted(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionDelegation(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionDelegationCompleted(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref msg) => msg.session_nonce, - DecryptionMessage::RequestPartialDecryption(ref msg) => msg.session_nonce, - DecryptionMessage::PartialDecryption(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionError(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionCompleted(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionDelegation(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionDelegationCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl SchnorrSigningMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl EcdsaSigningMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl ServersSetChangeMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref msg) => &msg.session, - ServersSetChangeMessage::UnknownSessionsRequest(ref msg) => &msg.session, - ServersSetChangeMessage::UnknownSessions(ref msg) => &msg.session, - ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref msg) => &msg.session, - ServersSetChangeMessage::InitializeShareChangeSession(ref msg) => &msg.session, - ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeDelegate(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeDelegateResponse(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeError(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeCompleted(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref msg) => msg.session_nonce, - ServersSetChangeMessage::UnknownSessionsRequest(ref msg) => msg.session_nonce, - ServersSetChangeMessage::UnknownSessions(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref msg) => msg.session_nonce, - ServersSetChangeMessage::InitializeShareChangeSession(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeDelegate(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeDelegateResponse(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeError(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl ShareAddMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - ShareAddMessage::ShareAddConsensusMessage(ref msg) => &msg.session, - ShareAddMessage::KeyShareCommon(ref msg) => &msg.session, - ShareAddMessage::NewKeysDissemination(ref msg) => &msg.session, - ShareAddMessage::ShareAddError(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - ShareAddMessage::ShareAddConsensusMessage(ref msg) => msg.session_nonce, - ShareAddMessage::KeyShareCommon(ref msg) => msg.session_nonce, - ShareAddMessage::NewKeysDissemination(ref msg) => msg.session_nonce, - ShareAddMessage::ShareAddError(ref msg) => msg.session_nonce, - } - } -} - -impl KeyVersionNegotiationMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(ref msg) => &msg.session, - KeyVersionNegotiationMessage::KeyVersions(ref msg) => &msg.session, - KeyVersionNegotiationMessage::KeyVersionsError(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(ref msg) => &msg.sub_session, - KeyVersionNegotiationMessage::KeyVersions(ref msg) => &msg.sub_session, - KeyVersionNegotiationMessage::KeyVersionsError(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(ref msg) => msg.session_nonce, - KeyVersionNegotiationMessage::KeyVersions(ref msg) => msg.session_nonce, - KeyVersionNegotiationMessage::KeyVersionsError(ref msg) => msg.session_nonce, - } - } -} - -impl fmt::Display for Message { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Message::Cluster(ref message) => write!(f, "Cluster.{}", message), - Message::Generation(ref message) => write!(f, "Generation.{}", message), - Message::Encryption(ref message) => write!(f, "Encryption.{}", message), - Message::Decryption(ref message) => write!(f, "Decryption.{}", message), - Message::SchnorrSigning(ref message) => write!(f, "SchnorrSigning.{}", message), - Message::EcdsaSigning(ref message) => write!(f, "EcdsaSigning.{}", message), - Message::ServersSetChange(ref message) => write!(f, "ServersSetChange.{}", message), - Message::ShareAdd(ref message) => write!(f, "ShareAdd.{}", message), - Message::KeyVersionNegotiation(ref message) => write!(f, "KeyVersionNegotiation.{}", message), - } - } -} - -impl fmt::Display for ClusterMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ClusterMessage::NodePublicKey(_) => write!(f, "NodePublicKey"), - ClusterMessage::NodePrivateKeySignature(_) => write!(f, "NodePrivateKeySignature"), - ClusterMessage::KeepAlive(_) => write!(f, "KeepAlive"), - ClusterMessage::KeepAliveResponse(_) => write!(f, "KeepAliveResponse"), - } - } -} - -impl fmt::Display for GenerationMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GenerationMessage::InitializeSession(_) => write!(f, "InitializeSession"), - GenerationMessage::ConfirmInitialization(_) => write!(f, "ConfirmInitialization"), - GenerationMessage::CompleteInitialization(_) => write!(f, "CompleteInitialization"), - GenerationMessage::KeysDissemination(_) => write!(f, "KeysDissemination"), - GenerationMessage::PublicKeyShare(_) => write!(f, "PublicKeyShare"), - GenerationMessage::SessionError(ref msg) => write!(f, "SessionError({})", msg.error), - GenerationMessage::SessionCompleted(_) => write!(f, "SessionCompleted"), - } - } -} - -impl fmt::Display for EncryptionMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - EncryptionMessage::InitializeEncryptionSession(_) => write!(f, "InitializeEncryptionSession"), - EncryptionMessage::ConfirmEncryptionInitialization(_) => write!(f, "ConfirmEncryptionInitialization"), - EncryptionMessage::EncryptionSessionError(ref msg) => write!(f, "EncryptionSessionError({})", msg.error), - } - } -} - -impl fmt::Display for ConsensusMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConsensusMessage::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), - ConsensusMessage::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed), - } - } -} - -impl fmt::Display for ConsensusMessageWithServersSet { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConsensusMessageWithServersSet::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), - ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed), - } - } -} - -impl fmt::Display for ConsensusMessageOfShareAdd { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConsensusMessageOfShareAdd::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), - ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed), - } - } -} - -impl fmt::Display for DecryptionMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref m) => write!(f, "DecryptionConsensusMessage.{}", m.message), - DecryptionMessage::RequestPartialDecryption(_) => write!(f, "RequestPartialDecryption"), - DecryptionMessage::PartialDecryption(_) => write!(f, "PartialDecryption"), - DecryptionMessage::DecryptionSessionError(_) => write!(f, "DecryptionSessionError"), - DecryptionMessage::DecryptionSessionCompleted(_) => write!(f, "DecryptionSessionCompleted"), - DecryptionMessage::DecryptionSessionDelegation(_) => write!(f, "DecryptionSessionDelegation"), - DecryptionMessage::DecryptionSessionDelegationCompleted(_) => write!(f, "DecryptionSessionDelegationCompleted"), - } - } -} - -impl fmt::Display for SchnorrSigningMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref m) => write!(f, "SchnorrSigningConsensusMessage.{}", m.message), - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref m) => write!(f, "SchnorrSigningGenerationMessage.{}", m.message), - SchnorrSigningMessage::SchnorrRequestPartialSignature(_) => write!(f, "SchnorrRequestPartialSignature"), - SchnorrSigningMessage::SchnorrPartialSignature(_) => write!(f, "SchnorrPartialSignature"), - SchnorrSigningMessage::SchnorrSigningSessionError(_) => write!(f, "SchnorrSigningSessionError"), - SchnorrSigningMessage::SchnorrSigningSessionCompleted(_) => write!(f, "SchnorrSigningSessionCompleted"), - SchnorrSigningMessage::SchnorrSigningSessionDelegation(_) => write!(f, "SchnorrSigningSessionDelegation"), - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(_) => write!(f, "SchnorrSigningSessionDelegationCompleted"), - } - } -} - -impl fmt::Display for EcdsaSigningMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref m) => write!(f, "EcdsaSigningConsensusMessage.{}", m.message), - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref m) => write!(f, "EcdsaSignatureNonceGenerationMessage.{}", m.message), - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref m) => write!(f, "EcdsaInversionNonceGenerationMessage.{}", m.message), - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref m) => write!(f, "EcdsaInversionZeroGenerationMessage.{}", m.message), - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(_) => write!(f, "EcdsaSigningInversedNonceCoeffShare"), - EcdsaSigningMessage::EcdsaRequestPartialSignature(_) => write!(f, "EcdsaRequestPartialSignature"), - EcdsaSigningMessage::EcdsaPartialSignature(_) => write!(f, "EcdsaPartialSignature"), - EcdsaSigningMessage::EcdsaSigningSessionError(_) => write!(f, "EcdsaSigningSessionError"), - EcdsaSigningMessage::EcdsaSigningSessionCompleted(_) => write!(f, "EcdsaSigningSessionCompleted"), - EcdsaSigningMessage::EcdsaSigningSessionDelegation(_) => write!(f, "EcdsaSigningSessionDelegation"), - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(_) => write!(f, "EcdsaSigningSessionDelegationCompleted"), - } - } -} - -impl fmt::Display for ServersSetChangeMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref m) => write!(f, "ServersSetChangeConsensusMessage.{}", m.message), - ServersSetChangeMessage::UnknownSessionsRequest(_) => write!(f, "UnknownSessionsRequest"), - ServersSetChangeMessage::UnknownSessions(_) => write!(f, "UnknownSessions"), - ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref m) => write!(f, "ShareChangeKeyVersionNegotiation.{}", m.message), - ServersSetChangeMessage::InitializeShareChangeSession(_) => write!(f, "InitializeShareChangeSession"), - ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(_) => write!(f, "ConfirmShareChangeSessionInitialization"), - ServersSetChangeMessage::ServersSetChangeDelegate(_) => write!(f, "ServersSetChangeDelegate"), - ServersSetChangeMessage::ServersSetChangeDelegateResponse(_) => write!(f, "ServersSetChangeDelegateResponse"), - ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref m) => write!(f, "ServersSetChangeShareAddMessage.{}", m.message), - ServersSetChangeMessage::ServersSetChangeError(_) => write!(f, "ServersSetChangeError"), - ServersSetChangeMessage::ServersSetChangeCompleted(_) => write!(f, "ServersSetChangeCompleted"), - } - } -} - -impl fmt::Display for ShareAddMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ShareAddMessage::ShareAddConsensusMessage(ref m) => write!(f, "ShareAddConsensusMessage.{}", m.message), - ShareAddMessage::KeyShareCommon(_) => write!(f, "KeyShareCommon"), - ShareAddMessage::NewKeysDissemination(_) => write!(f, "NewKeysDissemination"), - ShareAddMessage::ShareAddError(_) => write!(f, "ShareAddError"), - - } - } -} - -impl fmt::Display for KeyVersionNegotiationMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(_) => write!(f, "RequestKeyVersions"), - KeyVersionNegotiationMessage::KeyVersions(_) => write!(f, "KeyVersions"), - KeyVersionNegotiationMessage::KeyVersionsError(_) => write!(f, "KeyVersionsError"), - } - } -} diff --git a/secret-store/src/key_server_cluster/mod.rs b/secret-store/src/key_server_cluster/mod.rs deleted file mode 100644 index 7a264b70a..000000000 --- a/secret-store/src/key_server_cluster/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use super::types::ServerKeyId; - -pub use super::blockchain::SigningKeyPair; -pub use super::types::{Error, NodeId, Requester, EncryptedDocumentKeyShadow}; -pub use super::acl_storage::AclStorage; -pub use super::key_storage::{KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; -pub use super::key_server_set::{is_migration_required, KeyServerSet, KeyServerSetSnapshot, KeyServerSetMigration}; -pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic, - SerializableRequester, SerializableMessageHash, SerializableAddress}; -pub use self::cluster::{new_network_cluster, ClusterCore, ClusterConfiguration, ClusterClient}; -pub use self::cluster_connections_net::NetConnectionsManagerConfig; -pub use self::cluster_sessions::{ClusterSession, ClusterSessionsListener, WaitableSession}; -#[cfg(test)] -pub use self::cluster::tests::DummyClusterClient; - -#[cfg(test)] -pub use super::node_key_pair::PlainNodeKeyPair; -#[cfg(test)] -pub use super::key_storage::tests::DummyKeyStorage; -pub use super::acl_storage::DummyAclStorage; -#[cfg(test)] -pub use super::key_server_set::tests::MapKeyServerSet; - -pub type SessionId = ServerKeyId; - -/// Session metadata. -#[derive(Debug, Clone)] -pub struct SessionMeta { - /// Key id. - pub id: SessionId, - /// Id of node, which has started this session. - pub master_node_id: NodeId, - /// Id of node, on which this session is running. - pub self_node_id: NodeId, - /// Session threshold. - pub threshold: usize, - /// Count of all configured key server nodes (valid at session start time). - pub configured_nodes_count: usize, - /// Count of all connected key server nodes (valid at session start time). - pub connected_nodes_count: usize, -} - -mod admin_sessions; -mod client_sessions; - -pub use self::admin_sessions::key_version_negotiation_session; -pub use self::admin_sessions::servers_set_change_session; -pub use self::admin_sessions::share_add_session; -pub use self::admin_sessions::share_change_session; - -pub use self::client_sessions::decryption_session; -pub use self::client_sessions::encryption_session; -pub use self::client_sessions::generation_session; -pub use self::client_sessions::signing_session_ecdsa; -pub use self::client_sessions::signing_session_schnorr; - -mod cluster; -mod cluster_connections; -mod cluster_connections_net; -mod cluster_message_processor; -mod cluster_sessions; -mod cluster_sessions_creator; -mod connection_trigger; -mod connection_trigger_with_migration; -mod io; -mod jobs; -pub mod math; -mod message; -mod net; diff --git a/secret-store/src/key_server_cluster/net/accept_connection.rs b/secret-store/src/key_server_cluster/net/accept_connection.rs deleted file mode 100644 index 8ad2e952a..000000000 --- a/secret-store/src/key_server_cluster/net/accept_connection.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use std::sync::Arc; -use std::net::SocketAddr; -use std::time::Duration; -use futures::{Future, Poll}; -use tokio::net::TcpStream; -use blockchain::SigningKeyPair; -use key_server_cluster::Error; -use key_server_cluster::io::{accept_handshake, Handshake, Deadline, deadline}; -use key_server_cluster::net::Connection; - -/// Create future for accepting incoming connection. -pub fn accept_connection(stream: TcpStream, self_key_pair: Arc) -> Deadline { - // TODO: This could fail so it would be better either to accept the - // address as a separate argument or return a result. - let address = stream.peer_addr().expect("Unable to determine tcp peer address"); - - let accept = AcceptConnection { - handshake: accept_handshake(stream, self_key_pair), - address: address, - }; - - deadline(Duration::new(5, 0), accept).expect("Failed to create timeout") -} - -/// Future for accepting incoming connection. -pub struct AcceptConnection { - handshake: Handshake, - address: SocketAddr, -} - -impl Future for AcceptConnection { - type Item = Result; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (stream, result) = try_ready!(self.handshake.poll()); - let result = match result { - Ok(result) => result, - Err(err) => return Ok(Err(err).into()), - }; - let connection = Connection { - stream: stream.into(), - address: self.address, - node_id: result.node_id, - key: result.shared_key, - }; - Ok(Ok(connection).into()) - } -} diff --git a/secret-store/src/key_server_cluster/net/connect.rs b/secret-store/src/key_server_cluster/net/connect.rs deleted file mode 100644 index 532f0105b..000000000 --- a/secret-store/src/key_server_cluster/net/connect.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::BTreeSet; -use std::io; -use std::time::Duration; -use std::net::SocketAddr; -use futures::{Future, Poll, Async}; -use tokio::net::{TcpStream, tcp::ConnectFuture}; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::io::{handshake, Handshake, Deadline, deadline}; -use key_server_cluster::net::Connection; - -/// Create future for connecting to other node. -pub fn connect(address: &SocketAddr, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { - let connect = Connect { - state: ConnectState::TcpConnect(TcpStream::connect(address)), - address: address.clone(), - self_key_pair: self_key_pair, - trusted_nodes: trusted_nodes, - }; - - deadline(Duration::new(5, 0), connect).expect("Failed to create timeout") -} - -enum ConnectState { - TcpConnect(ConnectFuture), - Handshake(Handshake), - Connected, -} - -/// Future for connecting to other node. -pub struct Connect { - state: ConnectState, - address: SocketAddr, - self_key_pair: Arc, - trusted_nodes: BTreeSet, -} - -impl Future for Connect { - type Item = Result; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (next, result) = match self.state { - ConnectState::TcpConnect(ref mut future) => { - let stream = try_ready!(future.poll()); - let handshake = handshake(stream, self.self_key_pair.clone(), self.trusted_nodes.clone()); - (ConnectState::Handshake(handshake), Async::NotReady) - }, - ConnectState::Handshake(ref mut future) => { - let (stream, result) = try_ready!(future.poll()); - let result = match result { - Ok(result) => result, - Err(err) => return Ok(Async::Ready(Err(err))), - }; - let connection = Connection { - stream: stream.into(), - address: self.address, - node_id: result.node_id, - key: result.shared_key, - }; - (ConnectState::Connected, Async::Ready(Ok(connection))) - }, - ConnectState::Connected => panic!("poll Connect after it's done"), - }; - - self.state = next; - match result { - // by polling again, we register new future - Async::NotReady => self.poll(), - result => Ok(result) - } - } -} diff --git a/secret-store/src/key_server_cluster/net/connection.rs b/secret-store/src/key_server_cluster/net/connection.rs deleted file mode 100644 index cf3306a88..000000000 --- a/secret-store/src/key_server_cluster/net/connection.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::net; -use crypto::publickey::KeyPair; -use key_server_cluster::NodeId; -use key_server_cluster::io::SharedTcpStream; - -/// Established connection data -pub struct Connection { - /// Peer address. - pub address: net::SocketAddr, - /// Connection stream. - pub stream: SharedTcpStream, - /// Peer node id. - pub node_id: NodeId, - /// Encryption key. - pub key: KeyPair, -} diff --git a/secret-store/src/key_server_cluster/net/mod.rs b/secret-store/src/key_server_cluster/net/mod.rs deleted file mode 100644 index 2ba3201d7..000000000 --- a/secret-store/src/key_server_cluster/net/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -mod accept_connection; -mod connect; -mod connection; - -pub use self::accept_connection::{AcceptConnection, accept_connection}; -pub use self::connect::{Connect, connect}; -pub use self::connection::Connection; diff --git a/secret-store/src/key_server_set.rs b/secret-store/src/key_server_set.rs deleted file mode 100644 index b4c5622ec..000000000 --- a/secret-store/src/key_server_set.rs +++ /dev/null @@ -1,825 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::net::SocketAddr; -use std::collections::{BTreeMap, HashSet}; -use parking_lot::Mutex; -use ethabi::FunctionOutputDecoder; -use ethereum_types::{H256, Address}; -use crypto::publickey::public_to_address; -use bytes::Bytes; -use types::{Error, Public, NodeAddress, NodeId}; -use blockchain::{SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, BlockId}; - -use_contract!(key_server, "res/key_server_set.json"); - -/// Name of KeyServerSet contract in registry. -const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; -/// Number of blocks (since latest new_set change) required before actually starting migration. -const MIGRATION_CONFIRMATIONS_REQUIRED: u64 = 5; -/// Number of blocks before the same-migration transaction (be it start or confirmation) will be retried. -const TRANSACTION_RETRY_INTERVAL_BLOCKS: u64 = 30; - -#[derive(Default, Debug, Clone, PartialEq)] -/// Key Server Set state. -pub struct KeyServerSetSnapshot { - /// Current set of key servers. - pub current_set: BTreeMap, - /// New set of key servers. - pub new_set: BTreeMap, - /// Current migration data. - pub migration: Option, -} - -#[derive(Default, Debug, Clone, PartialEq)] -/// Key server set migration. -pub struct KeyServerSetMigration { - /// Migration id. - pub id: H256, - /// Migration set of key servers. It is the new_set at the moment of migration start. - pub set: BTreeMap, - /// Master node of the migration process. - pub master: NodeId, - /// Is migration confirmed by this node? - pub is_confirmed: bool, -} - -/// Key Server Set -pub trait KeyServerSet: Send + Sync { - /// Is this node currently isolated from the set? - fn is_isolated(&self) -> bool; - /// Get server set state. - fn snapshot(&self) -> KeyServerSetSnapshot; - /// Start migration. - fn start_migration(&self, migration_id: H256); - /// Confirm migration. - fn confirm_migration(&self, migration_id: H256); -} - -/// On-chain Key Server set implementation. -pub struct OnChainKeyServerSet { - /// Cached on-chain contract. - contract: Mutex, -} - -#[derive(Default, Debug, Clone, PartialEq)] -/// Non-finalized new_set. -struct FutureNewSet { - /// New servers set. - pub new_set: BTreeMap, - /// Hash of block, when this set has appeared for first time. - pub block: H256, -} - -#[derive(Default, Debug, Clone, PartialEq)] -/// Migration-related transaction information. -struct PreviousMigrationTransaction { - /// Migration id. - pub migration_id: H256, - /// Latest actual block number at the time this transaction has been sent. - pub block: u64, -} - -/// Cached on-chain Key Server set contract. -struct CachedContract { - /// Blockchain client. - client: Arc, - /// Contract address source. - contract_address_source: Option, - /// Current contract address. - contract_address: Option
, - /// Is auto-migrate enabled? - auto_migrate_enabled: bool, - /// Current contract state. - snapshot: KeyServerSetSnapshot, - /// Scheduled contract state (if any). - future_new_set: Option, - /// Previous start migration transaction. - start_migration_tx: Option, - /// Previous confirm migration transaction. - confirm_migration_tx: Option, - /// This node key pair. - self_key_pair: Arc, -} - -impl OnChainKeyServerSet { - pub fn new(trusted_client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { - let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(CachedContract::new(trusted_client.clone(), contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), - }); - trusted_client.add_listener(key_server_set.clone()); - Ok(key_server_set) - } -} - -impl KeyServerSet for OnChainKeyServerSet { - fn is_isolated(&self) -> bool { - self.contract.lock().is_isolated() - } - - fn snapshot(&self) -> KeyServerSetSnapshot { - self.contract.lock().snapshot() - } - - fn start_migration(&self, migration_id: H256) { - self.contract.lock().start_migration(migration_id) - } - - fn confirm_migration(&self, migration_id: H256) { - self.contract.lock().confirm_migration(migration_id); - } -} - -impl NewBlocksNotify for OnChainKeyServerSet { - fn new_blocks(&self, _new_enacted_len: usize) { - self.contract.lock().update() - } -} - -trait KeyServerSubset) -> Result, String>> { - fn read_list(&self, f: &F) -> Result, String>; - - fn read_public(&self, address: Address, f: &F) -> Result; - - fn read_address(&self, address: Address, f: &F) -> Result; -} - -struct CurrentKeyServerSubset; - -impl ) -> Result, String>> KeyServerSubset for CurrentKeyServerSubset { - fn read_list(&self, f: &F) -> Result, String> { - let (encoded, decoder) = key_server::functions::get_current_key_servers::call(); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_public(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_current_key_server_public::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_address(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_current_key_server_address::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } -} - -struct MigrationKeyServerSubset; - -impl ) -> Result, String>> KeyServerSubset for MigrationKeyServerSubset { - fn read_list(&self, f: &F) -> Result, String> { - let (encoded, decoder) = key_server::functions::get_migration_key_servers::call(); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_public(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_migration_key_server_public::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_address(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_migration_key_server_address::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } -} - -struct NewKeyServerSubset; - -impl ) -> Result, String>> KeyServerSubset for NewKeyServerSubset { - fn read_list(&self, f: &F) -> Result, String> { - let (encoded, decoder) = key_server::functions::get_new_key_servers::call(); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_public(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_new_key_server_public::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_address(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_new_key_server_address::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } -} - -impl CachedContract { - pub fn new(client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { - let server_set = match contract_address_source.is_none() { - true => key_servers.into_iter() - .map(|(p, addr)| { - let addr = format!("{}:{}", addr.address, addr.port).parse() - .map_err(|err| Error::Internal(format!("error parsing node address: {}", err)))?; - Ok((p, addr)) - }) - .collect::, Error>>()?, - false => Default::default(), - }; - - let mut contract = CachedContract { - client: client, - contract_address_source: contract_address_source, - contract_address: None, - auto_migrate_enabled: auto_migrate_enabled, - future_new_set: None, - confirm_migration_tx: None, - start_migration_tx: None, - snapshot: KeyServerSetSnapshot { - current_set: server_set.clone(), - new_set: server_set, - ..Default::default() - }, - self_key_pair: self_key_pair, - }; - contract.update_contract_address(); - - Ok(contract) - } - - pub fn update_contract_address(&mut self) { - if let Some(ref contract_address_source) = self.contract_address_source { - let contract_address = self.client.read_contract_address( - KEY_SERVER_SET_CONTRACT_REGISTRY_NAME, - contract_address_source - ); - if contract_address != self.contract_address { - trace!(target: "secretstore", "{}: Configuring for key server set contract from address {:?}", - self.self_key_pair.public(), contract_address); - - self.contract_address = contract_address; - } - } - } - - pub fn update(&mut self) { - // no need to update when servers set is hardcoded - if self.contract_address_source.is_none() { - return; - } - - if self.client.is_trusted() { - // read new snapshot from reqistry - self.update_contract_address(); - self.read_from_registry(); - - // update number of confirmations (if there's future new set) - self.update_number_of_confirmations_if_required(); - } - } - - fn is_isolated(&self) -> bool { - !self.snapshot.current_set.contains_key(self.self_key_pair.public()) - } - - fn snapshot(&self) -> KeyServerSetSnapshot { - self.snapshot.clone() - } - - fn start_migration(&mut self, migration_id: H256) { - // trust is not needed here, because it is the reaction to the read of the trusted client - if let Some(contract_address) = self.contract_address.as_ref() { - // check if we need to send start migration transaction - if !update_last_transaction_block(&*self.client, &migration_id, &mut self.start_migration_tx) { - return; - } - - // prepare transaction data - let transaction_data = key_server::functions::start_migration::encode_input(migration_id); - - // send transaction - match self.client.transact_contract(*contract_address, transaction_data) { - Ok(_) => trace!(target: "secretstore_net", "{}: sent auto-migration start transaction", - self.self_key_pair.public()), - Err(error) => warn!(target: "secretstore_net", "{}: failed to submit auto-migration start transaction: {}", - self.self_key_pair.public(), error), - } - } - } - - fn confirm_migration(&mut self, migration_id: H256) { - // trust is not needed here, because we have already completed the action - if let (true, Some(contract_address)) = (self.client.is_trusted(), self.contract_address) { - // check if we need to send start migration transaction - if !update_last_transaction_block(&*self.client, &migration_id, &mut self.confirm_migration_tx) { - return; - } - - // prepare transaction data - let transaction_data = key_server::functions::confirm_migration::encode_input(migration_id); - - // send transaction - match self.client.transact_contract(contract_address, transaction_data) { - Ok(_) => trace!(target: "secretstore_net", "{}: sent auto-migration confirm transaction", - self.self_key_pair.public()), - Err(error) => warn!(target: "secretstore_net", "{}: failed to submit auto-migration confirmation transaction: {}", - self.self_key_pair.public(), error), - } - } - } - - fn read_from_registry(&mut self) { - let contract_address = match self.contract_address { - Some(contract_address) => contract_address, - None => { - // no contract installed => empty snapshot - // WARNING: after restart current_set will be reset to the set from configuration file - // even though we have reset to empty set here. We are not considering this as an issue - // because it is actually the issue of administrator. - self.snapshot = Default::default(); - self.future_new_set = None; - return; - }, - }; - - let do_call = |data| self.client.call_contract(BlockId::Latest, contract_address, data); - - let current_set = Self::read_key_server_set(CurrentKeyServerSubset, &do_call); - - // read migration-related data if auto migration is enabled - let (new_set, migration) = match self.auto_migrate_enabled { - true => { - let new_set = Self::read_key_server_set(NewKeyServerSubset, &do_call); - let migration_set = Self::read_key_server_set(MigrationKeyServerSubset, &do_call); - - let migration_id = match migration_set.is_empty() { - false => { - let (encoded, decoder) = key_server::functions::get_migration_id::call(); - do_call(encoded) - .map_err(|e| e.to_string()) - .and_then(|data| decoder.decode(&data).map_err(|e| e.to_string())) - .map_err(|err| { trace!(target: "secretstore", "Error {} reading migration id from contract", err); err }) - .ok() - }, - true => None, - }; - - let migration_master = match migration_set.is_empty() { - false => { - let (encoded, decoder) = key_server::functions::get_migration_master::call(); - do_call(encoded) - .map_err(|e| e.to_string()) - .and_then(|data| decoder.decode(&data).map_err(|e| e.to_string())) - .map_err(|err| { trace!(target: "secretstore", "Error {} reading migration master from contract", err); err }) - .ok() - .and_then(|address| current_set.keys().chain(migration_set.keys()) - .find(|public| public_to_address(public) == address) - .cloned()) - }, - true => None, - }; - - let is_migration_confirmed = match migration_set.is_empty() { - false if current_set.contains_key(self.self_key_pair.public()) || migration_set.contains_key(self.self_key_pair.public()) => { - let (encoded, decoder) = key_server::functions::is_migration_confirmed::call(self.self_key_pair.address()); - do_call(encoded) - .map_err(|e| e.to_string()) - .and_then(|data| decoder.decode(&data).map_err(|e| e.to_string())) - .map_err(|err| { trace!(target: "secretstore", "Error {} reading migration confirmation from contract", err); err }) - .ok() - }, - _ => None, - }; - - let migration = match (migration_set.is_empty(), migration_id, migration_master, is_migration_confirmed) { - (false, Some(migration_id), Some(migration_master), Some(is_migration_confirmed)) => - Some(KeyServerSetMigration { - id: migration_id, - master: migration_master, - set: migration_set, - is_confirmed: is_migration_confirmed, - }), - _ => None, - }; - - (new_set, migration) - } - false => (current_set.clone(), None), - }; - - let mut new_snapshot = KeyServerSetSnapshot { - current_set: current_set, - new_set: new_set, - migration: migration, - }; - - // we might want to adjust new_set if auto migration is enabled - if self.auto_migrate_enabled { - let block = self.client.block_hash(BlockId::Latest).unwrap_or_default(); - update_future_set(&mut self.future_new_set, &mut new_snapshot, block); - } - - self.snapshot = new_snapshot; - } - - fn read_key_server_set(subset: T, do_call: F) -> BTreeMap - where - T: KeyServerSubset, - F: Fn(Vec) -> Result, String> { - let mut key_servers = BTreeMap::new(); - let mut key_servers_addresses = HashSet::new(); - let key_servers_list = subset.read_list(&do_call) - .map_err(|err| { warn!(target: "secretstore_net", "error {} reading list of key servers from contract", err); err }) - .unwrap_or_default(); - for key_server in key_servers_list { - let key_server_public = subset.read_public(key_server, &do_call) - .and_then(|p| if p.len() == 64 { Ok(Public::from_slice(&p)) } else { Err(format!("Invalid public length {}", p.len())) }); - let key_server_address: Result = subset.read_address(key_server, &do_call) - .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); - - // only add successfully parsed nodes - match (key_server_public, key_server_address) { - (Ok(key_server_public), Ok(key_server_address)) => { - if !key_servers_addresses.insert(key_server_address.clone()) { - warn!(target: "secretstore_net", "the same address ({}) specified twice in list of contracts. Ignoring server {}", - key_server_address, key_server_public); - continue; - } - - key_servers.insert(key_server_public, key_server_address); - }, - (Err(public_err), _) => warn!(target: "secretstore_net", "received invalid public from key server set contract: {}", public_err), - (_, Err(ip_err)) => warn!(target: "secretstore_net", "received invalid IP from key server set contract: {}", ip_err), - } - } - key_servers - } - - fn update_number_of_confirmations_if_required(&mut self) { - if !self.auto_migrate_enabled { - return; - } - let client = &*self.client; - update_number_of_confirmations( - &|| latest_block_hash(client), - &|block| block_confirmations(client, block), - &mut self.future_new_set, &mut self.snapshot); - } -} - -/// Check if two sets are equal (in terms of migration requirements). We do not need migration if only -/// addresses are changed - simply adjusting connections is enough in this case. -pub fn is_migration_required(current_set: &BTreeMap, new_set: &BTreeMap) -> bool { - let no_nodes_removed = current_set.keys().all(|n| new_set.contains_key(n)); - let no_nodes_added = new_set.keys().all(|n| current_set.contains_key(n)); - !no_nodes_removed || !no_nodes_added -} - -fn update_future_set(future_new_set: &mut Option, new_snapshot: &mut KeyServerSetSnapshot, block: H256) { - // migration has already started => no need to delay visibility - if new_snapshot.migration.is_some() { - *future_new_set = None; - return; - } - - // no migration is required => no need to delay visibility - if !is_migration_required(&new_snapshot.current_set, &new_snapshot.new_set) { - *future_new_set = None; - return; - } - - // when auto-migrate is enabled, we do not want to start migration right after new_set is changed, because of: - // 1) there could be a fork && we could start migration to forked version (and potentially lose secrets) - // 2) there must be some period for new_set changes finalization (i.e. adding/removing more servers) - let mut new_set = new_snapshot.current_set.clone(); - ::std::mem::swap(&mut new_set, &mut new_snapshot.new_set); - - // if nothing has changed in future_new_set, then we want to preserve previous block hash - let block = match Some(&new_set) == future_new_set.as_ref().map(|f| &f.new_set) { - true => future_new_set.as_ref().map(|f| &f.block).cloned().unwrap_or_else(|| block), - false => block, - }; - - *future_new_set = Some(FutureNewSet { - new_set: new_set, - block: block, - }); -} - -fn update_number_of_confirmations H256, F2: Fn(H256) -> Option>(latest_block: &F1, confirmations: &F2, future_new_set: &mut Option, snapshot: &mut KeyServerSetSnapshot) { - match future_new_set.as_mut() { - // no future new set is scheduled => do nothing, - None => return, - // else we should calculate number of confirmations for future new set - Some(future_new_set) => match confirmations(future_new_set.block.clone()) { - // we have enough confirmations => should move new_set from future to snapshot - Some(confirmations) if confirmations >= MIGRATION_CONFIRMATIONS_REQUIRED => (), - // not enough confirmations => do nothing - Some(_) => return, - // if number of confirmations is None, then reorg has happened && we need to reset block - // (some more intelligent strategy is possible, but let's stick to simplest one) - None => { - future_new_set.block = latest_block(); - return; - } - } - } - - let future_new_set = future_new_set.take() - .expect("we only pass through match above when future_new_set is some; qed"); - snapshot.new_set = future_new_set.new_set; -} - -fn update_last_transaction_block(client: &dyn SecretStoreChain, migration_id: &H256, previous_transaction: &mut Option) -> bool { - let last_block = client.block_number(BlockId::Latest).unwrap_or_default(); - match previous_transaction.as_ref() { - // no previous transaction => send immediately - None => (), - // previous transaction has been sent for other migration process => send immediately - Some(tx) if tx.migration_id != *migration_id => (), - // if we have sent the same type of transaction recently => do nothing (hope it will be mined eventually) - // if we have sent the same transaction some time ago => - // assume that our tx queue was full - // or we didn't have enough eth fot this tx - // or the transaction has been removed from the queue (and never reached any miner node) - // if we have restarted after sending tx => assume we have never sent it - Some(tx) => { - if tx.block > last_block || last_block - tx.block < TRANSACTION_RETRY_INTERVAL_BLOCKS { - return false; - } - }, - } - - *previous_transaction = Some(PreviousMigrationTransaction { - migration_id: migration_id.clone(), - block: last_block, - }); - - true -} - -fn latest_block_hash(client: &dyn SecretStoreChain) -> H256 { - client.block_hash(BlockId::Latest).unwrap_or_default() -} - -fn block_confirmations(client: &dyn SecretStoreChain, block: H256) -> Option { - client.block_number(BlockId::Hash(block)) - .and_then(|block| client.block_number(BlockId::Latest).map(|last_block| (block, last_block))) - .map(|(block, last_block)| last_block - block) -} - -#[cfg(test)] -pub mod tests { - use std::collections::BTreeMap; - use std::net::SocketAddr; - use ethereum_types::{H256, H512}; - use crypto::publickey::Public; - use super::{update_future_set, update_number_of_confirmations, FutureNewSet, - KeyServerSet, KeyServerSetSnapshot, MIGRATION_CONFIRMATIONS_REQUIRED}; - - #[derive(Default)] - pub struct MapKeyServerSet { - is_isolated: bool, - nodes: BTreeMap, - } - - impl MapKeyServerSet { - pub fn new(is_isolated: bool, nodes: BTreeMap) -> Self { - MapKeyServerSet { - is_isolated: is_isolated, - nodes: nodes, - } - } - } - - impl KeyServerSet for MapKeyServerSet { - fn is_isolated(&self) -> bool { - self.is_isolated - } - - fn snapshot(&self) -> KeyServerSetSnapshot { - KeyServerSetSnapshot { - current_set: self.nodes.clone(), - new_set: self.nodes.clone(), - ..Default::default() - } - } - - fn start_migration(&self, _migration_id: H256) { - unimplemented!("test-only") - } - - fn confirm_migration(&self, _migration_id: H256) { - unimplemented!("test-only") - } - } - - #[test] - fn future_set_is_updated_to_none_when_migration_has_already_started() { - let mut future_new_set = Some(Default::default()); - let mut new_snapshot = KeyServerSetSnapshot { - migration: Some(Default::default()), - ..Default::default() - }; - let new_snapshot_copy = new_snapshot.clone(); - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, None); - assert_eq!(new_snapshot, new_snapshot_copy); - } - - #[test] - fn future_set_is_updated_to_none_when_no_migration_is_required() { - let node_id = Default::default(); - let address1 = "127.0.0.1:12000".parse().unwrap(); - let address2 = "127.0.0.1:12001".parse().unwrap(); - - // addresses are different, but node set is the same => no migration is required - let mut future_new_set = Some(Default::default()); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(node_id, address1)].into_iter().collect(), - new_set: vec![(node_id, address2)].into_iter().collect(), - ..Default::default() - }; - let new_snapshot_copy = new_snapshot.clone(); - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, None); - assert_eq!(new_snapshot, new_snapshot_copy); - - // everything is the same => no migration is required - let mut future_new_set = Some(Default::default()); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(node_id, address1)].into_iter().collect(), - new_set: vec![(node_id, address1)].into_iter().collect(), - ..Default::default() - }; - let new_snapshot_copy = new_snapshot.clone(); - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, None); - assert_eq!(new_snapshot, new_snapshot_copy); - } - - #[test] - fn future_set_is_initialized() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = None; - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - ..Default::default() - }; - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - })); - assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn future_set_is_updated_when_set_differs() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(3), address)].into_iter().collect(), - ..Default::default() - }; - update_future_set(&mut future_new_set, &mut new_snapshot, H256::from_low_u64_be(1)); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(3), address)].into_iter().collect(), - block: H256::from_low_u64_be(1), - })); - assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn future_set_is_not_updated_when_set_is_the_same() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - ..Default::default() - }; - update_future_set(&mut future_new_set, &mut new_snapshot, H256::from_low_u64_be(1)); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - })); - assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn when_updating_confirmations_nothing_is_changed_if_no_future_set() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = None; - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - let snapshot_copy = snapshot.clone(); - update_number_of_confirmations( - &|| H256::from_low_u64_be(1), - &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED), - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, None); - assert_eq!(snapshot, snapshot_copy); - } - - #[test] - fn when_updating_confirmations_migration_is_scheduled() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - update_number_of_confirmations( - &|| H256::from_low_u64_be(1), - &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED), - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, None); - assert_eq!(snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn when_updating_confirmations_migration_is_not_scheduled_when_not_enough_confirmations() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - let future_new_set_copy = future_new_set.clone(); - let snapshot_copy = snapshot.clone(); - update_number_of_confirmations( - &|| H256::from_low_u64_be(1), - &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED - 1), - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, future_new_set_copy); - assert_eq!(snapshot, snapshot_copy); - } - - #[test] - fn when_updating_confirmations_migration_is_reset_when_reorganized() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: H256::from_low_u64_be(1), - }); - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - let snapshot_copy = snapshot.clone(); - update_number_of_confirmations( - &|| H256::from_low_u64_be(2), - &|_| None, - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: H256::from_low_u64_be(2), - })); - assert_eq!(snapshot, snapshot_copy); - } -} diff --git a/secret-store/src/key_storage.rs b/secret-store/src/key_storage.rs deleted file mode 100644 index 14a4073da..000000000 --- a/secret-store/src/key_storage.rs +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; -use std::sync::Arc; -use serde_json; -use tiny_keccak::Keccak; -use ethereum_types::{H256, Address}; -use crypto::publickey::{Secret, Public}; -use kvdb::KeyValueDB; -use types::{Error, ServerKeyId, NodeId}; -use serialization::{SerializablePublic, SerializableSecret, SerializableH256, SerializableAddress}; - -/// Encrypted key share, stored by key storage on the single key server. -#[derive(Debug, Default, Clone, PartialEq)] -pub struct DocumentKeyShare { - /// Author of the entry. - pub author: Address, - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Server public key. - pub public: Public, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Key share versions. - pub versions: Vec, -} - -/// Versioned portion of document key share. -#[derive(Debug, Clone, PartialEq)] -pub struct DocumentKeyShareVersion { - /// Version hash (Keccak(time + id_numbers)). - pub hash: H256, - /// Nodes ids numbers. - pub id_numbers: BTreeMap, - /// Node secret share. - pub secret_share: Secret, -} - -/// Document encryption keys storage -pub trait KeyStorage: Send + Sync { - /// Insert document encryption key - fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; - /// Update document encryption key - fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; - /// Get document encryption key - fn get(&self, document: &ServerKeyId) -> Result, Error>; - /// Remove document encryption key - fn remove(&self, document: &ServerKeyId) -> Result<(), Error>; - /// Clears the database - fn clear(&self) -> Result<(), Error>; - /// Check if storage contains document encryption key - fn contains(&self, document: &ServerKeyId) -> bool; - /// Iterate through storage - fn iter<'a>(&'a self) -> Box + 'a>; -} - -/// Persistent document encryption keys storage -pub struct PersistentKeyStorage { - db: Arc, -} - -/// Persistent document encryption keys storage iterator -pub struct PersistentKeyStorageIterator<'a> { - iter: Box, Box<[u8]>)> + 'a>, -} - -/// V3 of encrypted key share, as it is stored by key storage on the single key server. -#[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareV3 { - /// Author of the entry. - pub author: SerializableAddress, - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Server public. - pub public: SerializablePublic, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Versions. - pub versions: Vec -} - -/// V3 of encrypted key share version, as it is stored by key storage on the single key server. -#[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareVersionV3 { - /// Version hash. - pub hash: SerializableH256, - /// Nodes ids numbers. - pub id_numbers: BTreeMap, - /// Node secret share. - pub secret_share: SerializableSecret, -} - -impl PersistentKeyStorage { - /// Create new persistent document encryption keys storage - pub fn new(db: Arc) -> Result { - Ok(Self { db }) - } -} - -impl KeyStorage for PersistentKeyStorage { - fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - let key: SerializableDocumentKeyShareV3 = key.into(); - let key = serde_json::to_vec(&key).map_err(|e| Error::Database(e.to_string()))?; - let mut batch = self.db.transaction(); - batch.put(0, document.as_bytes(), &key); - self.db.write(batch).map_err(Into::into) - } - - fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - self.insert(document, key) - } - - fn get(&self, document: &ServerKeyId) -> Result, Error> { - self.db.get(0, document.as_bytes()) - .map_err(|e| Error::Database(e.to_string())) - .and_then(|key| match key { - None => Ok(None), - Some(key) => serde_json::from_slice::(&key) - .map_err(|e| Error::Database(e.to_string())) - .map(Into::into) - .map(Some), - }) - } - - fn remove(&self, document: &ServerKeyId) -> Result<(), Error> { - let mut batch = self.db.transaction(); - batch.delete(0, document.as_bytes()); - self.db.write(batch).map_err(Into::into) - } - - fn clear(&self) -> Result<(), Error> { - let mut batch = self.db.transaction(); - for (key, _) in self.iter() { - batch.delete(0, key.as_bytes()); - } - self.db.write(batch) - .map_err(|e| Error::Database(e.to_string())) - } - - fn contains(&self, document: &ServerKeyId) -> bool { - self.db.get(0, document.as_bytes()) - .map(|k| k.is_some()) - .unwrap_or(false) - } - - fn iter<'a>(&'a self) -> Box + 'a> { - Box::new(PersistentKeyStorageIterator { - iter: self.db.iter(0), - }) - } -} - -impl<'a> Iterator for PersistentKeyStorageIterator<'a> { - type Item = (ServerKeyId, DocumentKeyShare); - - fn next(&mut self) -> Option<(ServerKeyId, DocumentKeyShare)> { - self.iter.as_mut().next() - .and_then(|(db_key, db_val)| serde_json::from_slice::(&db_val) - .ok() - .map(|key| (ServerKeyId::from_slice(&*db_key), key.into()))) - } -} - -impl DocumentKeyShare { - /// Get last version reference. - #[cfg(test)] - pub fn last_version(&self) -> Result<&DocumentKeyShareVersion, Error> { - self.versions.iter().rev() - .nth(0) - .ok_or_else(|| Error::Database("key version is not found".into())) - } - - /// Get given version reference. - pub fn version(&self, version: &H256) -> Result<&DocumentKeyShareVersion, Error> { - self.versions.iter().rev() - .find(|v| &v.hash == version) - .ok_or_else(|| Error::Database("key version is not found".into())) - } -} - -impl DocumentKeyShareVersion { - /// Create new version - pub fn new(id_numbers: BTreeMap, secret_share: Secret) -> Self { - DocumentKeyShareVersion { - hash: Self::data_hash(id_numbers.iter().map(|(k, v)| (k.as_bytes(), v.as_bytes()))), - id_numbers: id_numbers, - secret_share: secret_share, - } - } - - /// Calculate hash of given version data. - pub fn data_hash<'a, I>(id_numbers: I) -> H256 where I: Iterator { - let mut nodes_keccak = Keccak::new_keccak256(); - - for (node, node_number) in id_numbers { - nodes_keccak.update(node); - nodes_keccak.update(node_number); - } - - let mut nodes_keccak_value = [0u8; 32]; - nodes_keccak.finalize(&mut nodes_keccak_value); - - nodes_keccak_value.into() - } -} - -impl From for SerializableDocumentKeyShareV3 { - fn from(key: DocumentKeyShare) -> Self { - SerializableDocumentKeyShareV3 { - author: key.author.into(), - threshold: key.threshold, - public: key.public.into(), - common_point: key.common_point.map(Into::into), - encrypted_point: key.encrypted_point.map(Into::into), - versions: key.versions.into_iter().map(Into::into).collect(), - } - } -} - -impl From for SerializableDocumentKeyShareVersionV3 { - fn from(version: DocumentKeyShareVersion) -> Self { - SerializableDocumentKeyShareVersionV3 { - hash: version.hash.into(), - id_numbers: version.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - secret_share: version.secret_share.into(), - } - } -} - -impl From for DocumentKeyShare { - fn from(key: SerializableDocumentKeyShareV3) -> Self { - DocumentKeyShare { - author: key.author.into(), - threshold: key.threshold, - public: key.public.into(), - common_point: key.common_point.map(Into::into), - encrypted_point: key.encrypted_point.map(Into::into), - versions: key.versions.into_iter() - .map(|v| DocumentKeyShareVersion { - hash: v.hash.into(), - id_numbers: v.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - secret_share: v.secret_share.into(), - }) - .collect(), - } - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::HashMap; - use std::sync::Arc; - use parking_lot::RwLock; - use tempdir::TempDir; - use crypto::publickey::{Random, Generator, Public}; - use kvdb_rocksdb::{Database, DatabaseConfig}; - use types::{Error, ServerKeyId}; - use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; - - /// In-memory document encryption keys storage - #[derive(Default)] - pub struct DummyKeyStorage { - keys: RwLock>, - } - - impl KeyStorage for DummyKeyStorage { - fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - self.keys.write().insert(document, key); - Ok(()) - } - - fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - self.keys.write().insert(document, key); - Ok(()) - } - - fn get(&self, document: &ServerKeyId) -> Result, Error> { - Ok(self.keys.read().get(document).cloned()) - } - - fn remove(&self, document: &ServerKeyId) -> Result<(), Error> { - self.keys.write().remove(document); - Ok(()) - } - - fn clear(&self) -> Result<(), Error> { - self.keys.write().clear(); - Ok(()) - } - - fn contains(&self, document: &ServerKeyId) -> bool { - self.keys.read().contains_key(document) - } - - fn iter<'a>(&'a self) -> Box + 'a> { - Box::new(self.keys.read().clone().into_iter()) - } - } - - #[test] - fn persistent_key_storage() { - let tempdir = TempDir::new("").unwrap(); - let key1 = ServerKeyId::from_low_u64_be(1); - let value1 = DocumentKeyShare { - author: Default::default(), - threshold: 100, - public: Public::default(), - common_point: Some(Random.generate().public().clone()), - encrypted_point: Some(Random.generate().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: vec![ - (Random.generate().public().clone(), Random.generate().secret().clone()) - ].into_iter().collect(), - secret_share: Random.generate().secret().clone(), - }], - }; - let key2 = ServerKeyId::from_low_u64_be(2); - let value2 = DocumentKeyShare { - author: Default::default(), - threshold: 200, - public: Public::default(), - common_point: Some(Random.generate().public().clone()), - encrypted_point: Some(Random.generate().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: vec![ - (Random.generate().public().clone(), Random.generate().secret().clone()) - ].into_iter().collect(), - secret_share: Random.generate().secret().clone(), - }], - }; - let key3 = ServerKeyId::from_low_u64_be(3); - - let db_config = DatabaseConfig::with_columns(1); - let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); - - let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); - key_storage.insert(key1.clone(), value1.clone()).unwrap(); - key_storage.insert(key2.clone(), value2.clone()).unwrap(); - assert_eq!(key_storage.get(&key1), Ok(Some(value1.clone()))); - assert_eq!(key_storage.get(&key2), Ok(Some(value2.clone()))); - assert_eq!(key_storage.get(&key3), Ok(None)); - drop(key_storage); - - let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); - - let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); - assert_eq!(key_storage.get(&key1), Ok(Some(value1))); - assert_eq!(key_storage.get(&key2), Ok(Some(value2))); - assert_eq!(key_storage.get(&key3), Ok(None)); - } -} diff --git a/secret-store/src/lib.rs b/secret-store/src/lib.rs deleted file mode 100644 index 7b121016b..000000000 --- a/secret-store/src/lib.rs +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -extern crate byteorder; -extern crate ethabi; -extern crate ethereum_types; -extern crate hyper; -extern crate secp256k1; -extern crate keccak_hash as hash; -extern crate kvdb; -extern crate kvdb_rocksdb; -extern crate parity_bytes as bytes; -extern crate parity_crypto as crypto; -extern crate parity_runtime; -extern crate parking_lot; -extern crate percent_encoding; -extern crate rustc_hex; -extern crate serde; -extern crate serde_json; -extern crate tiny_keccak; -extern crate tokio; -extern crate tokio_io; -extern crate tokio_service; -extern crate url; -extern crate jsonrpc_server_utils; - -extern crate ethabi_derive; -#[macro_use] -extern crate ethabi_contract; -#[macro_use] -extern crate futures; -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; - -#[cfg(test)] -extern crate env_logger; -#[cfg(test)] -extern crate tempdir; - -mod key_server_cluster; -mod types; - -mod traits; -mod acl_storage; -mod key_server; -mod key_storage; -mod serialization; -mod key_server_set; -mod node_key_pair; -mod listener; -mod blockchain; -mod migration; - -use std::sync::Arc; -use kvdb::KeyValueDB; -use kvdb_rocksdb::{Database, DatabaseConfig}; -use parity_runtime::Executor; - -pub use types::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, - Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; -pub use traits::KeyServer; -pub use blockchain::{SecretStoreChain, SigningKeyPair, ContractAddress, BlockId, BlockNumber, NewBlocksNotify, Filter}; -pub use self::node_key_pair::PlainNodeKeyPair; - -/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. -pub fn open_secretstore_db(data_path: &str) -> Result, String> { - use std::path::PathBuf; - - migration::upgrade_db(data_path).map_err(|e| e.to_string())?; - - let mut db_path = PathBuf::from(data_path); - db_path.push("db"); - let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?; - - let config = DatabaseConfig::with_columns(1); - Ok(Arc::new(Database::open(&config, &db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) -} - -/// Start new key server instance -pub fn start(trusted_client: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, - db: Arc, executor: Executor) -> Result, Error> -{ - let acl_storage: Arc = match config.acl_check_contract_address.take() { - Some(acl_check_contract_address) => acl_storage::OnChainAclStorage::new(trusted_client.clone(), acl_check_contract_address)?, - None => Arc::new(acl_storage::DummyAclStorage::default()), - }; - - let key_server_set = key_server_set::OnChainKeyServerSet::new(trusted_client.clone(), config.cluster_config.key_server_set_contract_address.take(), - self_key_pair.clone(), config.cluster_config.auto_migrate_enabled, config.cluster_config.nodes.clone())?; - let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(db)?); - let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set.clone(), self_key_pair.clone(), - acl_storage.clone(), key_storage.clone(), executor.clone())?); - let cluster = key_server.cluster(); - let key_server: Arc = key_server; - - // prepare HTTP listener - let http_listener = match config.listener_address { - Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, config.cors, Arc::downgrade(&key_server), executor)?), - None => None, - }; - - // prepare service contract listeners - let create_service_contract = |address, name, api_mask| - Arc::new(listener::service_contract::OnChainServiceContract::new( - api_mask, - trusted_client.clone(), - name, - address, - self_key_pair.clone())); - - let mut contracts: Vec> = Vec::new(); - config.service_contract_address.map(|address| - create_service_contract(address, - listener::service_contract::SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask::all())) - .map(|l| contracts.push(l)); - config.service_contract_srv_gen_address.map(|address| - create_service_contract(address, - listener::service_contract::SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { server_key_generation_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - config.service_contract_srv_retr_address.map(|address| - create_service_contract(address, - listener::service_contract::SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { server_key_retrieval_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - config.service_contract_doc_store_address.map(|address| - create_service_contract(address, - listener::service_contract::DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { document_key_store_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - config.service_contract_doc_sretr_address.map(|address| - create_service_contract(address, - listener::service_contract::DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { document_key_shadow_retrieval_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - - let contract: Option> = match contracts.len() { - 0 => None, - 1 => Some(contracts.pop().expect("contract.len() is 1; qed")), - _ => Some(Arc::new(listener::service_contract_aggregate::OnChainServiceContractAggregate::new(contracts))), - }; - - let contract_listener = match contract { - Some(contract) => Some({ - let listener = listener::service_contract_listener::ServiceContractListener::new( - listener::service_contract_listener::ServiceContractListenerParams { - contract: contract, - self_key_pair: self_key_pair.clone(), - key_server_set: key_server_set, - acl_storage: acl_storage, - cluster: cluster, - key_storage: key_storage, - } - )?; - trusted_client.add_listener(listener.clone()); - listener - }), - None => None, - }; - - Ok(Box::new(listener::Listener::new(key_server, http_listener, contract_listener))) -} diff --git a/secret-store/src/listener/http_listener.rs b/secret-store/src/listener/http_listener.rs deleted file mode 100644 index 663164fa0..000000000 --- a/secret-store/src/listener/http_listener.rs +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use std::sync::{Arc, Weak}; -use futures::future::{ok, result}; -use hyper::{self, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod, - StatusCode as HttpStatusCode, Body, - header::{self, HeaderValue}, - server::conn::Http, - service::Service, -}; -use serde::Serialize; -use serde_json; -use tokio; -use tokio::net::TcpListener; -use parity_runtime::Executor; -use futures::{future, Future, Stream}; -use percent_encoding::percent_decode; - -use traits::KeyServer; -use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; -use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; -use jsonrpc_server_utils::cors::{self, AllowCors, AccessControlAllowOrigin}; - -/// Key server http-requests listener. Available requests: -/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold} -/// To store pregenerated encrypted document key: POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} -/// To generate server && document key: POST /{server_key_id}/{signature}/{threshold} -/// To get public portion of server key: GET /server/{server_key_id}/{signature} -/// To get document key: GET /{server_key_id}/{signature} -/// To get document key shadow: GET /shadow/{server_key_id}/{signature} -/// To generate Schnorr signature with server key: GET /schnorr/{server_key_id}/{signature}/{message_hash} -/// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash} -/// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids - -type CorsDomains = Option>; - -pub struct KeyServerHttpListener { - _executor: Executor, - _handler: Arc, -} - -/// Parsed http request -#[derive(Debug, Clone, PartialEq)] -enum Request { - /// Invalid request - Invalid, - /// Generate server key. - GenerateServerKey(ServerKeyId, RequestSignature, usize), - /// Store document key. - StoreDocumentKey(ServerKeyId, RequestSignature, Public, Public), - /// Generate encryption key. - GenerateDocumentKey(ServerKeyId, RequestSignature, usize), - /// Request public portion of server key. - GetServerKey(ServerKeyId, RequestSignature), - /// Request encryption key of given document for given requestor. - GetDocumentKey(ServerKeyId, RequestSignature), - /// Request shadow of encryption key of given document for given requestor. - GetDocumentKeyShadow(ServerKeyId, RequestSignature), - /// Generate Schnorr signature for the message. - SchnorrSignMessage(ServerKeyId, RequestSignature, MessageHash), - /// Generate ECDSA signature for the message. - EcdsaSignMessage(ServerKeyId, RequestSignature, MessageHash), - /// Change servers set. - ChangeServersSet(RequestSignature, RequestSignature, BTreeSet), -} - -/// Cloneable http handler -#[derive(Clone)] -struct KeyServerHttpHandler { - handler: Arc, - cors: CorsDomains, -} - -/// Shared http handler -struct KeyServerSharedHttpHandler { - key_server: Weak, -} - - -impl KeyServerHttpListener { - /// Start KeyServer http listener - pub fn start(listener_address: NodeAddress, cors_domains: Option>, key_server: Weak, executor: Executor) -> Result { - let shared_handler = Arc::new(KeyServerSharedHttpHandler { - key_server: key_server, - }); - let cors: CorsDomains = cors_domains.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect()); - let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?; - let listener = TcpListener::bind(&listener_address)?; - - let shared_handler2 = shared_handler.clone(); - - let server = listener.incoming() - .map_err(|e| warn!("Key server listener error: {:?}", e)) - .for_each(move |socket| { - let http = Http::new(); - let serve = http.serve_connection(socket, - KeyServerHttpHandler { handler: shared_handler2.clone(), cors: cors.clone() } - ).map(|_| ()).map_err(|e| { - warn!("Key server handler error: {:?}", e); - }); - - tokio::spawn(serve) - }); - - executor.spawn(server); - - let listener = KeyServerHttpListener { - _executor: executor, - _handler: shared_handler, - }; - - Ok(listener) - } -} - -impl KeyServerHttpHandler { - fn key_server(&self) -> Result, Error> { - self.handler.key_server.upgrade() - .ok_or_else(|| Error::Internal("KeyServer is already destroyed".into())) - } - - fn process( - self, - req_method: HttpMethod, - req_uri: Uri, - path: &str, - req_body: &[u8], - cors: AllowCors, - ) -> Box, Error=hyper::Error> + Send> { - match parse_request(&req_method, &path, &req_body) { - Request::GenerateServerKey(document, signature, threshold) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.generate_key(document, signature.into(), threshold)) - .then(move |result| ok(return_server_public_key("GenerateServerKey", &req_uri, cors, result)))), - Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.store_document_key( - document, - signature.into(), - common_point, - encrypted_document_key, - )) - .then(move |result| ok(return_empty("StoreDocumentKey", &req_uri, cors, result)))), - Request::GenerateDocumentKey(document, signature, threshold) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.generate_document_key( - document, - signature.into(), - threshold, - )) - .then(move |result| ok(return_document_key("GenerateDocumentKey", &req_uri, cors, result)))), - Request::GetServerKey(document, signature) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.restore_key_public( - document, - signature.into(), - )) - .then(move |result| ok(return_server_public_key("GetServerKey", &req_uri, cors, result)))), - Request::GetDocumentKey(document, signature) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.restore_document_key(document, signature.into())) - .then(move |result| ok(return_document_key("GetDocumentKey", &req_uri, cors, result)))), - Request::GetDocumentKeyShadow(document, signature) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.restore_document_key_shadow(document, signature.into())) - .then(move |result| ok(return_document_key_shadow("GetDocumentKeyShadow", &req_uri, cors, result)))), - Request::SchnorrSignMessage(document, signature, message_hash) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.sign_message_schnorr( - document, - signature.into(), - message_hash, - )) - .then(move |result| ok(return_message_signature("SchnorrSignMessage", &req_uri, cors, result)))), - Request::EcdsaSignMessage(document, signature, message_hash) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.sign_message_ecdsa( - document, - signature.into(), - message_hash, - )) - .then(move |result| ok(return_message_signature("EcdsaSignMessage", &req_uri, cors, result)))), - Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.change_servers_set( - old_set_signature, - new_set_signature, - new_servers_set, - )) - .then(move |result| ok(return_empty("ChangeServersSet", &req_uri, cors, result)))), - Request::Invalid => { - warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); - Box::new(ok(HttpResponse::builder() - .status(HttpStatusCode::BAD_REQUEST) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed"))) - }, - } - } -} - -impl Service for KeyServerHttpHandler { - type ReqBody = Body; - type ResBody = Body; - type Error = hyper::Error; - type Future = Box, Error=Self::Error> + Send>; - - fn call(&mut self, req: HttpRequest) -> Self::Future { - let cors = cors::get_cors_allow_origin( - req.headers().get(header::ORIGIN).and_then(|value| value.to_str().ok()), - req.headers().get(header::HOST).and_then(|value| value.to_str().ok()), - &self.cors - ); - match cors { - AllowCors::Invalid => { - warn!(target: "secretstore", "Ignoring {}-request {} with unauthorized Origin header", req.method(), req.uri()); - Box::new(future::ok(HttpResponse::builder() - .status(HttpStatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed"))) - }, - _ => { - let req_method = req.method().clone(); - let req_uri = req.uri().clone(); - let path = req_uri.path().to_string(); - // We cannot consume Self because of the Service trait requirement. - let this = self.clone(); - - Box::new(req.into_body().concat2() - .and_then(move |body| this.process(req_method, req_uri, &path, &body, cors))) - } - } - } -} - -fn return_empty(req_type: &str, req_uri: &Uri, cors: AllowCors, empty: Result<(), Error>) -> HttpResponse { - return_bytes::(req_type, req_uri, cors, empty.map(|_| None)) -} - -fn return_server_public_key( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - server_public: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, server_public.map(|k| Some(SerializablePublic(k)))) -} - -fn return_message_signature( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - signature: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, signature.map(|s| Some(SerializableBytes(s)))) -} - -fn return_document_key( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - document_key: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, document_key.map(|k| Some(SerializableBytes(k)))) -} - -fn return_document_key_shadow( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - document_key_shadow: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow { - decrypted_secret: k.decrypted_secret.into(), - common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(), - decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect() - }))) -} - -fn return_bytes( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - result: Result, Error>, -) -> HttpResponse { - match result { - Ok(Some(result)) => match serde_json::to_vec(&result) { - Ok(result) => { - let body: Body = result.into(); - let mut builder = HttpResponse::builder(); - builder.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")); - if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { - builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); - } - builder.body(body).expect("Error creating http response") - }, - Err(err) => { - warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err); - HttpResponse::builder() - .status(HttpStatusCode::INTERNAL_SERVER_ERROR) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") - } - }, - Ok(None) => { - let mut builder = HttpResponse::builder(); - builder.status(HttpStatusCode::OK); - if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { - builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); - } - builder.body(Body::empty()).expect("Nothing to parse, cannot fail; qed") - }, - Err(err) => { - warn!(target: "secretstore", "{} request {} has failed with: {}", req_type, req_uri, err); - return_error(err) - }, - } -} - -fn return_error(err: Error) -> HttpResponse { - let status = match err { - | Error::AccessDenied - | Error::ConsensusUnreachable - | Error::ConsensusTemporaryUnreachable => - HttpStatusCode::FORBIDDEN, - | Error::ServerKeyIsNotFound - | Error::DocumentKeyIsNotFound => - HttpStatusCode::NOT_FOUND, - | Error::InsufficientRequesterData(_) - | Error::Hyper(_) - | Error::Serde(_) - | Error::DocumentKeyAlreadyStored - | Error::ServerKeyAlreadyGenerated => - HttpStatusCode::BAD_REQUEST, - _ => HttpStatusCode::INTERNAL_SERVER_ERROR, - }; - - let mut res = HttpResponse::builder(); - res.status(status); - - // return error text. ignore errors when returning error - let error_text = format!("\"{}\"", err); - if let Ok(error_text) = serde_json::to_vec(&error_text) { - res.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")); - res.body(error_text.into()) - .expect("`error_text` is a formatted string, parsing cannot fail; qed") - } else { - res.body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") - } -} - -fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request { - let uri_path = match percent_decode(uri_path.as_bytes()).decode_utf8() { - Ok(path) => path, - Err(_) => return Request::Invalid, - }; - - let path: Vec = uri_path.trim_start_matches('/').split('/').map(Into::into).collect(); - if path.len() == 0 { - return Request::Invalid; - } - - if path[0] == "admin" { - return parse_admin_request(method, path, body); - } - - let is_known_prefix = &path[0] == "shadow" || &path[0] == "schnorr" || &path[0] == "ecdsa" || &path[0] == "server"; - let (prefix, args_offset) = if is_known_prefix { (&*path[0], 1) } else { ("", 0) }; - let args_count = path.len() - args_offset; - if args_count < 2 || path[args_offset].is_empty() || path[args_offset + 1].is_empty() { - return Request::Invalid; - } - - let document = match path[args_offset].parse() { - Ok(document) => document, - _ => return Request::Invalid, - }; - let signature = match path[args_offset + 1].parse() { - Ok(signature) => signature, - _ => return Request::Invalid, - }; - - let threshold = path.get(args_offset + 2).map(|v| v.parse()); - let message_hash = path.get(args_offset + 2).map(|v| v.parse()); - let common_point = path.get(args_offset + 2).map(|v| v.parse()); - let encrypted_key = path.get(args_offset + 3).map(|v| v.parse()); - match (prefix, args_count, method, threshold, message_hash, common_point, encrypted_key) { - ("shadow", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) => - Request::GenerateServerKey(document, signature, threshold), - ("shadow", 4, &HttpMethod::POST, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) => - Request::StoreDocumentKey(document, signature, common_point, encrypted_key), - ("", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) => - Request::GenerateDocumentKey(document, signature, threshold), - ("server", 2, &HttpMethod::GET, _, _, _, _) => - Request::GetServerKey(document, signature), - ("", 2, &HttpMethod::GET, _, _, _, _) => - Request::GetDocumentKey(document, signature), - ("shadow", 2, &HttpMethod::GET, _, _, _, _) => - Request::GetDocumentKeyShadow(document, signature), - ("schnorr", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) => - Request::SchnorrSignMessage(document, signature, message_hash), - ("ecdsa", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) => - Request::EcdsaSignMessage(document, signature, message_hash), - _ => Request::Invalid, - } -} - -fn parse_admin_request(method: &HttpMethod, path: Vec, body: &[u8]) -> Request { - let args_count = path.len(); - if *method != HttpMethod::POST || args_count != 4 || path[1] != "servers_set_change" { - return Request::Invalid; - } - - let old_set_signature = match path[2].parse() { - Ok(signature) => signature, - _ => return Request::Invalid, - }; - - let new_set_signature = match path[3].parse() { - Ok(signature) => signature, - _ => return Request::Invalid, - }; - - let new_servers_set: BTreeSet = match serde_json::from_slice(body) { - Ok(new_servers_set) => new_servers_set, - _ => return Request::Invalid, - }; - - Request::ChangeServersSet(old_set_signature, new_set_signature, - new_servers_set.into_iter().map(Into::into).collect()) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::str::FromStr; - use hyper::Method as HttpMethod; - use crypto::publickey::Public; - use traits::KeyServer; - use key_server::tests::DummyKeyServer; - use types::NodeAddress; - use parity_runtime::Runtime; - use ethereum_types::H256; - use super::{parse_request, Request, KeyServerHttpListener}; - - #[test] - fn http_listener_successfully_drops() { - let key_server: Arc = Arc::new(DummyKeyServer::default()); - let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; - let runtime = Runtime::with_thread_count(1); - let listener = KeyServerHttpListener::start(address, None, Arc::downgrade(&key_server), - runtime.executor()).unwrap(); - drop(listener); - } - - #[test] - fn parse_request_successful() { - // POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key - assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()), - Request::GenerateServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - 2)); - // POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} => store encrypted document key - assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()), - Request::StoreDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap(), - "1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".parse().unwrap())); - // POST /{server_key_id}/{signature}/{threshold} => generate server && document key - assert_eq!(parse_request(&HttpMethod::POST, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()), - Request::GenerateDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - 2)); - // GET /server/{server_key_id}/{signature} => get public portion of server key - assert_eq!(parse_request(&HttpMethod::GET, "/server/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /{server_key_id}/{signature} => get document key - assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - assert_eq!(parse_request(&HttpMethod::GET, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /shadow/{server_key_id}/{signature} => get document key shadow - assert_eq!(parse_request(&HttpMethod::GET, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKeyShadow(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /schnorr/{server_key_id}/{signature}/{message_hash} => schnorr-sign message with server key - assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::SchnorrSignMessage(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); - // GET /ecdsa/{server_key_id}/{signature}/{message_hash} => ecdsa-sign message with server key - assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::EcdsaSignMessage(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); - // POST /admin/servers_set_change/{old_set_signature}/{new_set_signature} + body - let node1: Public = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(); - let node2: Public = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap(); - let nodes = vec![node1, node2].into_iter().collect(); - assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", - &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", - "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()), - Request::ChangeServersSet( - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - nodes, - )); - } - - #[test] - fn parse_request_failed() { - assert_eq!(parse_request(&HttpMethod::GET, "", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/shadow", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "///2", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/shadow///2", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/a/b", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/xxx/yyy", - &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", - "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()), - Request::Invalid); - assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", "".as_bytes()), - Request::Invalid); - } -} diff --git a/secret-store/src/listener/mod.rs b/secret-store/src/listener/mod.rs deleted file mode 100644 index 1911a4d59..000000000 --- a/secret-store/src/listener/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod http_listener; -pub mod service_contract; -pub mod service_contract_aggregate; -pub mod service_contract_listener; -mod tasks_queue; - -use std::collections::BTreeSet; -use std::sync::Arc; -use futures::Future; -use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, AdminSessionsServer, KeyServer}; -use types::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId, Requester}; - -/// Available API mask. -#[derive(Debug, Default)] -pub struct ApiMask { - /// Accept server key generation requests. - pub server_key_generation_requests: bool, - /// Accept server key retrieval requests. - pub server_key_retrieval_requests: bool, - /// Accept document key store requests. - pub document_key_store_requests: bool, - /// Accept document key shadow retrieval requests. - pub document_key_shadow_retrieval_requests: bool, -} - -/// Combined HTTP + service contract listener. -pub struct Listener { - key_server: Arc, - _http: Option, - _contract: Option>, -} - -impl ApiMask { - /// Create mask that accepts all requests. - pub fn all() -> Self { - ApiMask { - server_key_generation_requests: true, - server_key_retrieval_requests: true, - document_key_store_requests: true, - document_key_shadow_retrieval_requests: true, - } - } -} - -impl Listener { - /// Create new listener. - pub fn new(key_server: Arc, http: Option, contract: Option>) -> Self { - Self { - key_server: key_server, - _http: http, - _contract: contract, - } - } -} - -impl KeyServer for Listener {} - -impl ServerKeyGenerator for Listener { - fn generate_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - self.key_server.generate_key(key_id, author, threshold) - } - - fn restore_key_public( - &self, - key_id: ServerKeyId, - author: Requester, - ) -> Box + Send> { - self.key_server.restore_key_public(key_id, author) - } -} - -impl DocumentKeyServer for Listener { - fn store_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - common_point: Public, - encrypted_document_key: Public, - ) -> Box + Send> { - self.key_server.store_document_key(key_id, author, common_point, encrypted_document_key) - } - - fn generate_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - self.key_server.generate_document_key(key_id, author, threshold) - } - - fn restore_document_key( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - self.key_server.restore_document_key(key_id, requester) - } - - fn restore_document_key_shadow( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - self.key_server.restore_document_key_shadow(key_id, requester) - } -} - -impl MessageSigner for Listener { - fn sign_message_schnorr( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - self.key_server.sign_message_schnorr(key_id, requester, message) - } - - fn sign_message_ecdsa( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - self.key_server.sign_message_ecdsa(key_id, requester, message) - } -} - -impl AdminSessionsServer for Listener { - fn change_servers_set( - &self, - old_set_signature: RequestSignature, - new_set_signature: RequestSignature, - new_servers_set: BTreeSet, - ) -> Box + Send> { - self.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set) - } -} diff --git a/secret-store/src/listener/service_contract.rs b/secret-store/src/listener/service_contract.rs deleted file mode 100644 index 6ebe1796a..000000000 --- a/secret-store/src/listener/service_contract.rs +++ /dev/null @@ -1,807 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use parking_lot::RwLock; -use ethabi::RawLog; -use ethabi::FunctionOutputDecoder; -use crypto::publickey::{Public, public_to_address}; -use hash::keccak; -use bytes::Bytes; -use ethereum_types::{H256, U256, Address, H512}; -use listener::ApiMask; -use listener::service_contract_listener::ServiceTask; -use blockchain::{SecretStoreChain, Filter, SigningKeyPair, ContractAddress, BlockId}; -use ServerKeyId; - -use_contract!(service, "res/service.json"); - -/// Name of the general SecretStore contract in the registry. -pub const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service"; -/// Name of the server key generation SecretStore contract in the registry. -pub const SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_gen"; -/// Name of the server key retrieval SecretStore contract in the registry. -pub const SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_retr"; -/// Name of the document key store SecretStore contract in the registry. -pub const DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_store"; -/// Name of the document key retrieval SecretStore contract in the registry. -pub const DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_sretr"; - -/// Server key generation has been requested. -const SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyGenerationRequested(bytes32,address,uint8)"; -/// Server key retrieval has been requested. -const SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRetrievalRequested(bytes32)"; -/// Document key store has been requested. -const DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyStoreRequested(bytes32,address,bytes,bytes)"; -/// Document key common part retrieval has been requested. -const DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyCommonRetrievalRequested(bytes32,address)"; -/// Document key personal part retrieval has been requested. -const DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyPersonalRetrievalRequested(bytes32,bytes)"; - -lazy_static! { - pub static ref SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME); - pub static ref SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME); - pub static ref DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME); - pub static ref DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME); - pub static ref DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME); -} - -/// Service contract trait. -pub trait ServiceContract: Send + Sync { - /// Update contract when new blocks are enacted. Returns true if contract is installed && up-to-date (i.e. chain is synced). - fn update(&self) -> bool; - /// Read recent contract logs. Returns topics of every entry. - fn read_logs(&self) -> Box>; - /// Publish generated key. - fn read_pending_requests(&self) -> Box>; - /// Publish generated server key. - fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String>; - /// Publish server key generation error. - fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish retrieved server key. - fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String>; - /// Publish server key retrieval error. - fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish stored document key. - fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish document key store error. - fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish retrieved document key common. - fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String>; - /// Publish retrieved document key personal. - fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String>; - /// Publish document key store error. - fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String>; -} - -/// On-chain service contract. -pub struct OnChainServiceContract { - /// Requests mask. - mask: ApiMask, - /// Blockchain client. - client: Arc, - /// This node key pair. - self_key_pair: Arc, - /// Contract registry name (if any). - name: String, - /// Contract address source. - address_source: ContractAddress, - /// Contract. - data: RwLock, -} - -/// On-chain service contract data. -struct ServiceData { - /// Current contract address. - pub contract_address: Option
, - /// Last block we have read logs from. - pub last_log_block: Option, -} - -/// Pending requests iterator. -struct PendingRequestsIterator Option<(bool, ServiceTask)>> { - /// Pending request read function. - read_request: F, - /// Current request index. - index: U256, - /// Requests length. - length: U256, -} - -/// Server key generation related functions. -struct ServerKeyGenerationService; -/// Server key retrieval related functions. -struct ServerKeyRetrievalService; -/// Document key store related functions. -struct DocumentKeyStoreService; -/// Document key shadow retrievalrelated functions. -struct DocumentKeyShadowRetrievalService; - -impl OnChainServiceContract { - /// Create new on-chain service contract. - pub fn new(mask: ApiMask, client: Arc, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { - let contract = OnChainServiceContract { - mask: mask, - client: client, - self_key_pair: self_key_pair, - name: name, - address_source: address_source, - data: RwLock::new(ServiceData { - contract_address: None, - last_log_block: None, - }), - }; - - contract.update_contract_address(); - contract - } - - /// Send transaction to the service contract. - fn send_contract_transaction(&self, tx_name: &str, origin: &Address, server_key_id: &ServerKeyId, is_response_required: C, prepare_tx: P) -> Result<(), String> - where C: FnOnce(&dyn SecretStoreChain, &Address, &ServerKeyId, &Address) -> bool, - P: FnOnce(&dyn SecretStoreChain, &Address) -> Result { - // only publish if contract address is set && client is online - if !self.client.is_trusted() { - return Err("trusted client is required to publish key".into()) - } - - // only publish key if contract waits for publication - // failing is ok here - it could be that enough confirmations have been recevied - // or key has been requested using HTTP API - let self_address = public_to_address(self.self_key_pair.public()); - if !is_response_required(&*self.client, origin, server_key_id, &self_address) { - return Ok(()); - } - - // prepare transaction data - let transaction_data = prepare_tx(&*self.client, origin)?; - - // send transaction - self.client.transact_contract( - origin.clone(), - transaction_data - ).map_err(|e| format!("{}", e))?; - - trace!(target: "secretstore", "{}: transaction {} sent to service contract", - self.self_key_pair.public(), tx_name); - - Ok(()) - } - - /// Create task-specific pending requests iterator. - fn create_pending_requests_iterator< - C: 'static + Fn(&dyn SecretStoreChain, &Address, &BlockId) -> Result, - R: 'static + Fn(&dyn SigningKeyPair, &dyn SecretStoreChain, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String> - >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { - get_count(&*client, contract_address, block) - .map(|count| { - let client = client.clone(); - let self_key_pair = self.self_key_pair.clone(); - let contract_address = contract_address.clone(); - let block = block.clone(); - Box::new(PendingRequestsIterator { - read_request: move |index| read_item(&*self_key_pair, &*client, &contract_address, &block, index) - .map_err(|error| { - warn!(target: "secretstore", "{}: reading pending request failed: {}", - self_key_pair.public(), error); - error - }) - .ok(), - index: 0.into(), - length: count, - }) as Box> - }) - .map_err(|error| { - warn!(target: "secretstore", "{}: creating pending requests iterator failed: {}", - self.self_key_pair.public(), error); - error - }) - .ok() - .unwrap_or_else(|| Box::new(::std::iter::empty())) - } - - /// Update service contract address. - fn update_contract_address(&self) -> bool { - let contract_address = self.client.read_contract_address(&self.name, &self.address_source); - let mut data = self.data.write(); - if contract_address != data.contract_address { - trace!(target: "secretstore", "{}: installing {} service contract from address {:?}", - self.self_key_pair.public(), self.name, contract_address); - - data.contract_address = contract_address; - } - - data.contract_address.is_some() - } -} - -impl ServiceContract for OnChainServiceContract { - fn update(&self) -> bool { - self.update_contract_address() && self.client.is_trusted() - } - - fn read_logs(&self) -> Box> { - if !self.client.is_trusted() { - warn!(target: "secretstore", "{}: client is offline during read_logs call", - self.self_key_pair.public()); - return Box::new(::std::iter::empty()); - } - - let address = match self.data.read().contract_address { - Some(address) => address, - None => return Box::new(::std::iter::empty()), // no contract installed - }; - let confirmed_block = match self.client.get_confirmed_block_hash() { - Some(confirmed_block) => confirmed_block, - None => return Box::new(::std::iter::empty()), // no block with enough confirmations - }; - - let request_logs = self.client.retrieve_last_logs(Filter { - from_block: BlockId::Hash(self.data.read().last_log_block.unwrap_or_else(|| confirmed_block)), - address: Some(vec![address]), - topics: vec![Some(mask_topics(&self.mask))], - }).unwrap_or_default(); - - let mut data = self.data.write(); - data.last_log_block = Some(confirmed_block.clone()); - - Box::new(request_logs.into_iter() - .filter_map(|log| { - if log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH { - ServerKeyGenerationService::parse_log(&address, log) - } else if log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - ServerKeyRetrievalService::parse_log(&address, log) - } else if log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { - DocumentKeyStoreService::parse_log(&address, log) - } else if log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_common_request_log(&address, log) - } else if log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, log) - } else { - Err("unknown type of log entry".into()) - } - .map_err(|error| { - warn!(target: "secretstore", "{}: error parsing log entry from service contract: {}", - self.self_key_pair.public(), error); - error - }) - .ok() - }).collect::>().into_iter()) - } - - fn read_pending_requests(&self) -> Box> { - if !self.client.is_trusted() { - return Box::new(::std::iter::empty()) - } - - // we only need requests that are here from the last confirm block - let data = self.data.read(); - match data.contract_address { - None => Box::new(::std::iter::empty()), - Some(contract_address) => self.client.get_confirmed_block_hash() - .map(|b| { - let block = BlockId::Hash(b); - let iter = match self.mask.server_key_generation_requests { - true => Box::new(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &ServerKeyGenerationService::read_pending_requests_count, - &ServerKeyGenerationService::read_pending_request)) as Box>, - false => Box::new(::std::iter::empty()), - }; - let iter = match self.mask.server_key_retrieval_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &ServerKeyRetrievalService::read_pending_requests_count, - &ServerKeyRetrievalService::read_pending_request))), - false => iter, - }; - let iter = match self.mask.document_key_store_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &DocumentKeyStoreService::read_pending_requests_count, - &DocumentKeyStoreService::read_pending_request))), - false => iter, - }; - let iter = match self.mask.document_key_shadow_retrieval_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &DocumentKeyShadowRetrievalService::read_pending_requests_count, - &DocumentKeyShadowRetrievalService::read_pending_request))), - false => iter - }; - - iter - }) - .unwrap_or_else(|| Box::new(::std::iter::empty())) - } - } - - fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { - self.send_contract_transaction("publish_generated_server_key", origin, server_key_id, ServerKeyGenerationService::is_response_required, - |_, _| Ok(ServerKeyGenerationService::prepare_pubish_tx_data(server_key_id, &server_key))) - } - - fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_server_key_generation_error", origin, server_key_id, ServerKeyGenerationService::is_response_required, - |_, _| Ok(ServerKeyGenerationService::prepare_error_tx_data(server_key_id))) - } - - fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { - let threshold = serialize_threshold(threshold)?; - self.send_contract_transaction("publish_retrieved_server_key", origin, server_key_id, ServerKeyRetrievalService::is_response_required, - |_, _| Ok(ServerKeyRetrievalService::prepare_pubish_tx_data(server_key_id, server_key, threshold))) - } - - fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_server_key_retrieval_error", origin, server_key_id, ServerKeyRetrievalService::is_response_required, - |_, _| Ok(ServerKeyRetrievalService::prepare_error_tx_data(server_key_id))) - } - - fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_stored_document_key", origin, server_key_id, DocumentKeyStoreService::is_response_required, - |_, _| Ok(DocumentKeyStoreService::prepare_pubish_tx_data(server_key_id))) - } - - fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_document_key_store_error", origin, server_key_id, DocumentKeyStoreService::is_response_required, - |_, _| Ok(DocumentKeyStoreService::prepare_error_tx_data(server_key_id))) - } - - fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { - let threshold = serialize_threshold(threshold)?; - self.send_contract_transaction("publish_retrieved_document_key_common", origin, server_key_id, - |client, contract_address, server_key_id, key_server| - DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, server_key_id, requester, key_server), - |_, _| - Ok(DocumentKeyShadowRetrievalService::prepare_pubish_common_tx_data(server_key_id, requester, common_point, threshold)) - ) - } - - fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { - self.send_contract_transaction("publish_retrieved_document_key_personal", origin, server_key_id, |_, _, _, _| true, - move |client, address| - DocumentKeyShadowRetrievalService::prepare_pubish_personal_tx_data(client, address, server_key_id, requester, participants, decrypted_secret, shadow) - ) - } - - fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - self.send_contract_transaction("publish_document_key_retrieval_error", origin, server_key_id, - |client, contract_address, server_key_id, key_server| - DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, server_key_id, requester, key_server), - |_, _| - Ok(DocumentKeyShadowRetrievalService::prepare_error_tx_data(server_key_id, requester)) - ) - } -} - -impl Iterator for PendingRequestsIterator where F: Fn(U256) -> Option<(bool, ServiceTask)> { - type Item = (bool, ServiceTask); - - fn next(&mut self) -> Option<(bool, ServiceTask)> { - if self.index >= self.length { - return None; - } - - let index = self.index.clone(); - self.index = self.index + 1; - - (self.read_request)(index) - } -} - -/// Returns vector of logs topics to listen to. -pub fn mask_topics(mask: &ApiMask) -> Vec { - let mut topics = Vec::new(); - if mask.server_key_generation_requests { - topics.push(*SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH); - } - if mask.server_key_retrieval_requests { - topics.push(*SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); - } - if mask.document_key_store_requests { - topics.push(*DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH); - } - if mask.document_key_shadow_retrieval_requests { - topics.push(*DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); - topics.push(*DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); - } - topics -} - -impl ServerKeyGenerationService { - /// Parse request log entry. - pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::server_key_generation_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::GenerateServerKey(origin.clone(), l.server_key_id, l.author, parse_threshold(l.threshold)?)), - Err(e) => Err(format!("{}", e)), - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(*server_key_id, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish key transaction data. - pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes { - service::functions::server_key_generated::encode_input(*server_key_id, server_key_public.as_bytes().to_vec()) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::server_key_generation_error::encode_input(*server_key_id) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::server_key_generation_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - - let (encoded, decoder) = service::functions::get_server_key_generation_request::call(index); - let (server_key_id, author, threshold) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - let threshold = parse_threshold(threshold)?; - - let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(server_key_id, self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = ServiceTask::GenerateServerKey( - contract_address.clone(), - server_key_id, - author, - threshold, - ); - - Ok((not_confirmed, task)) - } -} - -impl ServerKeyRetrievalService { - /// Parse request log entry. - pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::server_key_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveServerKey(*origin, l.server_key_id)), - Err(e) => Err(e.to_string()) - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(*server_key_id, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish key transaction data. - pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes { - service::functions::server_key_retrieved::encode_input(*server_key_id, server_key_public.as_bytes().to_vec(), threshold) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::server_key_retrieval_error::encode_input(*server_key_id) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::server_key_retrieval_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - - let (encoded, decoder) = service::functions::get_server_key_retrieval_request::call(index); - let server_key_id = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(server_key_id, self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = ServiceTask::RetrieveServerKey( - *contract_address, - server_key_id, - ); - - Ok((not_confirmed, task)) - } -} - -impl DocumentKeyStoreService { - /// Parse request log entry. - pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::document_key_store_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::StoreDocumentKey( - origin.clone(), - l.server_key_id, - l.author, - H512::from_slice(&*l.common_point), - H512::from_slice(&*l.encrypted_point), - )), - Err(e) => Err(format!("{}", e)), - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(*server_key_id, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish key transaction data. - pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::document_key_stored::encode_input(*server_key_id) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::document_key_store_error::encode_input(*server_key_id) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::document_key_store_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - let (encoded, decoder) = service::functions::get_document_key_store_request::call(index); - let (server_key_id, author, common_point, encrypted_point) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(server_key_id, self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = ServiceTask::StoreDocumentKey( - *contract_address, - server_key_id, - author, - Public::from_slice(&common_point), - Public::from_slice(&encrypted_point), - ); - - Ok((not_confirmed, task)) - } -} - -impl DocumentKeyShadowRetrievalService { - /// Parse common request log entry. - pub fn parse_common_request_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::document_key_common_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyCommon(origin.clone(), l.server_key_id, l.requester)), - Err(e) => Err(e.to_string()) - } - } - - /// Parse personal request log entry. - pub fn parse_personal_request_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::document_key_personal_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, H512::from_slice(&*l.requester_public))), - Err(e) => Err(e.to_string()) - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(*server_key_id, *requester, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish common key transaction data. - pub fn prepare_pubish_common_tx_data(server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes { - service::functions::document_key_common_retrieved::encode_input(*server_key_id, *requester, common_point.as_bytes().to_vec(), threshold) - } - - /// Prepare publish personal key transaction data. - pub fn prepare_pubish_personal_tx_data(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result { - let mut participants_mask = U256::default(); - for participant in participants { - let participant_index = Self::map_key_server_address(client, contract_address, participant.clone()) - .map_err(|e| format!("Error searching for {} participant: {}", participant, e))?; - participants_mask = participants_mask | (U256::one() << participant_index); - } - Ok(service::functions::document_key_personal_retrieved::encode_input( - *server_key_id, *requester, participants_mask, decrypted_secret.as_bytes().to_vec(), shadow - )) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId, requester: &Address) -> Bytes { - service::functions::document_key_shadow_retrieval_error::encode_input(*server_key_id, *requester) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::document_key_shadow_retrieval_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - - let (encoded, decoder) = service::functions::get_document_key_shadow_retrieval_request::call(index); - let (server_key_id, requester, is_common_retrieval_completed) = - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let requester = Public::from_slice(&requester); - let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(server_key_id, public_to_address(&requester), self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = match is_common_retrieval_completed { - true => ServiceTask::RetrieveShadowDocumentKeyPersonal( - *contract_address, - server_key_id, - requester, - ), - false => ServiceTask::RetrieveShadowDocumentKeyCommon( - *contract_address, - server_key_id, - public_to_address(&requester), - ), - }; - - Ok((not_confirmed, task)) - } - - /// Map from key server address to key server index. - fn map_key_server_address(client: &dyn SecretStoreChain, contract_address: &Address, key_server: Address) -> Result { - // we're checking confirmation in Latest block, because tx ,ust be appended to the latest state - let (encoded, decoder) = service::functions::require_key_server::call(key_server); - let index = decoder.decode(&client.call_contract(BlockId::Latest, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - if index > u8::max_value().into() { - Err(format!("key server index is too big: {}", index)) - } else { - let index: u32 = index.low_u32(); - Ok(index as u8) - } - } -} - -/// Parse threshold (we only supposrt 256 KS at max). -fn parse_threshold(threshold: U256) -> Result { - let threshold_num = threshold.low_u64(); - if threshold != threshold_num.into() || threshold_num >= ::std::u8::MAX as u64 { - return Err(format!("invalid threshold to use in service contract: {}", threshold)); - } - - Ok(threshold_num as usize) -} - -/// Serialize threshold (we only support 256 KS at max). -fn serialize_threshold(threshold: usize) -> Result { - if threshold > ::std::u8::MAX as usize { - return Err(format!("invalid threshold to use in service contract: {}", threshold)); - } - Ok(threshold.into()) -} - -#[cfg(test)] -pub mod tests { - use parking_lot::Mutex; - use bytes::Bytes; - use crypto::publickey::Public; - use ethereum_types::Address; - use listener::service_contract_listener::ServiceTask; - use {ServerKeyId}; - use super::ServiceContract; - - #[derive(Default)] - pub struct DummyServiceContract { - pub is_actual: bool, - pub logs: Vec, - pub pending_requests: Vec<(bool, ServiceTask)>, - pub generated_server_keys: Mutex>, - pub server_keys_generation_failures: Mutex>, - pub retrieved_server_keys: Mutex>, - pub server_keys_retrieval_failures: Mutex>, - pub stored_document_keys: Mutex>, - pub document_keys_store_failures: Mutex>, - pub common_shadow_retrieved_document_keys: Mutex>, - pub personal_shadow_retrieved_document_keys: Mutex, Public, Bytes)>>, - pub document_keys_shadow_retrieval_failures: Mutex>, - } - - impl ServiceContract for DummyServiceContract { - fn update(&self) -> bool { - true - } - - fn read_logs(&self) -> Box> { - Box::new(self.logs.clone().into_iter()) - } - - fn read_pending_requests(&self) -> Box> { - Box::new(self.pending_requests.clone().into_iter()) - } - - fn publish_generated_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { - self.generated_server_keys.lock().push((server_key_id.clone(), server_key.clone())); - Ok(()) - } - - fn publish_server_key_generation_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.server_keys_generation_failures.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_retrieved_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { - self.retrieved_server_keys.lock().push((server_key_id.clone(), server_key.clone(), threshold)); - Ok(()) - } - - fn publish_server_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.server_keys_retrieval_failures.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_stored_document_key(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.stored_document_keys.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_document_key_store_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.document_keys_store_failures.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_retrieved_document_key_common(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { - self.common_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), common_point.clone(), threshold)); - Ok(()) - } - - fn publish_retrieved_document_key_personal(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { - self.personal_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), participants.iter().cloned().collect(), decrypted_secret, shadow)); - Ok(()) - } - - fn publish_document_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - self.document_keys_shadow_retrieval_failures.lock().push((server_key_id.clone(), requester.clone())); - Ok(()) - } - } -} diff --git a/secret-store/src/listener/service_contract_aggregate.rs b/secret-store/src/listener/service_contract_aggregate.rs deleted file mode 100644 index 9b2fb0e54..000000000 --- a/secret-store/src/listener/service_contract_aggregate.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use bytes::Bytes; -use ethereum_types::Address; -use crypto::publickey::Public; -use listener::service_contract::ServiceContract; -use listener::service_contract_listener::ServiceTask; -use {ServerKeyId}; - -/// Aggregated on-chain service contract. -pub struct OnChainServiceContractAggregate { - /// All hosted service contracts. - contracts: Vec>, -} - -impl OnChainServiceContractAggregate { - /// Create new aggregated service contract listener. - pub fn new(contracts: Vec>) -> Self { - debug_assert!(contracts.len() > 1); - OnChainServiceContractAggregate { - contracts: contracts, - } - } -} - -impl ServiceContract for OnChainServiceContractAggregate { - fn update(&self) -> bool { - let mut result = false; - for contract in &self.contracts { - result = contract.update() || result; - } - result - } - - fn read_logs(&self) -> Box> { - self.contracts.iter() - .fold(Box::new(::std::iter::empty()) as Box>, |i, c| - Box::new(i.chain(c.read_logs()))) - } - - fn read_pending_requests(&self) -> Box> { - self.contracts.iter() - .fold(Box::new(::std::iter::empty()) as Box>, |i, c| - Box::new(i.chain(c.read_pending_requests()))) - } - - // in current implementation all publish methods are independent of actual contract adddress - // (tx is sent to origin) => we do not care which contract to use for publish data in methods below - - fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { - self.contracts[0].publish_generated_server_key(origin, server_key_id, server_key) - } - - fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_server_key_generation_error(origin, server_key_id) - } - - fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { - self.contracts[0].publish_retrieved_server_key(origin, server_key_id, server_key, threshold) - } - - fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_server_key_retrieval_error(origin, server_key_id) - } - - fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_stored_document_key(origin, server_key_id) - } - - fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_document_key_store_error(origin, server_key_id) - } - - fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { - self.contracts[0].publish_retrieved_document_key_common(origin, server_key_id, requester, common_point, threshold) - } - - fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { - self.contracts[0].publish_retrieved_document_key_personal(origin, server_key_id, requester, participants, decrypted_secret, shadow) - } - - fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - self.contracts[0].publish_document_key_retrieval_error(origin, server_key_id, requester) - } -} diff --git a/secret-store/src/listener/service_contract_listener.rs b/secret-store/src/listener/service_contract_listener.rs deleted file mode 100644 index c3de0cb03..000000000 --- a/secret-store/src/listener/service_contract_listener.rs +++ /dev/null @@ -1,1054 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::HashSet; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::thread; -use bytes::Bytes; -use crypto::publickey::{Public, public_to_address}; -use ethereum_types::{H256, U256, Address, BigEndianHash as _}; -use key_server_set::KeyServerSet; -use key_server_cluster::{NodeId, ClusterClient, ClusterSessionsListener, ClusterSession}; -use key_server_cluster::math; -use key_server_cluster::generation_session::SessionImpl as GenerationSession; -use key_server_cluster::encryption_session::{check_encrypted_data, update_encrypted_data}; -use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationTransport, FailedContinueAction}; -use key_storage::KeyStorage; -use parking_lot::Mutex; -use acl_storage::AclStorage; -use listener::service_contract::ServiceContract; -use listener::tasks_queue::TasksQueue; -use {ServerKeyId, Error}; -use blockchain::{NewBlocksNotify, SigningKeyPair}; - -/// Retry interval (in blocks). Every RETRY_INTERVAL_BLOCKS blocks each KeyServer reads pending requests from -/// service contract && tries to re-execute. The reason to have this mechanism is primarily because keys -/// servers set change takes a lot of time + there could be some races, when blocks are coming to different -/// KS at different times. This isn't intended to fix && respond to general session errors! -const RETRY_INTERVAL_BLOCKS: usize = 30; - -/// Max failed retry requests (in single retry interval). The reason behind this constant is that if several -/// pending requests have failed, then most probably other will fail too. -const MAX_FAILED_RETRY_REQUESTS: usize = 1; - -/// SecretStore <-> Authority connector responsible for: -/// 1. listening for new requests on SecretStore contract -/// 2. redirecting requests to key server -/// 3. publishing response on SecretStore contract -pub struct ServiceContractListener { - /// Service contract listener data. - data: Arc, - /// Service thread handle. - service_handle: Option>, -} - -/// Service contract listener parameters. -pub struct ServiceContractListenerParams { - /// Service contract. - pub contract: Arc, - /// This node key pair. - pub self_key_pair: Arc, - /// Key servers set. - pub key_server_set: Arc, - /// ACL storage reference. - pub acl_storage: Arc, - /// Cluster reference. - pub cluster: Arc, - /// Key storage reference. - pub key_storage: Arc, -} - -/// Service contract listener data. -struct ServiceContractListenerData { - /// Blocks since last retry. - pub last_retry: AtomicUsize, - /// Retry-related data. - pub retry_data: Mutex, - /// Service tasks queue. - pub tasks_queue: Arc>, - /// Service contract. - pub contract: Arc, - /// ACL storage reference. - pub acl_storage: Arc, - /// Cluster client reference. - pub cluster: Arc, - /// This node key pair. - pub self_key_pair: Arc, - /// Key servers set. - pub key_server_set: Arc, - /// Key storage reference. - pub key_storage: Arc, - -} - -/// Retry-related data. -#[derive(Default)] -struct ServiceContractRetryData { - /// Server keys, which we have 'touched' since last retry. - pub affected_server_keys: HashSet, - /// Document keys + requesters, which we have 'touched' since last retry. - pub affected_document_keys: HashSet<(ServerKeyId, Address)>, -} - -/// Service task. -#[derive(Debug, Clone, PartialEq)] -pub enum ServiceTask { - /// Retry all 'stalled' tasks. - Retry, - /// Generate server key (origin, server_key_id, author, threshold). - GenerateServerKey(Address, ServerKeyId, Address, usize), - /// Retrieve server key (origin, server_key_id). - RetrieveServerKey(Address, ServerKeyId), - /// Store document key (origin, server_key_id, author, common_point, encrypted_point). - StoreDocumentKey(Address, ServerKeyId, Address, Public, Public), - /// Retrieve common data of document key (origin, server_key_id, requester). - RetrieveShadowDocumentKeyCommon(Address, ServerKeyId, Address), - /// Retrieve personal data of document key (origin, server_key_id, requester). - RetrieveShadowDocumentKeyPersonal(Address, ServerKeyId, Public), - /// Shutdown listener. - Shutdown, -} - -impl ServiceContractListener { - /// Create new service contract listener. - pub fn new(params: ServiceContractListenerParams) -> Result, Error> { - let data = Arc::new(ServiceContractListenerData { - last_retry: AtomicUsize::new(0), - retry_data: Default::default(), - tasks_queue: Arc::new(TasksQueue::new()), - contract: params.contract, - acl_storage: params.acl_storage, - cluster: params.cluster, - self_key_pair: params.self_key_pair, - key_server_set: params.key_server_set, - key_storage: params.key_storage, - }); - - // we are not starting thread when in test mode - let service_handle = if cfg!(test) { - None - } else { - let service_thread_data = data.clone(); - Some(thread::Builder::new().name("ServiceContractListener".into()).spawn(move || - Self::run_service_thread(service_thread_data)).map_err(|e| Error::Internal(format!("{}", e)))?) - }; - let contract = Arc::new(ServiceContractListener { - data: data, - service_handle: service_handle, - }); - contract.data.cluster.add_generation_listener(contract.clone()); - contract.data.cluster.add_decryption_listener(contract.clone()); - contract.data.cluster.add_key_version_negotiation_listener(contract.clone()); - Ok(contract) - } - - /// Process incoming events of service contract. - fn process_service_contract_events(&self) { - // shortcut: do not process events if we're isolated from the cluster - if self.data.key_server_set.is_isolated() { - return; - } - - self.data.tasks_queue.push_many(self.data.contract.read_logs() - .filter_map(|task| Self::filter_task(&self.data, task))); - } - - /// Filter service task. Only returns Some if task must be executed by this server. - fn filter_task(data: &Arc, task: ServiceTask) -> Option { - match task { - // when this node should be master of this server key generation session - ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold) if is_processed_by_this_key_server( - &*data.key_server_set, data.self_key_pair.public(), &server_key_id) => - Some(ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold)), - // when server key is not yet generated and generation must be initiated by other node - ServiceTask::GenerateServerKey(_, _, _, _) => None, - - // when server key retrieval is requested - ServiceTask::RetrieveServerKey(origin, server_key_id) => - Some(ServiceTask::RetrieveServerKey(origin, server_key_id)), - - // when document key store is requested - ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point) => - Some(ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point)), - - // when common document key data retrieval is requested - ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester) => - Some(ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester)), - - // when this node should be master of this document key decryption session - ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester) if is_processed_by_this_key_server( - &*data.key_server_set, data.self_key_pair.public(), &server_key_id) => - Some(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester)), - // when server key is not yet generated and generation must be initiated by other node - ServiceTask::RetrieveShadowDocumentKeyPersonal(_, _, _) => None, - - ServiceTask::Retry | ServiceTask::Shutdown => unreachable!("must be filtered outside"), - } - } - - /// Service thread procedure. - fn run_service_thread(data: Arc) { - loop { - let task = data.tasks_queue.wait(); - trace!(target: "secretstore", "{}: processing {:?} task", data.self_key_pair.public(), task); - - match task { - ServiceTask::Shutdown => break, - task => { - // the only possible reaction to an error is a tx+trace && it is already happened - let _ = Self::process_service_task(&data, task); - }, - }; - } - - trace!(target: "secretstore", "{}: ServiceContractListener thread stopped", data.self_key_pair.public()); - } - - /// Process single service task. - fn process_service_task(data: &Arc, task: ServiceTask) -> Result<(), String> { - match &task { - &ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold) => { - data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); - log_service_task_result(&task, data.self_key_pair.public(), - Self::generate_server_key(&data, origin, &server_key_id, author, threshold)) - }, - &ServiceTask::RetrieveServerKey(origin, server_key_id) => { - data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); - log_service_task_result(&task, data.self_key_pair.public(), - Self::retrieve_server_key(&data, origin, &server_key_id)) - }, - &ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point) => { - data.retry_data.lock().affected_document_keys.insert((server_key_id.clone(), author.clone())); - log_service_task_result(&task, data.self_key_pair.public(), - Self::store_document_key(&data, origin, &server_key_id, &author, &common_point, &encrypted_point)) - }, - &ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester) => { - data.retry_data.lock().affected_document_keys.insert((server_key_id.clone(), requester.clone())); - log_service_task_result(&task, data.self_key_pair.public(), - Self::retrieve_document_key_common(&data, origin, &server_key_id, &requester)) - }, - &ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester) => { - data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); - log_service_task_result(&task, data.self_key_pair.public(), - Self::retrieve_document_key_personal(&data, origin, &server_key_id, requester)) - }, - &ServiceTask::Retry => { - Self::retry_pending_requests(&data) - .map(|processed_requests| { - if processed_requests != 0 { - trace!(target: "secretstore", "{}: successfully retried {} pending requests", - data.self_key_pair.public(), processed_requests); - } - () - }) - .map_err(|error| { - warn!(target: "secretstore", "{}: retrying pending requests has failed with: {}", - data.self_key_pair.public(), error); - error - }) - }, - &ServiceTask::Shutdown => unreachable!("must be filtered outside"), - } - } - - /// Retry processing pending requests. - fn retry_pending_requests(data: &Arc) -> Result { - let mut failed_requests = 0; - let mut processed_requests = 0; - let retry_data = ::std::mem::replace(&mut *data.retry_data.lock(), Default::default()); - let pending_tasks = data.contract.read_pending_requests() - .filter_map(|(is_confirmed, task)| Self::filter_task(data, task) - .map(|t| (is_confirmed, t))); - for (is_response_required, task) in pending_tasks { - // only process requests, which we haven't confirmed yet - if !is_response_required { - continue; - } - - match task { - ServiceTask::GenerateServerKey(_, ref key, _, _) | ServiceTask::RetrieveServerKey(_, ref key) - if retry_data.affected_server_keys.contains(key) => continue, - ServiceTask::StoreDocumentKey(_, ref key, ref author, _, _) | - ServiceTask::RetrieveShadowDocumentKeyCommon(_, ref key, ref author) - if retry_data.affected_document_keys.contains(&(key.clone(), author.clone())) => continue, - ServiceTask::RetrieveShadowDocumentKeyPersonal(_, ref key, ref requester) - if retry_data.affected_document_keys.contains(&(key.clone(), public_to_address(requester))) => continue, - _ => (), - } - - // process request result - let request_result = Self::process_service_task(data, task); - match request_result { - Ok(_) => processed_requests += 1, - Err(_) => { - failed_requests += 1; - if failed_requests > MAX_FAILED_RETRY_REQUESTS { - return Err("too many failed requests".into()); - } - }, - } - } - - Ok(processed_requests) - } - - /// Generate server key (start generation session). - fn generate_server_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId, author: Address, threshold: usize) -> Result<(), String> { - Self::process_server_key_generation_result(data, origin, server_key_id, data.cluster.new_generation_session( - server_key_id.clone(), Some(origin), author, threshold).map(|_| None).map_err(Into::into)) - } - - /// Process server key generation result. - fn process_server_key_generation_result(data: &Arc, origin: Address, server_key_id: &ServerKeyId, result: Result, Error>) -> Result<(), String> { - match result { - Ok(None) => Ok(()), - Ok(Some(server_key)) => { - data.contract.publish_generated_server_key(&origin, server_key_id, server_key) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_server_key_generation_error(&origin, server_key_id) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish GenerateServerKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - } - } - } - - /// Retrieve server key. - fn retrieve_server_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId) -> Result<(), String> { - match data.key_storage.get(server_key_id) { - Ok(Some(server_key_share)) => { - data.contract.publish_retrieved_server_key(&origin, server_key_id, server_key_share.public, server_key_share.threshold) - }, - Ok(None) => { - data.contract.publish_server_key_retrieval_error(&origin, server_key_id) - } - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_server_key_retrieval_error(&origin, server_key_id) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveServerKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - } - } - } - - /// Store document key. - fn store_document_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId, author: &Address, common_point: &Public, encrypted_point: &Public) -> Result<(), String> { - let store_result = data.key_storage.get(server_key_id) - .and_then(|key_share| key_share.ok_or(Error::ServerKeyIsNotFound)) - .and_then(|key_share| check_encrypted_data(Some(&key_share)).map(|_| key_share).map_err(Into::into)) - .and_then(|key_share| update_encrypted_data(&data.key_storage, server_key_id.clone(), key_share, - author.clone(), common_point.clone(), encrypted_point.clone()).map_err(Into::into)); - match store_result { - Ok(()) => { - data.contract.publish_stored_document_key(&origin, server_key_id) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_document_key_store_error(&origin, server_key_id) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish StoreDocumentKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - }, - } - } - - /// Retrieve common part of document key. - fn retrieve_document_key_common(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - let retrieval_result = data.acl_storage.check(requester.clone(), server_key_id) - .and_then(|is_allowed| if !is_allowed { Err(Error::AccessDenied) } else { Ok(()) }) - .and_then(|_| data.key_storage.get(server_key_id).and_then(|key_share| key_share.ok_or(Error::ServerKeyIsNotFound))) - .and_then(|key_share| key_share.common_point - .ok_or(Error::DocumentKeyIsNotFound) - .and_then(|common_point| math::make_common_shadow_point(key_share.threshold, common_point)) - .map(|common_point| (common_point, key_share.threshold))); - match retrieval_result { - Ok((common_point, threshold)) => { - data.contract.publish_retrieved_document_key_common(&origin, server_key_id, requester, common_point, threshold) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_document_key_retrieval_error(&origin, server_key_id, requester) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveDocumentKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - }, - } - } - - /// Retrieve personal part of document key (start decryption session). - fn retrieve_document_key_personal(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: Public) -> Result<(), String> { - Self::process_document_key_retrieval_result(data, origin, server_key_id, &public_to_address(&requester), data.cluster.new_decryption_session( - server_key_id.clone(), Some(origin), requester.clone().into(), None, true, true).map(|_| None).map_err(Into::into)) - } - - /// Process document key retrieval result. - fn process_document_key_retrieval_result(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: &Address, result: Result, Public, Bytes)>, Error>) -> Result<(), String> { - match result { - Ok(None) => Ok(()), - Ok(Some((participants, decrypted_secret, shadow))) => { - data.contract.publish_retrieved_document_key_personal(&origin, server_key_id, &requester, &participants, decrypted_secret, shadow) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_document_key_retrieval_error(&origin, server_key_id, &requester) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveDocumentKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - } - } - } -} - -impl Drop for ServiceContractListener { - fn drop(&mut self) { - if let Some(service_handle) = self.service_handle.take() { - self.data.tasks_queue.push_front(ServiceTask::Shutdown); - // ignore error as we are already closing - let _ = service_handle.join(); - } - } -} - -impl NewBlocksNotify for ServiceContractListener { - fn new_blocks(&self, new_enacted_len: usize) { - if !self.data.contract.update() { - return; - } - - self.process_service_contract_events(); - - // schedule retry if received enough blocks since last retry - // it maybe inaccurate when switching syncing/synced states, but that's ok - if self.data.last_retry.fetch_add(new_enacted_len, Ordering::Relaxed) >= RETRY_INTERVAL_BLOCKS { - // shortcut: do not retry if we're isolated from the cluster - if !self.data.key_server_set.is_isolated() { - self.data.tasks_queue.push(ServiceTask::Retry); - self.data.last_retry.store(0, Ordering::Relaxed); - } - } - } -} - -impl ClusterSessionsListener for ServiceContractListener { - fn on_session_removed(&self, session: Arc) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // ignore result - the only thing that we can do is to log the error - let server_key_id = session.id(); - if let Some(origin) = session.origin() { - if let Some(generation_result) = session.result() { - let generation_result = generation_result.map(Some).map_err(Into::into); - let _ = Self::process_server_key_generation_result(&self.data, origin, &server_key_id, generation_result); - } - } - } -} - -impl ClusterSessionsListener for ServiceContractListener { - fn on_session_removed(&self, session: Arc) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // ignore result - the only thing that we can do is to log the error - let session_id = session.id(); - let server_key_id = session_id.id; - if let (Some(requester), Some(origin)) = (session.requester().and_then(|r| r.address(&server_key_id).ok()), session.origin()) { - if let Some(retrieval_result) = session.result() { - let retrieval_result = retrieval_result.map(|key_shadow| - session.broadcast_shadows() - .and_then(|broadcast_shadows| - broadcast_shadows.get(self.data.self_key_pair.public()) - .map(|self_shadow| ( - broadcast_shadows.keys().map(public_to_address).collect(), - key_shadow.decrypted_secret, - self_shadow.clone() - ))) - ).map_err(Into::into); - let _ = Self::process_document_key_retrieval_result(&self.data, origin, &server_key_id, &requester, retrieval_result); - } - } - } -} - -impl ClusterSessionsListener> for ServiceContractListener { - fn on_session_removed(&self, session: Arc>) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // we're interested in: - // 1) sessions failed with fatal error - // 2) with decryption continue action - let error = match session.result() { - Some(Err(ref error)) if !error.is_non_fatal() => error.clone(), - _ => return, - }; - - let (origin, requester) = match session.take_failed_continue_action() { - Some(FailedContinueAction::Decrypt(Some(origin), requester)) => (origin, requester), - _ => return, - }; - - // check if master node is responsible for processing key requests - let meta = session.meta(); - if !is_processed_by_this_key_server(&*self.data.key_server_set, &meta.master_node_id, &meta.id) { - return; - } - - // ignore result as we're already processing an error - let _ = Self::process_document_key_retrieval_result(&self.data, origin, &meta.id, &requester, Err(error)); - } -} - -impl ::std::fmt::Display for ServiceTask { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - ServiceTask::Retry => write!(f, "Retry"), - ServiceTask::GenerateServerKey(_, ref server_key_id, ref author, ref threshold) => - write!(f, "GenerateServerKey({}, {}, {})", server_key_id, author, threshold), - ServiceTask::RetrieveServerKey(_, ref server_key_id) => - write!(f, "RetrieveServerKey({})", server_key_id), - ServiceTask::StoreDocumentKey(_, ref server_key_id, ref author, _, _) => - write!(f, "StoreDocumentKey({}, {})", server_key_id, author), - ServiceTask::RetrieveShadowDocumentKeyCommon(_, ref server_key_id, ref requester) => - write!(f, "RetrieveShadowDocumentKeyCommon({}, {})", server_key_id, requester), - ServiceTask::RetrieveShadowDocumentKeyPersonal(_, ref server_key_id, ref requester) => - write!(f, "RetrieveShadowDocumentKeyPersonal({}, {})", server_key_id, public_to_address(requester)), - ServiceTask::Shutdown => write!(f, "Shutdown"), - } - } -} - -/// Log service task result. -fn log_service_task_result(task: &ServiceTask, self_id: &Public, result: Result<(), String>) -> Result<(), String> { - match result { - Ok(_) => trace!(target: "secretstore", "{}: processed {} request", self_id, task), - Err(ref error) => warn!(target: "secretstore", "{}: failed to process {} request with: {}", self_id, task, error), - } - - result -} - -/// Returns true when session, related to `server_key_id` must be started on `node`. -fn is_processed_by_this_key_server(key_server_set: &dyn KeyServerSet, node: &NodeId, server_key_id: &H256) -> bool { - let servers = key_server_set.snapshot().current_set; - let total_servers_count = servers.len(); - match total_servers_count { - 0 => return false, - 1 => return true, - _ => (), - } - - let this_server_index = match servers.keys().enumerate().find(|&(_, s)| s == node) { - Some((index, _)) => index, - None => return false, - }; - - let server_key_id_value: U256 = server_key_id.into_uint(); - let range_interval = U256::max_value() / total_servers_count; - let range_begin = (range_interval + 1) * this_server_index as u32; - let range_end = range_begin.saturating_add(range_interval); - - server_key_id_value >= range_begin && server_key_id_value <= range_end -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::sync::atomic::Ordering; - use crypto::publickey::{Random, Generator, KeyPair}; - use listener::service_contract::ServiceContract; - use listener::service_contract::tests::DummyServiceContract; - use key_server_cluster::DummyClusterClient; - use acl_storage::{AclStorage, DummyAclStorage}; - use key_storage::{KeyStorage, DocumentKeyShare}; - use key_storage::tests::DummyKeyStorage; - use key_server_set::KeyServerSet; - use key_server_set::tests::MapKeyServerSet; - use blockchain::SigningKeyPair; - use {PlainNodeKeyPair, ServerKeyId}; - use super::{ServiceTask, ServiceContractListener, ServiceContractListenerParams, is_processed_by_this_key_server}; - use ethereum_types::Address; - - fn create_non_empty_key_storage(has_doc_key: bool) -> Arc { - let key_storage = Arc::new(DummyKeyStorage::default()); - let mut key_share = DocumentKeyShare::default(); - key_share.public = KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001" - .parse().unwrap()).unwrap().public().clone(); - if has_doc_key { - key_share.common_point = Some(Default::default()); - key_share.encrypted_point = Some(Default::default()); - } - key_storage.insert(Default::default(), key_share.clone()).unwrap(); - key_storage - } - - fn make_servers_set(is_isolated: bool) -> Arc { - Arc::new(MapKeyServerSet::new(is_isolated, vec![ - ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect())) - } - - fn make_service_contract_listener(contract: Option>, cluster: Option>, key_storage: Option>, acl_storage: Option>, servers_set: Option>) -> Arc { - let contract = contract.unwrap_or_else(|| Arc::new(DummyServiceContract::default())); - let cluster = cluster.unwrap_or_else(|| Arc::new(DummyClusterClient::default())); - let key_storage = key_storage.unwrap_or_else(|| Arc::new(DummyKeyStorage::default())); - let acl_storage = acl_storage.unwrap_or_else(|| Arc::new(DummyAclStorage::default())); - let servers_set = servers_set.unwrap_or_else(|| make_servers_set(false)); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap())); - ServiceContractListener::new(ServiceContractListenerParams { - contract: contract, - self_key_pair: self_key_pair, - key_server_set: servers_set, - acl_storage: acl_storage, - cluster: cluster, - key_storage: key_storage, - }).unwrap() - } - - #[test] - fn is_not_processed_by_this_key_server_with_zero_servers() { - assert_eq!(is_processed_by_this_key_server( - &MapKeyServerSet::default(), - Random.generate().public(), - &Default::default()), false); - } - - #[test] - fn is_processed_by_this_key_server_with_single_server() { - let self_key_pair = Random.generate(); - assert_eq!(is_processed_by_this_key_server( - &MapKeyServerSet::new(false, vec![ - (self_key_pair.public().clone(), "127.0.0.1:8080".parse().unwrap()) - ].into_iter().collect()), - self_key_pair.public(), - &Default::default()), true); - } - - #[test] - fn is_not_processed_by_this_key_server_when_not_a_part_of_servers_set() { - assert!(is_processed_by_this_key_server( - &MapKeyServerSet::new(false, vec![ - (Random.generate().public().clone(), "127.0.0.1:8080".parse().unwrap()) - ].into_iter().collect()), - Random.generate().public(), - &Default::default())); - } - - #[test] - fn is_processed_by_this_key_server_in_set_of_3() { - // servers set is ordered && server range depends on index of this server - let servers_set = MapKeyServerSet::new(false, vec![ - // secret: 0000000000000000000000000000000000000000000000000000000000000001 - ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000002 - ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000003 - ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect()); - - // 1st server: process hashes [0x0; 0x555...555] - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"3000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555556".parse().unwrap()), false); - - // 2nd server: process hashes from 0x555...556 to 0xaaa...aab - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555556".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"7555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), false); - - // 3rd server: process hashes from 0x800...000 to 0xbff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000003".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - } - - #[test] - fn is_processed_by_this_key_server_in_set_of_4() { - // servers set is ordered && server range depends on index of this server - let servers_set = MapKeyServerSet::new(false, vec![ - // secret: 0000000000000000000000000000000000000000000000000000000000000001 - ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000002 - ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000004 - ("e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1351ed993ea0d455b75642e2098ea51448d967ae33bfbdfe40cfe97bdc47739922".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000003 - ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect()); - - // 1st server: process hashes [0x0; 0x3ff...ff] - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"2000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"4000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); - - // 2nd server: process hashes from 0x400...000 to 0x7ff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"4000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"6000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"8000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); - - // 3rd server: process hashes from 0x800...000 to 0xbff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000004".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"8000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"a000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"c000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); - - // 4th server: process hashes from 0xc00...000 to 0xfff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000003".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"c000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"e000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - } - - #[test] - fn no_tasks_scheduled_when_no_contract_events() { - let listener = make_service_contract_listener(None, None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - } - - #[test] - fn tasks_are_not_scheduled_on_isolated_node() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), Default::default(), Default::default(), 0)); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, Some(make_servers_set(true))); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - } - - // server key generation tests - - #[test] - fn server_key_generation_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), Default::default(), Default::default(), 0)); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::GenerateServerKey( - Default::default(), Default::default(), Default::default(), 0))); - } - - #[test] - fn no_new_tasks_scheduled_when_server_key_generation_requested_and_request_belongs_to_other_key_server() { - let server_key_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), server_key_id, Default::default(), 0)); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - } - - #[test] - fn generation_session_is_created_when_processing_generate_server_key_task() { - let cluster = Arc::new(DummyClusterClient::default()); - let listener = make_service_contract_listener(None, Some(cluster.clone()), None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::GenerateServerKey( - Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 1); - } - - #[test] - fn server_key_generation_is_not_retried_if_tried_in_the_same_cycle() { - let mut contract = DummyServiceContract::default(); - contract.pending_requests.push((false, ServiceTask::GenerateServerKey(Default::default(), - Default::default(), Default::default(), Default::default()))); - let cluster = Arc::new(DummyClusterClient::default()); - let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(cluster.clone()), None, None, None); - listener.data.retry_data.lock().affected_server_keys.insert(Default::default()); - ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); - assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 0); - } - - // server key retrieval tests - - #[test] - fn server_key_retrieval_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveServerKey(Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveServerKey( - Default::default(), Default::default()))); - } - - #[test] - fn server_key_retrieval_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { - let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveServerKey(Default::default(), server_key_id.clone())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveServerKey( - Default::default(), server_key_id))); - } - - #[test] - fn server_key_is_retrieved_when_processing_retrieve_server_key_task() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveServerKey( - Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.retrieved_server_keys.lock(), vec![(Default::default(), - KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap().public().clone(), 0)]); - } - - #[test] - fn server_key_retrieval_failure_is_reported_when_processing_retrieve_server_key_task_and_key_is_unknown() { - let contract = Arc::new(DummyServiceContract::default()); - let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveServerKey( - Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.server_keys_retrieval_failures.lock(), vec![Default::default()]); - } - - #[test] - fn server_key_retrieval_is_not_retried_if_tried_in_the_same_cycle() { - let mut contract = DummyServiceContract::default(); - contract.pending_requests.push((false, ServiceTask::RetrieveServerKey(Default::default(), Default::default()))); - let cluster = Arc::new(DummyClusterClient::default()); - let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(cluster.clone()), None, None, None); - listener.data.retry_data.lock().affected_server_keys.insert(Default::default()); - ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); - assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 0); - } - - // document key store tests - - #[test] - fn document_key_store_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::StoreDocumentKey(Default::default(), Default::default(), - Default::default(), Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default()))); - } - - #[test] - fn document_key_store_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { - let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::StoreDocumentKey(Default::default(), server_key_id.clone(), - Default::default(), Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::StoreDocumentKey( - Default::default(), server_key_id, Default::default(), Default::default(), Default::default()))); - } - - #[test] - fn document_key_is_stored_when_processing_store_document_key_task() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.stored_document_keys.lock(), vec![Default::default()]); - - let key_share = key_storage.get(&Default::default()).unwrap().unwrap(); - assert_eq!(key_share.common_point, Some(Default::default())); - assert_eq!(key_share.encrypted_point, Some(Default::default())); - } - - #[test] - fn document_key_store_failure_reported_when_no_server_key() { - let contract = Arc::new(DummyServiceContract::default()); - let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); - } - - #[test] - fn document_key_store_failure_reported_when_document_key_already_set() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(true); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); - } - - #[test] - fn document_key_store_failure_reported_when_author_differs() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Address::from_low_u64_be(1), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); - } - - // document key shadow common retrieval tests - - #[test] - fn document_key_shadow_common_retrieval_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveShadowDocumentKeyCommon(Default::default(), Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default()))); - } - - #[test] - fn document_key_shadow_common_retrieval_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { - let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveShadowDocumentKeyCommon(Default::default(), server_key_id.clone(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), server_key_id, Default::default()))); - } - - #[test] - fn document_key_shadow_common_is_retrieved_when_processing_document_key_shadow_common_retrieval_task() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(true); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.common_shadow_retrieved_document_keys.lock(), vec![(Default::default(), Default::default(), - Default::default(), 0)]); - } - - #[test] - fn document_key_shadow_common_retrieval_failure_reported_when_access_denied() { - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(Default::default(), Default::default()); - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(true); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), Some(Arc::new(acl_storage)), None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); - } - - #[test] - fn document_key_shadow_common_retrieval_failure_reported_when_no_server_key() { - let contract = Arc::new(DummyServiceContract::default()); - let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); - } - - #[test] - fn document_key_shadow_common_retrieval_failure_reported_when_no_document_key() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); - } -} diff --git a/secret-store/src/listener/tasks_queue.rs b/secret-store/src/listener/tasks_queue.rs deleted file mode 100644 index 08a4a316d..000000000 --- a/secret-store/src/listener/tasks_queue.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::VecDeque; -use parking_lot::{Mutex, Condvar}; - -#[derive(Default)] -/// General deque-based tasks queue. -pub struct TasksQueue { - /// Service event. - service_event: Condvar, - /// Service tasks queue. - service_tasks: Mutex>, -} - -impl TasksQueue where Task: Clone { - /// Create new tasks queue. - pub fn new() -> Self { - TasksQueue { - service_event: Condvar::new(), - service_tasks: Mutex::new(VecDeque::new()), - } - } - - #[cfg(test)] - /// Get current tasks snapshot. - pub fn snapshot(&self) -> VecDeque { - self.service_tasks.lock().clone() - } - - /// Push task to the front of queue. - pub fn push_front(&self, task: Task) { - let mut service_tasks = self.service_tasks.lock(); - service_tasks.push_front(task); - self.service_event.notify_all(); - } - - /// Push task to the back of queue. - pub fn push(&self, task: Task) { - let mut service_tasks = self.service_tasks.lock(); - service_tasks.push_back(task); - self.service_event.notify_all(); - } - - /// Push task to the back of queue. - pub fn push_many>(&self, tasks: I) { - let mut service_tasks = self.service_tasks.lock(); - let previous_len = service_tasks.len(); - service_tasks.extend(tasks); - if service_tasks.len() != previous_len { - self.service_event.notify_all(); - } - } - - /// Wait for new task (task is removed from the front of queue). - pub fn wait(&self) -> Task { - let mut service_tasks = self.service_tasks.lock(); - if service_tasks.is_empty() { - self.service_event.wait(&mut service_tasks); - } - - service_tasks.pop_front() - .expect("service_event is only fired when there are new tasks; qed") - } -} diff --git a/secret-store/src/migration.rs b/secret-store/src/migration.rs deleted file mode 100644 index 44ac51976..000000000 --- a/secret-store/src/migration.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Secret Store DB migration module. - - -use std::fmt::{Display, Error as FmtError, Formatter}; -use std::fs; -use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read as _}; -use std::path::PathBuf; - -/// Current db version. -const CURRENT_VERSION: u8 = 4; -/// Database is assumed to be at the default version, when no version file is found. -const DEFAULT_VERSION: u8 = 3; -/// Version file name. -const VERSION_FILE_NAME: &str = "db_version"; - -/// Migration related errors. -#[derive(Debug)] -pub enum Error { - /// Returned when current version cannot be read or guessed. - UnknownDatabaseVersion, - /// Existing DB is newer than the known one. - FutureDBVersion, - /// Migration using parity-ethereum 2.6.7 is required. - MigrationWithLegacyVersionRequired, - /// Migration was completed successfully, - /// but there was a problem with io. - Io(IoError), -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - let out = match *self { - Error::UnknownDatabaseVersion => - "Current Secret Store database version cannot be read".into(), - Error::FutureDBVersion => - "Secret Store database was created with newer client version.\ - Upgrade your client or delete DB and resync.".into(), - Error::MigrationWithLegacyVersionRequired => - "Secret Store database was created with an older client version.\ - To migrate, use parity-ethereum v2.6.7, then retry using the latest.".into(), - Error::Io(ref err) => - format!("Unexpected io error on Secret Store database migration: {}.", err), - }; - write!(f, "{}", out) - } -} - -impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err) - } -} - -/// Apply all migrations if possible. -pub fn upgrade_db(db_path: &str) -> Result<(), Error> { - match current_version(db_path)? { - old_version if old_version < CURRENT_VERSION => { - Err(Error::MigrationWithLegacyVersionRequired) - }, - CURRENT_VERSION => Ok(()), - _ => Err(Error::FutureDBVersion), - } -} - -/// Returns the version file path. -fn version_file_path(path: &str) -> PathBuf { - let mut file_path = PathBuf::from(path); - file_path.push(VERSION_FILE_NAME); - file_path -} - -/// Reads current database version from the file at given path. -/// If the file does not exist returns `DEFAULT_VERSION`. -fn current_version(path: &str) -> Result { - match fs::File::open(version_file_path(path)) { - Err(ref err) if err.kind() == IoErrorKind::NotFound => Ok(DEFAULT_VERSION), - Err(err) => Err(err.into()), - Ok(mut file) => { - let mut s = String::new(); - file.read_to_string(&mut s)?; - u8::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion) - }, - } -} - diff --git a/secret-store/src/node_key_pair.rs b/secret-store/src/node_key_pair.rs deleted file mode 100644 index 3b953a95f..000000000 --- a/secret-store/src/node_key_pair.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use crypto::publickey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address}; -use ethereum_types::{H256, Address}; -use blockchain::SigningKeyPair; - -pub struct PlainNodeKeyPair { - key_pair: KeyPair, -} - -impl PlainNodeKeyPair { - pub fn new(key_pair: KeyPair) -> Self { - PlainNodeKeyPair { - key_pair: key_pair, - } - } - - #[cfg(test)] - pub fn key_pair(&self) -> &KeyPair { - &self.key_pair - } -} - -impl SigningKeyPair for PlainNodeKeyPair { - fn public(&self) -> &Public { - self.key_pair.public() - } - - fn address(&self) -> Address { - public_to_address(self.key_pair.public()) - } - - fn sign(&self, data: &H256) -> Result { - sign(self.key_pair.secret(), data) - } -} diff --git a/secret-store/src/serialization.rs b/secret-store/src/serialization.rs deleted file mode 100644 index 90fe96781..000000000 --- a/secret-store/src/serialization.rs +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::ops::Deref; -use rustc_hex::{self, FromHex}; -use serde::{Serialize, Deserialize, Serializer, Deserializer}; -use serde::de::{Visitor, Error as SerdeError}; -use crypto::publickey::{Public, Secret, Signature}; -use ethereum_types::{H160, H256}; -use bytes::Bytes; -use types::Requester; - -trait ToHex { - fn to_hex(&self) -> String; -} - -impl ToHex for Bytes { - fn to_hex(&self) -> String { - format!("0x{}", rustc_hex::ToHex::to_hex(&self[..])) - } -} - -impl ToHex for Signature { - fn to_hex(&self) -> String { - format!("0x{}", self) - } -} - -impl ToHex for Secret { - fn to_hex(&self) -> String { - format!("0x{}", self.to_hex()) - } -} - -macro_rules! impl_to_hex { - ($name: ident) => ( - impl ToHex for $name { - fn to_hex(&self) -> String { - format!("{:#x}", self) - } - } - ); -} - -macro_rules! impl_bytes_deserialize { - ($name: ident, $value: expr, true) => { - $value[2..].from_hex().map($name).map_err(SerdeError::custom) - }; - ($name: ident, $value: expr, false) => { - $value[2..].parse().map($name).map_err(SerdeError::custom) - } -} - -macro_rules! impl_bytes { - ($name: ident, $other: ident, $from_hex: ident, ($($trait: ident),*)) => { - #[derive(Clone, Debug, PartialEq, Eq, $($trait,)*)] - pub struct $name(pub $other); - - impl From for $name where $other: From { - fn from(s: T) -> $name { - $name(s.into()) - } - } - - impl Into<$other> for $name { - fn into(self) -> $other { - self.0 - } - } - - impl Deref for $name { - type Target = $other; - - fn deref(&self) -> &$other { - &self.0 - } - } - - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_str(<$other as ToHex>::to_hex(&self.0).as_ref()) - } - } - - impl<'a> Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - struct HexBytesVisitor; - - impl<'b> Visitor<'b> for HexBytesVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex-encoded bytes string") - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - if value.len() >= 2 && &value[0..2] == "0x" && value.len() & 1 == 0 { - impl_bytes_deserialize!($name, value, $from_hex) - } else { - Err(SerdeError::custom("invalid format")) - } - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(HexBytesVisitor) - } - } - } -} - -/// Serializable message hash. -pub type SerializableMessageHash = SerializableH256; -/// Serializable address; -pub type SerializableAddress = SerializableH160; - -impl_to_hex!(H256); -impl_to_hex!(H160); -impl_to_hex!(Public); - -impl_bytes!(SerializableBytes, Bytes, true, (Default)); -impl_bytes!(SerializableH256, H256, false, (Default, PartialOrd, Ord)); -impl_bytes!(SerializableH160, H160, false, (Default)); -impl_bytes!(SerializablePublic, Public, false, (Default, PartialOrd, Ord)); -impl_bytes!(SerializableSecret, Secret, false, ()); -impl_bytes!(SerializableSignature, Signature, false, ()); - -/// Serializable shadow decryption result. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SerializableEncryptedDocumentKeyShadow { - /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. - pub decrypted_secret: SerializablePublic, - /// Shared common point. - pub common_point: SerializablePublic, - /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. - pub decrypt_shadows: Vec, -} - -/// Serializable requester identification data. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum SerializableRequester { - /// Requested with server key id signature. - Signature(SerializableSignature), - /// Requested with public key. - Public(SerializablePublic), - /// Requested with verified address. - Address(SerializableAddress), -} - -impl From for Requester { - fn from(requester: SerializableRequester) -> Requester { - match requester { - SerializableRequester::Signature(signature) => Requester::Signature(signature.into()), - SerializableRequester::Public(public) => Requester::Public(public.into()), - SerializableRequester::Address(address) => Requester::Address(address.into()), - } - } -} - -impl From for SerializableRequester { - fn from(requester: Requester) -> SerializableRequester { - match requester { - Requester::Signature(signature) => SerializableRequester::Signature(signature.into()), - Requester::Public(public) => SerializableRequester::Public(public.into()), - Requester::Address(address) => SerializableRequester::Address(address.into()), - } - } -} - -#[cfg(test)] -mod tests { - use serde_json; - use super::*; - use std::str::FromStr; - - macro_rules! do_test { - ($value: expr, $expected: expr, $expected_type: ident) => ( - let serialized = serde_json::to_string(&$value).unwrap(); - assert_eq!(serialized, $expected); - let deserialized: $expected_type = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, $value); - ); - } - - #[test] - fn serialize_and_deserialize_bytes() { - do_test!(SerializableBytes(vec![1, 2, 3, 4]), "\"0x01020304\"".to_owned(), SerializableBytes); - } - - #[test] - fn serialize_and_deserialize_h256() { - let s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; - let h256 = SerializableH256(H256::from_str(s).unwrap()); - do_test!(h256, format!("\"0x{}\"", s), SerializableH256); - } - - #[test] - fn serialize_and_deserialize_h160() { - let s = "c6d9d2cd449a754c494264e1809c50e34d64562b"; - let h160 = SerializableH160(H160::from_str(s).unwrap()); - do_test!(h160, format!("\"0x{}\"", s), SerializableH160); - } - - #[test] - fn serialize_and_deserialize_public() { - let s = "cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325"; - let public = SerializablePublic(s.parse().unwrap()); - do_test!(public, format!("\"0x{}\"", s), SerializablePublic); - } - - #[test] - fn serialize_and_deserialize_secret() { - let s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; - let secret = SerializableSecret(Secret::from_str(s).unwrap()); - do_test!(secret, format!("\"0x{}\"", s), SerializableSecret); - } - - #[test] - fn serialize_and_deserialize_signature() { - let raw_r = "afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"; - let raw_s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; - let r = H256::from_str(raw_r).unwrap(); - let s = H256::from_str(raw_s).unwrap(); - let v = 42u8; - let public = SerializableSignature(Signature::from_rsv(&r, &s, v)); - do_test!(public, format!("\"0x{}{}{:x}\"", raw_r, raw_s, v), SerializableSignature); - } -} diff --git a/secret-store/src/traits.rs b/secret-store/src/traits.rs deleted file mode 100644 index 441bd25a7..000000000 --- a/secret-store/src/traits.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use futures::Future; -use types::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, Requester, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; - -/// Server key (SK) generator. -pub trait ServerKeyGenerator { - /// Generate new SK. - /// `key_id` is the caller-provided identifier of generated SK. - /// `author` is the author of key entry. - /// `threshold + 1` is the minimal number of nodes, required to restore private key. - /// Result is a public portion of SK. - fn generate_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send>; - /// Retrieve public portion of previously generated SK. - /// `key_id` is identifier of previously generated SK. - /// `author` is the same author, that has created the server key. - fn restore_key_public( - &self, - key_id: ServerKeyId, - author: Requester, - ) -> Box + Send>; -} - -/// Document key (DK) server. -pub trait DocumentKeyServer: ServerKeyGenerator { - /// Store externally generated DK. - /// `key_id` is identifier of previously generated SK. - /// `author` is the same author, that has created the server key. - /// `common_point` is a result of `k * T` expression, where `T` is generation point and `k` is random scalar in EC field. - /// `encrypted_document_key` is a result of `M + k * y` expression, where `M` is unencrypted document key (point on EC), - /// `k` is the same scalar used in `common_point` calculation and `y` is previously generated public part of SK. - fn store_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - common_point: Public, - encrypted_document_key: Public, - ) -> Box + Send>; - /// Generate and store both SK and DK. This is a shortcut for consequent calls of `generate_key` and `store_document_key`. - /// The only difference is that DK is generated by DocumentKeyServer (which might be considered unsafe). - /// `key_id` is the caller-provided identifier of generated SK. - /// `author` is the author of server && document key entry. - /// `threshold + 1` is the minimal number of nodes, required to restore private key. - /// Result is a DK, encrypted with caller public key. - fn generate_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send>; - /// Restore previously stored DK. - /// DK is decrypted on the key server (which might be considered unsafe), and then encrypted with caller public key. - /// `key_id` is identifier of previously generated SK. - /// `requester` is the one who requests access to document key. Caller must be on ACL for this function to succeed. - /// Result is a DK, encrypted with caller public key. - fn restore_document_key( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send>; - /// Restore previously stored DK. - /// To decrypt DK on client: - /// 1) use requestor secret key to decrypt secret coefficients from result.decrypt_shadows - /// 2) calculate decrypt_shadows_sum = sum of all secrets from (1) - /// 3) calculate decrypt_shadow_point: decrypt_shadows_sum * result.common_point - /// 4) calculate decrypted_secret: result.decrypted_secret + decrypt_shadow_point - /// Result is a DK shadow. - fn restore_document_key_shadow( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send>; -} - -/// Message signer. -pub trait MessageSigner: ServerKeyGenerator { - /// Generate Schnorr signature for message with previously generated SK. - /// `key_id` is the caller-provided identifier of generated SK. - /// `requester` is the one who requests access to server key private. - /// `message` is the message to be signed. - /// Result is a signed message, encrypted with caller public key. - fn sign_message_schnorr( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send>; - /// Generate ECDSA signature for message with previously generated SK. - /// WARNING: only possible when SK was generated using t <= 2 * N. - /// `key_id` is the caller-provided identifier of generated SK. - /// `signature` is `key_id`, signed with caller public key. - /// `message` is the message to be signed. - /// Result is a signed message, encrypted with caller public key. - fn sign_message_ecdsa( - &self, - key_id: ServerKeyId, - signature: Requester, - message: MessageHash, - ) -> Box + Send>; -} - -/// Administrative sessions server. -pub trait AdminSessionsServer { - /// Change servers set so that nodes in new_servers_set became owners of shares for all keys. - /// And old nodes (i.e. cluster nodes except new_servers_set) have clear databases. - /// WARNING: newly generated keys will be distributed among all cluster nodes. So this session - /// must be followed with cluster nodes change (either via contract, or config files). - fn change_servers_set( - &self, - old_set_signature: RequestSignature, - new_set_signature: RequestSignature, - new_servers_set: BTreeSet, - ) -> Box + Send>; -} - -/// Key server. -pub trait KeyServer: AdminSessionsServer + DocumentKeyServer + MessageSigner + Send + Sync { -} diff --git a/secret-store/src/types/all.rs b/secret-store/src/types/all.rs deleted file mode 100644 index e85285b26..000000000 --- a/secret-store/src/types/all.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; - -use blockchain::ContractAddress; -use {bytes, ethereum_types}; - -/// Node id. -pub type NodeId = crypto::publickey::Public; -/// Server key id. When key is used to encrypt document, it could be document contents hash. -pub type ServerKeyId = ethereum_types::H256; -/// Encrypted document key type. -pub type EncryptedDocumentKey = bytes::Bytes; -/// Message hash. -pub type MessageHash = ethereum_types::H256; -/// Message signature. -pub type EncryptedMessageSignature = bytes::Bytes; -/// Request signature type. -pub type RequestSignature = crypto::publickey::Signature; -/// Public key type. -pub use crypto::publickey::Public; - -/// Secret store configuration -#[derive(Debug, Clone)] -pub struct NodeAddress { - /// IP address. - pub address: String, - /// IP port. - pub port: u16, -} - -/// Secret store configuration -#[derive(Debug)] -pub struct ServiceConfiguration { - /// HTTP listener address. If None, HTTP API is disabled. - pub listener_address: Option, - /// Service contract address. - pub service_contract_address: Option, - /// Server key generation service contract address. - pub service_contract_srv_gen_address: Option, - /// Server key retrieval service contract address. - pub service_contract_srv_retr_address: Option, - /// Document key store service contract address. - pub service_contract_doc_store_address: Option, - /// Document key shadow retrieval service contract address. - pub service_contract_doc_sretr_address: Option, - /// ACL check contract address. If None, everyone has access to all keys. Useful for tests only. - pub acl_check_contract_address: Option, - /// Cluster configuration. - pub cluster_config: ClusterConfiguration, - // Allowed CORS domains - pub cors: Option>, -} - -/// Key server cluster configuration -#[derive(Debug)] -pub struct ClusterConfiguration { - /// This node address. - pub listener_address: NodeAddress, - /// All cluster nodes addresses. - pub nodes: BTreeMap, - /// Key Server Set contract address. If None, servers from 'nodes' map are used. - pub key_server_set_contract_address: Option, - /// Allow outbound connections to 'higher' nodes. - /// This is useful for tests, but slower a bit for production. - pub allow_connecting_to_higher_nodes: bool, - /// Administrator public key. - pub admin_public: Option, - /// Should key servers set change session should be started when servers set changes. - /// This will only work when servers set is configured using KeyServerSet contract. - pub auto_migrate_enabled: bool, -} - -/// Shadow decryption result. -#[derive(Clone, Debug, PartialEq)] -pub struct EncryptedDocumentKeyShadow { - /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. - pub decrypted_secret: crypto::publickey::Public, - /// Shared common point. - pub common_point: Option, - /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. - pub decrypt_shadows: Option>>, -} - -/// Requester identification data. -#[derive(Debug, Clone)] -pub enum Requester { - /// Requested with server key id signature. - Signature(crypto::publickey::Signature), - /// Requested with public key. - Public(crypto::publickey::Public), - /// Requested with verified address. - Address(ethereum_types::Address), -} - -impl Default for Requester { - fn default() -> Self { - Requester::Signature(Default::default()) - } -} - -impl Requester { - pub fn public(&self, server_key_id: &ServerKeyId) -> Result { - match *self { - Requester::Signature(ref signature) => crypto::publickey::recover(signature, server_key_id) - .map_err(|e| format!("bad signature: {}", e)), - Requester::Public(ref public) => Ok(public.clone()), - Requester::Address(_) => Err("cannot recover public from address".into()), - } - } - - pub fn address(&self, server_key_id: &ServerKeyId) -> Result { - self.public(server_key_id) - .map(|p| crypto::publickey::public_to_address(&p)) - } -} - -impl From for Requester { - fn from(signature: crypto::publickey::Signature) -> Requester { - Requester::Signature(signature) - } -} - -impl From for Requester { - fn from(public: ethereum_types::Public) -> Requester { - Requester::Public(public) - } -} - -impl From for Requester { - fn from(address: ethereum_types::Address) -> Requester { - Requester::Address(address) - } -} diff --git a/secret-store/src/types/error.rs b/secret-store/src/types/error.rs deleted file mode 100644 index f977146e7..000000000 --- a/secret-store/src/types/error.rs +++ /dev/null @@ -1,202 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::net; -use std::io::Error as IoError; - -use crypto; - -/// Secret store error. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum Error { - /// Invalid node address has been passed. - InvalidNodeAddress, - /// Invalid node id has been passed. - InvalidNodeId, - /// Session with the given id already exists. - DuplicateSessionId, - /// No active session with given id. - NoActiveSessionWithId, - /// Invalid threshold value has been passed. - /// Threshold value must be in [0; n - 1], where n is a number of nodes participating in the encryption. - NotEnoughNodesForThreshold, - /// Current state of encryption/decryption session does not allow to proceed request. - /// Reschedule this request for later processing. - TooEarlyForRequest, - /// Current state of encryption/decryption session does not allow to proceed request. - /// This means that either there is some comm-failure or node is misbehaving/cheating. - InvalidStateForRequest, - /// Request cannot be sent/received from this node. - InvalidNodeForRequest, - /// Message or some data in the message was recognized as invalid. - /// This means that node is misbehaving/cheating. - InvalidMessage, - /// Message version is not supported. - InvalidMessageVersion, - /// Message is invalid because of replay-attack protection. - ReplayProtection, - /// Connection to node, required for this session is not established. - NodeDisconnected, - /// Server key with this ID is already generated. - ServerKeyAlreadyGenerated, - /// Server key with this ID is not yet generated. - ServerKeyIsNotFound, - /// Document key with this ID is already stored. - DocumentKeyAlreadyStored, - /// Document key with this ID is not yet stored. - DocumentKeyIsNotFound, - /// Consensus is temporary unreachable. Means that something is currently blocking us from either forming - /// consensus group (like disconnecting from too many nodes, which are AGREE to participate in consensus) - /// or from rejecting request (disconnecting from AccessDenied-nodes). - ConsensusTemporaryUnreachable, - /// Consensus is unreachable. It doesn't mean that it will ALWAYS remain unreachable, but right NOW we have - /// enough nodes confirmed that they do not want to be a part of consensus. Example: we're connected to 10 - /// of 100 nodes. Key threshold is 6 (i.e. 7 nodes are required for consensus). 4 nodes are responding with - /// reject => consensus is considered unreachable, even though another 90 nodes still can respond with OK. - ConsensusUnreachable, - /// Acl storage error. - AccessDenied, - /// Can't start session, because exclusive session is active. - ExclusiveSessionActive, - /// Can't start exclusive session, because there are other active sessions. - HasActiveSessions, - /// Insufficient requester data. - InsufficientRequesterData(String), - /// Cryptographic error. - EthKey(String), - /// I/O error has occurred. - Io(String), - /// Deserialization error has occurred. - Serde(String), - /// Hyper error. - Hyper(String), - /// Database-related error. - Database(String), - /// Internal error. - Internal(String), -} - -impl Error { - /// Is this a fatal error? Non-fatal means that it is possible to replay the same request with a non-zero - /// chance to success. I.e. the error is not about request itself (or current environment factors that - /// are affecting request processing), but about current SecretStore state. - pub fn is_non_fatal(&self) -> bool { - match *self { - // non-fatal errors: - - // session start errors => restarting session is a solution - Error::DuplicateSessionId | Error::NoActiveSessionWithId | - // unexpected message errors => restarting session/excluding node is a solution - Error::TooEarlyForRequest | Error::InvalidStateForRequest | Error::InvalidNodeForRequest | - // invalid message errors => restarting/updating/excluding node is a solution - Error::InvalidMessage | Error::InvalidMessageVersion | Error::ReplayProtection | - // connectivity problems => waiting for reconnect && restarting session is a solution - Error::NodeDisconnected | - // temporary (?) consensus problems, related to other non-fatal errors => restarting is probably (!) a solution - Error::ConsensusTemporaryUnreachable | - // exclusive session errors => waiting && restarting is a solution - Error::ExclusiveSessionActive | Error::HasActiveSessions => true, - - // fatal errors: - - // config-related errors - Error::InvalidNodeAddress | Error::InvalidNodeId | - // wrong session input params errors - Error::NotEnoughNodesForThreshold | Error::ServerKeyAlreadyGenerated | Error::ServerKeyIsNotFound | - Error::DocumentKeyAlreadyStored | Error::DocumentKeyIsNotFound | Error::InsufficientRequesterData(_) | - // access denied/consensus error - Error::AccessDenied | Error::ConsensusUnreachable | - // indeterminate internal errors, which could be either fatal (db failure, invalid request), or not (network error), - // but we still consider these errors as fatal - Error::EthKey(_) | Error::Serde(_) | Error::Hyper(_) | Error::Database(_) | Error::Internal(_) | Error::Io(_) => false, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::InvalidNodeAddress => write!(f, "invalid node address has been passed"), - Error::InvalidNodeId => write!(f, "invalid node id has been passed"), - Error::DuplicateSessionId => write!(f, "session with the same id is already registered"), - Error::NoActiveSessionWithId => write!(f, "no active session with given id"), - Error::NotEnoughNodesForThreshold => write!(f, "not enough nodes for passed threshold"), - Error::TooEarlyForRequest => write!(f, "session is not yet ready to process this request"), - Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), - Error::InvalidNodeForRequest => write!(f, "invalid node for this request"), - Error::InvalidMessage => write!(f, "invalid message is received"), - Error::InvalidMessageVersion => write!(f, "unsupported message is received"), - Error::ReplayProtection => write!(f, "replay message is received"), - Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), - Error::ServerKeyAlreadyGenerated => write!(f, "Server key with this ID is already generated"), - Error::ServerKeyIsNotFound => write!(f, "Server key with this ID is not found"), - Error::DocumentKeyAlreadyStored => write!(f, "Document key with this ID is already stored"), - Error::DocumentKeyIsNotFound => write!(f, "Document key with this ID is not found"), - Error::ConsensusUnreachable => write!(f, "Consensus unreachable"), - Error::ConsensusTemporaryUnreachable => write!(f, "Consensus temporary unreachable"), - Error::AccessDenied => write!(f, "Access denied"), - Error::ExclusiveSessionActive => write!(f, "Exclusive session active"), - Error::HasActiveSessions => write!(f, "Unable to start exclusive session"), - Error::InsufficientRequesterData(ref e) => write!(f, "Insufficient requester data: {}", e), - Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), - Error::Hyper(ref msg) => write!(f, "Hyper error: {}", msg), - Error::Serde(ref msg) => write!(f, "Serialization error: {}", msg), - Error::Database(ref msg) => write!(f, "Database error: {}", msg), - Error::Internal(ref msg) => write!(f, "Internal error: {}", msg), - Error::Io(ref msg) => write!(f, "IO error: {}", msg), - } - } -} - -impl From for Error { - fn from(err: crypto::publickey::Error) -> Self { - Error::EthKey(err.into()) - } -} - -impl From for Error { - fn from(err: crypto::Error) -> Self { - Error::EthKey(err.to_string()) - } -} - -impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err.to_string()) - } -} - -impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } -} - -impl From for Error { - fn from(err: net::AddrParseError) -> Error { - Error::Internal(err.to_string()) - } -} - -impl From for Error { - fn from(e: secp256k1::Error) -> Self { - match e { - secp256k1::Error::InvalidSecretKey => Error::EthKey("Invalid SecretKey".into()), - _ => Error::EthKey(format!("Crypto error: {}", e).into()) - } - } -} diff --git a/secret-store/src/types/mod.rs b/secret-store/src/types/mod.rs deleted file mode 100644 index 3a8511d23..000000000 --- a/secret-store/src/types/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum 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. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Types used in the public api - -mod all; -mod error; - -pub use self::all::*; -pub use self::error::*;