From 18582d7b654382473a831eda59dc3d5139b39efe Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 30 Jun 2017 11:26:09 +0300 Subject: [PATCH 01/28] do not cache ACL storage contract --- secret_store/src/acl_storage.rs | 54 ++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 816d100dc..8ddc9a6e3 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -20,6 +20,7 @@ use parking_lot::Mutex; use ethkey::public_to_address; use ethcore::client::{Client, BlockChainClient, BlockId}; use native_contracts::SecretStoreAclStorage; +use util::{H256, Address}; use types::all::{Error, ServerKeyId, Public}; const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; @@ -32,33 +33,64 @@ pub trait AclStorage: Send + Sync { /// 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, - /// On-chain contract. - contract: Mutex>, + /// Hash of best block, when contract address has been read. + best_block_hash: Option, + /// Contract address. + contract_addr: Option
, + /// Contract at given address. + contract: Option, } impl OnChainAclStorage { pub fn new(client: Arc) -> Self { OnChainAclStorage { - client: client, - contract: Mutex::new(None), + contract: Mutex::new(CachedContract::new(client)), } } } impl AclStorage for OnChainAclStorage { fn check(&self, public: &Public, document: &ServerKeyId) -> Result { - let mut contract = self.contract.lock(); - if !contract.is_some() { - *contract = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()) - .and_then(|contract_addr| { + self.contract.lock().check(public, document) + } +} + +impl CachedContract { + pub fn new(client: Arc) -> Self { + CachedContract { + client: client, + best_block_hash: None, + contract_addr: None, + contract: None, + } + } + + pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result { + let new_best_block_hash = self.client.best_block_header().hash(); + if self.best_block_hash.as_ref() != Some(&new_best_block_hash) { + let new_contract_addr = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()); + if self.contract_addr.as_ref() != new_contract_addr.as_ref() { + self.contract = new_contract_addr.map(|contract_addr| { trace!(target: "secretstore", "Configuring for ACL checker contract from {}", contract_addr); - Some(SecretStoreAclStorage::new(contract_addr)) - }) + SecretStoreAclStorage::new(contract_addr) + }); + + self.contract_addr = new_contract_addr; + } + + self.best_block_hash = Some(new_best_block_hash); } - if let Some(ref contract) = *contract { + + if let Some(contract) = self.contract.as_ref() { let address = public_to_address(&public); let do_call = |a, d| future::done(self.client.call_contract(BlockId::Latest, a, d)); contract.check_permissions(do_call, address, document.clone()) From 5cc40d45251196353bf2b99dd0ad09c38c9ecae1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 14 Jul 2017 14:51:24 +0300 Subject: [PATCH 02/28] when error comes before initialization --- .../key_server_cluster/jobs/job_session.rs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs index 7ae1da42a..6608397dd 100644 --- a/secret_store/src/key_server_cluster/jobs/job_session.rs +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -299,22 +299,22 @@ impl JobSession where Executor: JobExe return Err(Error::ConsensusUnreachable); } - let active_data = self.data.active_data.as_mut() - .expect("we have checked that we are on master node; on master nodes active_data is filled during initialization; qed"); - if active_data.rejects.contains(node) { - return Ok(()); - } - if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { - active_data.rejects.insert(node.clone()); - 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 { + if let Some(active_data) = self.data.active_data.as_mut() { + if active_data.rejects.contains(node) { return Ok(()); } + if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { + active_data.rejects.insert(node.clone()); + 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(Error::ConsensusUnreachable); + self.data.state = JobSessionState::Failed; + return Err(Error::ConsensusUnreachable); + } } Ok(()) From 81de7e1075600dbaca46bc14baab6cf56ec7c4e0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 19 Jul 2017 11:35:17 +0300 Subject: [PATCH 03/28] initial KeyServerSet commit --- Cargo.lock | 1 + ethcore/native_contracts/build.rs | 2 + .../native_contracts/res/key_server_set.json | 1 + .../native_contracts/src/key_server_set.rs | 21 +++ ethcore/native_contracts/src/lib.rs | 2 + secret_store/Cargo.toml | 1 + secret_store/src/acl_storage.rs | 61 ++++---- secret_store/src/key_server.rs | 11 +- .../src/key_server_cluster/cluster.rs | 13 +- .../key_server_cluster/cluster_sessions.rs | 2 +- secret_store/src/key_server_cluster/mod.rs | 1 + secret_store/src/key_server_set.rs | 145 ++++++++++++++++++ secret_store/src/lib.rs | 10 +- 13 files changed, 230 insertions(+), 41 deletions(-) create mode 100644 ethcore/native_contracts/res/key_server_set.json create mode 100644 ethcore/native_contracts/src/key_server_set.rs create mode 100644 secret_store/src/key_server_set.rs diff --git a/Cargo.lock b/Cargo.lock index ad3cca5c7..ae33d7590 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -669,6 +669,7 @@ dependencies = [ "futures 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "futures-cpupool 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", "native-contracts 0.1.0", "parking_lot 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/native_contracts/build.rs b/ethcore/native_contracts/build.rs index cec830929..bcb64067c 100644 --- a/ethcore/native_contracts/build.rs +++ b/ethcore/native_contracts/build.rs @@ -21,6 +21,7 @@ use std::fs::File; use std::io::Write; // TODO: just walk the "res" directory and generate whole crate automatically. +const KEY_SERVER_SET_ABI: &'static str = include_str!("res/key_server_set.json"); const REGISTRY_ABI: &'static str = include_str!("res/registrar.json"); const URLHINT_ABI: &'static str = include_str!("res/urlhint.json"); const SERVICE_TRANSACTION_ABI: &'static str = include_str!("res/service_transaction.json"); @@ -45,6 +46,7 @@ fn build_test_contracts() { } fn main() { + build_file("KeyServerSet", KEY_SERVER_SET_ABI, "key_server_set.rs"); build_file("Registry", REGISTRY_ABI, "registry.rs"); build_file("Urlhint", URLHINT_ABI, "urlhint.rs"); build_file("ServiceTransactionChecker", SERVICE_TRANSACTION_ABI, "service_transaction.rs"); diff --git a/ethcore/native_contracts/res/key_server_set.json b/ethcore/native_contracts/res/key_server_set.json new file mode 100644 index 000000000..93f68837a --- /dev/null +++ b/ethcore/native_contracts/res/key_server_set.json @@ -0,0 +1 @@ +[{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"keyServersList","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_new","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"getKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"keyServer","type":"address"}],"name":"removeKeyServer","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"keyServerPublic","type":"bytes"},{"name":"keyServerIp","type":"string"}],"name":"addKeyServer","outputs":[],"payable":false,"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":[{"indexed":true,"name":"old","type":"address"},{"indexed":true,"name":"current","type":"address"}],"name":"NewOwner","type":"event"}] \ No newline at end of file diff --git a/ethcore/native_contracts/src/key_server_set.rs b/ethcore/native_contracts/src/key_server_set.rs new file mode 100644 index 000000000..60b137aae --- /dev/null +++ b/ethcore/native_contracts/src/key_server_set.rs @@ -0,0 +1,21 @@ +// Copyright 2015-2017 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 . + +#![allow(unused_mut, unused_variables, unused_imports)] + +//! Secret store Key Server set contract. + +include!(concat!(env!("OUT_DIR"), "/key_server_set.rs")); diff --git a/ethcore/native_contracts/src/lib.rs b/ethcore/native_contracts/src/lib.rs index e35a4ec19..33cb91563 100644 --- a/ethcore/native_contracts/src/lib.rs +++ b/ethcore/native_contracts/src/lib.rs @@ -23,6 +23,7 @@ extern crate byteorder; extern crate ethabi; extern crate ethcore_util as util; +mod key_server_set; mod registry; mod urlhint; mod service_transaction; @@ -32,6 +33,7 @@ mod validator_report; pub mod test_contracts; +pub use self::key_server_set::KeyServerSet; pub use self::registry::Registry; pub use self::urlhint::Urlhint; pub use self::service_transaction::ServiceTransactionChecker; diff --git a/secret_store/Cargo.toml b/secret_store/Cargo.toml index eea49978d..19f342aa9 100644 --- a/secret_store/Cargo.toml +++ b/secret_store/Cargo.toml @@ -35,3 +35,4 @@ ethcore-logger = { path = "../logger" } ethcrypto = { path = "../ethcrypto" } ethkey = { path = "../ethkey" } native-contracts = { path = "../ethcore/native_contracts" } +lazy_static = "0.2" diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 8ddc9a6e3..7ae72cbfc 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -14,13 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::sync::Arc; +use std::sync::{Arc, Weak}; use futures::{future, Future}; use parking_lot::Mutex; use ethkey::public_to_address; -use ethcore::client::{Client, BlockChainClient, BlockId}; +use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use native_contracts::SecretStoreAclStorage; -use util::{H256, Address}; +use util::{H256, Address, Bytes}; use types::all::{Error, ServerKeyId, Public}; const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; @@ -40,9 +40,7 @@ pub struct OnChainAclStorage { /// Cached on-chain ACL storage contract. struct CachedContract { /// Blockchain client. - client: Arc, - /// Hash of best block, when contract address has been read. - best_block_hash: Option, + client: Weak, /// Contract address. contract_addr: Option
, /// Contract at given address. @@ -50,10 +48,12 @@ struct CachedContract { } impl OnChainAclStorage { - pub fn new(client: Arc) -> Self { - OnChainAclStorage { + pub fn new(client: &Arc) -> Arc { + let acl_storage = Arc::new(OnChainAclStorage { contract: Mutex::new(CachedContract::new(client)), - } + }); + client.add_notify(acl_storage.clone()); + acl_storage } } @@ -63,20 +63,24 @@ impl AclStorage for OnChainAclStorage { } } +impl ChainNotify for OnChainAclStorage { + fn new_blocks(&self, _imported: Vec, _invalid: Vec, _enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { + self.contract.lock().update() + } +} + impl CachedContract { - pub fn new(client: Arc) -> Self { + pub fn new(client: &Arc) -> Self { CachedContract { - client: client, - best_block_hash: None, + client: Arc::downgrade(client), contract_addr: None, contract: None, } } - pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result { - let new_best_block_hash = self.client.best_block_header().hash(); - if self.best_block_hash.as_ref() != Some(&new_best_block_hash) { - let new_contract_addr = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()); + pub fn update(&mut self) { + if let Some(client) = self.client.upgrade() { + let new_contract_addr = client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()); if self.contract_addr.as_ref() != new_contract_addr.as_ref() { self.contract = new_contract_addr.map(|contract_addr| { trace!(target: "secretstore", "Configuring for ACL checker contract from {}", contract_addr); @@ -86,18 +90,23 @@ impl CachedContract { self.contract_addr = new_contract_addr; } - - self.best_block_hash = Some(new_best_block_hash); } + } - if let Some(contract) = self.contract.as_ref() { - let address = public_to_address(&public); - let do_call = |a, d| future::done(self.client.call_contract(BlockId::Latest, a, d)); - contract.check_permissions(do_call, address, document.clone()) - .map_err(|err| Error::Internal(err)) - .wait() - } else { - Err(Error::Internal("ACL checker contract is not configured".to_owned())) + pub fn check(&mut self, public: &Public, document: &ServerKeyId) -> Result { + match self.contract.as_ref() { + Some(contract) => { + let address = public_to_address(&public); + let do_call = |a, d| future::done( + self.client + .upgrade() + .ok_or("Calling contract without client".into()) + .and_then(|c| c.call_contract(BlockId::Latest, a, d))); + contract.check_permissions(do_call, address, document.clone()) + .map_err(|err| Error::Internal(err)) + .wait() + }, + None => Err(Error::Internal("ACL checker contract is not configured".to_owned())), } } } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index fd4e154fa..969782ca2 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -24,6 +24,7 @@ use ethcrypto; use ethkey; use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; +use super::key_server_set::KeyServerSet; use key_server_cluster::{math, ClusterCore}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, @@ -44,9 +45,9 @@ pub struct KeyServerCore { impl KeyServerImpl { /// Create new key server instance - pub fn new(config: &ClusterConfiguration, acl_storage: Arc, key_storage: Arc) -> Result { + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, acl_storage: Arc, key_storage: Arc) -> Result { Ok(KeyServerImpl { - data: Arc::new(Mutex::new(KeyServerCore::new(config, acl_storage, key_storage)?)), + data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, acl_storage, key_storage)?)), }) } @@ -143,14 +144,12 @@ impl MessageSigner for KeyServerImpl { } impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, acl_storage: Arc, key_storage: Arc) -> Result { + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, acl_storage: Arc, key_storage: Arc) -> Result { let config = NetClusterConfiguration { threads: config.threads, self_key_pair: ethkey::KeyPair::from_secret_slice(&config.self_private)?, listen_address: (config.listener_address.address.clone(), config.listener_address.port), - nodes: config.nodes.iter() - .map(|(node_id, node_address)| (node_id.clone(), (node_address.address.clone(), node_address.port))) - .collect(), + key_server_set: key_server_set, allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes, acl_storage: acl_storage, key_storage: key_storage, diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index c86f30267..b929e835d 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -28,7 +28,7 @@ use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; use util::H256; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, KeyServerSet}; use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, DecryptionSessionWrapper, SigningSessionWrapper}; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, @@ -102,8 +102,8 @@ pub struct ClusterConfiguration { pub self_key_pair: KeyPair, /// Interface to listen to. pub listen_address: (String, u16), - /// Cluster nodes. - pub nodes: BTreeMap, + /// Cluster nodes set. + pub key_server_set: Arc, /// Reference to key storage pub key_storage: Arc, /// Reference to ACL storage @@ -671,9 +671,10 @@ impl ClusterConnections { connections: RwLock::new(BTreeMap::new()), }; - for (node_id, &(ref node_addr, node_port)) in config.nodes.iter().filter(|&(node_id, _)| node_id != config.self_key_pair.public()) { - let socket_address = make_socket_address(&node_addr, node_port)?; - connections.nodes.insert(node_id.clone(), socket_address); + let nodes = config.key_server_set.get(); + for (node_id, socket_address) in nodes.iter().filter(|&(node_id, _)| node_id != config.self_key_pair.public()) { + //let socket_address = make_socket_address(&node_addr, node_port)?; + connections.nodes.insert(node_id.clone(), socket_address.clone()); } Ok(connections) diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index f66ad972f..f8e4974b1 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -135,7 +135,7 @@ impl ClusterSessions { pub fn new(config: &ClusterConfiguration) -> Self { ClusterSessions { self_node_id: config.self_key_pair.public().clone(), - nodes: config.nodes.keys().cloned().collect(), + nodes: config.key_server_set.get().keys().cloned().collect(), acl_storage: config.acl_storage.clone(), key_storage: config.key_storage.clone(), generation_sessions: ClusterSessionsContainer::new(), diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 71c505f95..11c32d528 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -23,6 +23,7 @@ use super::types::all::ServerKeyId; pub use super::types::all::{NodeId, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare}; +pub use super::key_server_set::KeyServerSet; pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic, SerializableMessageHash}; pub use self::cluster::{ClusterCore, ClusterConfiguration, ClusterClient}; pub use self::generation_session::Session as GenerationSession; diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs new file mode 100644 index 000000000..22ffbdb07 --- /dev/null +++ b/secret_store/src/key_server_set.rs @@ -0,0 +1,145 @@ +// Copyright 2015-2017 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, Weak}; +use std::net::SocketAddr; +use std::collections::HashMap; +use futures::{future, Future}; +use parking_lot::Mutex; +use ethcore::filter::Filter; +use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; +use native_contracts::KeyServerSet as KeyServerSetContract; +use util::{H256, Address, Bytes, Hashable}; +use types::all::Public; + +const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; + +// TODO: ethabi should be able to generate this. +const ADDED_EVENT_NAME: &'static [u8] = &*b"KeyServerAdded()"; +const REMOVED_EVENT_NAME: &'static [u8] = &*b"KeyServerRemoved()"; + +lazy_static! { + static ref ADDED_EVENT_NAME_HASH: H256 = ADDED_EVENT_NAME.sha3(); + static ref REMOVED_EVENT_NAME_HASH: H256 = REMOVED_EVENT_NAME.sha3(); +} + +/// Key Server set +pub trait KeyServerSet: Send + Sync { + /// Get set of configured key servers + fn get(&self) -> HashMap; +} + +/// On-chain Key Server set implementation. +pub struct OnChainKeyServerSet { + /// Cached on-chain contract. + contract: Mutex, +} + +/// Cached on-chain Key Server set contract. +struct CachedContract { + /// Blockchain client. + client: Weak, + /// Contract address. + contract_addr: Option
, + /// Active set of key servers. + key_servers: HashMap, +} + +impl OnChainKeyServerSet { + pub fn new(client: &Arc, key_servers: HashMap) -> Arc { + let key_server_set = Arc::new(OnChainKeyServerSet { + contract: Mutex::new(CachedContract::new(client, key_servers)), + }); + client.add_notify(key_server_set.clone()); + key_server_set + } +} + +impl KeyServerSet for OnChainKeyServerSet { + fn get(&self) -> HashMap { + self.contract.lock().get() + } +} + +impl ChainNotify for OnChainKeyServerSet { + fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { + self.contract.lock().update(enacted, retracted) + } +} + +impl CachedContract { + pub fn new(client: &Arc, key_servers: HashMap) -> Self { + CachedContract { + client: Arc::downgrade(client), + contract_addr: None, + key_servers: key_servers, + } + } + + pub fn update(&mut self, enacted: Vec, _retracted: Vec) { + if let Some(client) = self.client.upgrade() { + let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); + + // new contract installed + if self.contract_addr.as_ref() != new_contract_addr.as_ref() { +println!("=== Installing contract from address: {:?}", new_contract_addr); + self.key_servers = new_contract_addr.map(|contract_addr| { + trace!(target: "secretstore", "Configuring for key server set contract from {}", contract_addr); + + KeyServerSetContract::new(contract_addr) + }) + .map(|contract| { + let mut key_servers = HashMap::new(); + let do_call = |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))); + let key_servers_list = contract.get_key_servers(do_call).wait() + .map_err(|err| { trace!(target: "secretstore", "Error {} reading list of key servers from contract", err); err }) + .unwrap_or_default(); + for key_server in key_servers_list { + let key_server_public = contract.get_key_server_public( + |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))), key_server).wait() + .and_then(|p| if p.len() == 64 { Ok(Public::from_slice(&p)) } else { Err(format!("Invalid public length {}", p.len())) }); + let key_server_ip = contract.get_key_server_address( + |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))), key_server).wait() + .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); + if let (Ok(key_server_public), Ok(key_server_ip)) = (key_server_public, key_server_ip) { + key_servers.insert(key_server_public, key_server_ip); + } + } + key_servers + }) + .unwrap_or_default(); + + return; + } + + // check for events + for enacted_hash in enacted { + let filter = Filter { + from_block: BlockId::Hash(enacted_hash.clone()), + to_block: BlockId::Hash(enacted_hash.clone()), + address: self.contract_addr.clone().map(|a| vec![a]), + topics: vec![Some(vec![*ADDED_EVENT_NAME_HASH]), Some(vec![*REMOVED_EVENT_NAME_HASH])], + limit: None, + }; + println!("=== Number of filtered log entries: {}", client.logs(filter).len()); + } + } + } + + fn get(&self) -> HashMap { + self.key_servers.clone() + } +} diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index f8a74dd1a..235d0edd9 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -21,6 +21,8 @@ extern crate log; extern crate futures; extern crate futures_cpupool; extern crate hyper; +#[macro_use] +extern crate lazy_static; extern crate parking_lot; extern crate rustc_hex; extern crate serde; @@ -56,6 +58,7 @@ mod http_listener; mod key_server; mod key_storage; mod serialization; +mod key_server_set; use std::sync::Arc; use ethcore::client::Client; @@ -68,9 +71,12 @@ pub use traits::{KeyServer}; pub fn start(client: Arc, config: ServiceConfiguration) -> Result, Error> { use std::sync::Arc; - let acl_storage = Arc::new(acl_storage::OnChainAclStorage::new(client)); + let acl_storage = acl_storage::OnChainAclStorage::new(&client); + let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone() + .into_iter() + .map(|a| (a.0, format!("{}:{}", a.1.address, a.1.port).parse().unwrap())).collect()); // TODO: remove after switching to enode:/// let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); - let key_server = key_server::KeyServerImpl::new(&config.cluster_config, acl_storage, key_storage)?; + let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, acl_storage, key_storage)?; let listener = http_listener::KeyServerHttpListener::start(&config.listener_address, key_server)?; Ok(Box::new(listener)) } From 5080cc3c9edd822d3436df989bf453f89336a89d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 19 Jul 2017 12:36:40 +0300 Subject: [PATCH 04/28] update_nodes_set in maintain --- .../src/key_server_cluster/cluster.rs | 70 ++++++++++++------- secret_store/src/key_server_set.rs | 16 ++--- 2 files changed, 52 insertions(+), 34 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index b929e835d..3f381b6f0 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -158,9 +158,17 @@ pub struct ClusterConnections { /// Self node id. pub self_node_id: NodeId, /// All known other key servers. - pub nodes: BTreeMap, + pub key_server_set: Arc, + /// Connections data. + pub data: RwLock, +} + +/// Cluster connections data. +pub struct ClusterConnectionsData { + /// Active key servers set. + pub nodes: BTreeMap, /// Active connections to key servers. - pub connections: RwLock>>, + pub connections: BTreeMap>, } /// Cluster view core. @@ -354,6 +362,7 @@ impl ClusterCore { /// Try to connect to every disconnected node. fn connect_disconnected_nodes(data: Arc) { + data.connections.update_nodes_set(); for (node_id, node_address) in data.connections.disconnected_nodes() { if data.config.allow_connecting_to_higher_nodes || data.self_key_pair.public() < &node_id { ClusterCore::connect(data.clone(), node_address); @@ -665,34 +674,29 @@ impl ClusterCore { impl ClusterConnections { pub fn new(config: &ClusterConfiguration) -> Result { - let mut connections = ClusterConnections { + Ok(ClusterConnections { self_node_id: config.self_key_pair.public().clone(), - nodes: BTreeMap::new(), - connections: RwLock::new(BTreeMap::new()), - }; - - let nodes = config.key_server_set.get(); - for (node_id, socket_address) in nodes.iter().filter(|&(node_id, _)| node_id != config.self_key_pair.public()) { - //let socket_address = make_socket_address(&node_addr, node_port)?; - connections.nodes.insert(node_id.clone(), socket_address.clone()); - } - - Ok(connections) + key_server_set: config.key_server_set.clone(), + data: RwLock::new(ClusterConnectionsData { + nodes: config.key_server_set.get(), + connections: BTreeMap::new(), + }), + }) } pub fn cluster_state(&self) -> ClusterState { ClusterState { - connected: self.connections.read().keys().cloned().collect(), + connected: self.data.read().connections.keys().cloned().collect(), } } pub fn get(&self, node: &NodeId) -> Option> { - self.connections.read().get(node).cloned() + self.data.read().connections.get(node).cloned() } pub fn insert(&self, connection: Arc) -> bool { - let mut connections = self.connections.write(); - if connections.contains_key(connection.node_id()) { + let mut data = self.data.write(); + if data.connections.contains_key(connection.node_id()) { // 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_node_id < connection.node_id() && connection.is_inbound()) @@ -702,13 +706,13 @@ impl ClusterConnections { } trace!(target: "secretstore_net", "{}: inserting connection to {} at {}", self.self_node_id, connection.node_id(), connection.node_address()); - connections.insert(connection.node_id().clone(), connection); + data.connections.insert(connection.node_id().clone(), connection); true } pub fn remove(&self, node: &NodeId, is_inbound: bool) { - let mut connections = self.connections.write(); - if let Entry::Occupied(entry) = connections.entry(node.clone()) { + let mut data = self.data.write(); + if let Entry::Occupied(entry) = data.connections.entry(node.clone()) { if entry.get().is_inbound() != is_inbound { return; } @@ -719,20 +723,34 @@ impl ClusterConnections { } pub fn connected_nodes(&self) -> BTreeSet { - self.connections.read().keys().cloned().collect() + self.data.read().connections.keys().cloned().collect() } pub fn active_connections(&self)-> Vec> { - self.connections.read().values().cloned().collect() + self.data.read().connections.values().cloned().collect() } pub fn disconnected_nodes(&self) -> BTreeMap { - let connections = self.connections.read(); - self.nodes.iter() - .filter(|&(node_id, _)| !connections.contains_key(node_id)) + let data = self.data.read(); + data.nodes.iter() + .filter(|&(node_id, _)| !data.connections.contains_key(node_id)) .map(|(node_id, node_address)| (node_id.clone(), node_address.clone())) .collect() } + + pub fn update_nodes_set(&self) { + let mut data = self.data.write(); + let new_nodes = self.key_server_set.get(); + for obsolete_node in data.nodes.keys().cloned().collect::>() { + if !new_nodes.contains_key(&obsolete_node) { + data.nodes.remove(&obsolete_node); + data.connections.remove(&obsolete_node); + } + } + for (new_node_public, new_node_addr) in new_nodes { + data.nodes.insert(new_node_public, new_node_addr); + } + } } impl ClusterData { diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index 22ffbdb07..eecfa3091 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -16,7 +16,7 @@ use std::sync::{Arc, Weak}; use std::net::SocketAddr; -use std::collections::HashMap; +use std::collections::BTreeMap; use futures::{future, Future}; use parking_lot::Mutex; use ethcore::filter::Filter; @@ -39,7 +39,7 @@ lazy_static! { /// Key Server set pub trait KeyServerSet: Send + Sync { /// Get set of configured key servers - fn get(&self) -> HashMap; + fn get(&self) -> BTreeMap; } /// On-chain Key Server set implementation. @@ -55,11 +55,11 @@ struct CachedContract { /// Contract address. contract_addr: Option
, /// Active set of key servers. - key_servers: HashMap, + key_servers: BTreeMap, } impl OnChainKeyServerSet { - pub fn new(client: &Arc, key_servers: HashMap) -> Arc { + pub fn new(client: &Arc, key_servers: BTreeMap) -> Arc { let key_server_set = Arc::new(OnChainKeyServerSet { contract: Mutex::new(CachedContract::new(client, key_servers)), }); @@ -69,7 +69,7 @@ impl OnChainKeyServerSet { } impl KeyServerSet for OnChainKeyServerSet { - fn get(&self) -> HashMap { + fn get(&self) -> BTreeMap { self.contract.lock().get() } } @@ -81,7 +81,7 @@ impl ChainNotify for OnChainKeyServerSet { } impl CachedContract { - pub fn new(client: &Arc, key_servers: HashMap) -> Self { + pub fn new(client: &Arc, key_servers: BTreeMap) -> Self { CachedContract { client: Arc::downgrade(client), contract_addr: None, @@ -102,7 +102,7 @@ println!("=== Installing contract from address: {:?}", new_contract_addr); KeyServerSetContract::new(contract_addr) }) .map(|contract| { - let mut key_servers = HashMap::new(); + let mut key_servers = BTreeMap::new(); let do_call = |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))); let key_servers_list = contract.get_key_servers(do_call).wait() .map_err(|err| { trace!(target: "secretstore", "Error {} reading list of key servers from contract", err); err }) @@ -139,7 +139,7 @@ println!("=== Installing contract from address: {:?}", new_contract_addr); } } - fn get(&self) -> HashMap { + fn get(&self) -> BTreeMap { self.key_servers.clone() } } From 7664ff5acd26d781eb9c4d5708d37735b9da01fc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 19 Jul 2017 15:14:37 +0300 Subject: [PATCH 05/28] do not connect to self --- .../src/key_server_cluster/cluster.rs | 9 ++- secret_store/src/key_server_set.rs | 74 +++++++++++-------- 2 files changed, 51 insertions(+), 32 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 3f381b6f0..87c602eae 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -674,11 +674,14 @@ impl ClusterCore { impl ClusterConnections { pub fn new(config: &ClusterConfiguration) -> Result { + let mut nodes = config.key_server_set.get(); + nodes.remove(config.self_key_pair.public()); + Ok(ClusterConnections { self_node_id: config.self_key_pair.public().clone(), key_server_set: config.key_server_set.clone(), data: RwLock::new(ClusterConnectionsData { - nodes: config.key_server_set.get(), + nodes: nodes, connections: BTreeMap::new(), }), }) @@ -740,7 +743,9 @@ impl ClusterConnections { pub fn update_nodes_set(&self) { let mut data = self.data.write(); - let new_nodes = self.key_server_set.get(); + let mut new_nodes = self.key_server_set.get(); + new_nodes.remove(&self.self_node_id); + for obsolete_node in data.nodes.keys().cloned().collect::>() { if !new_nodes.contains_key(&obsolete_node) { data.nodes.remove(&obsolete_node); diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index eecfa3091..f9ec120fd 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -60,8 +60,15 @@ struct CachedContract { impl OnChainKeyServerSet { pub fn new(client: &Arc, key_servers: BTreeMap) -> Arc { + let mut cached_contract = CachedContract::new(client, key_servers); + let key_server_contract_address = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); + // only initialize from contract if it is installed. otherwise - use default nodes + if key_server_contract_address.is_some() { + cached_contract.read_from_registry(&*client, key_server_contract_address); + } + let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(CachedContract::new(client, key_servers)), + contract: Mutex::new(cached_contract), }); client.add_notify(key_server_set.clone()); key_server_set @@ -76,6 +83,7 @@ impl KeyServerSet for OnChainKeyServerSet { impl ChainNotify for OnChainKeyServerSet { fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { +println!("=== new_blocks: imported {}, invalid: {}, enactd: {}, retracted: {}, sealed: {}, proposed: {}", _imported.len(), _invalid.len(), enacted.len(), retracted.len(), _sealed.len(), _proposed.len()); self.contract.lock().update(enacted, retracted) } } @@ -92,36 +100,10 @@ impl CachedContract { pub fn update(&mut self, enacted: Vec, _retracted: Vec) { if let Some(client) = self.client.upgrade() { let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); - +println!("=== Registry address = {:?}", new_contract_addr); // new contract installed if self.contract_addr.as_ref() != new_contract_addr.as_ref() { -println!("=== Installing contract from address: {:?}", new_contract_addr); - self.key_servers = new_contract_addr.map(|contract_addr| { - trace!(target: "secretstore", "Configuring for key server set contract from {}", contract_addr); - - KeyServerSetContract::new(contract_addr) - }) - .map(|contract| { - let mut key_servers = BTreeMap::new(); - let do_call = |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))); - let key_servers_list = contract.get_key_servers(do_call).wait() - .map_err(|err| { trace!(target: "secretstore", "Error {} reading list of key servers from contract", err); err }) - .unwrap_or_default(); - for key_server in key_servers_list { - let key_server_public = contract.get_key_server_public( - |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))), key_server).wait() - .and_then(|p| if p.len() == 64 { Ok(Public::from_slice(&p)) } else { Err(format!("Invalid public length {}", p.len())) }); - let key_server_ip = contract.get_key_server_address( - |a, d| future::done(self.client.upgrade().ok_or("Calling contract without client".into()).and_then(|c| c.call_contract(BlockId::Latest, a, d))), key_server).wait() - .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); - if let (Ok(key_server_public), Ok(key_server_ip)) = (key_server_public, key_server_ip) { - key_servers.insert(key_server_public, key_server_ip); - } - } - key_servers - }) - .unwrap_or_default(); - + self.read_from_registry(&*client, new_contract_addr); return; } @@ -139,7 +121,39 @@ println!("=== Installing contract from address: {:?}", new_contract_addr); } } - fn get(&self) -> BTreeMap { + pub fn get(&self) -> BTreeMap { self.key_servers.clone() } + + fn read_from_registry(&mut self, client: &Client, new_contract_address: Option
) { +println!("=== Installing contract from address: {:?}", new_contract_address); + self.key_servers = new_contract_address.map(|contract_addr| { + trace!(target: "secretstore", "Configuring for key server set contract from {}", contract_addr); + + KeyServerSetContract::new(contract_addr) + }) + .map(|contract| { + let mut key_servers = BTreeMap::new(); + let do_call = |a, d| future::done(client.call_contract(BlockId::Latest, a, d)); + let key_servers_list = contract.get_key_servers(do_call).wait() + .map_err(|err| { trace!(target: "secretstore", "Error {} reading list of key servers from contract", err); err }) + .unwrap_or_default(); + for key_server in key_servers_list { + let key_server_public = contract.get_key_server_public( + |a, d| future::done(client.call_contract(BlockId::Latest, a, d)), key_server).wait() + .and_then(|p| if p.len() == 64 { Ok(Public::from_slice(&p)) } else { Err(format!("Invalid public length {}", p.len())) }); + let key_server_ip = contract.get_key_server_address( + |a, d| future::done(client.call_contract(BlockId::Latest, a, d)), key_server).wait() + .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); + if let (Ok(key_server_public), Ok(key_server_ip)) = (key_server_public, key_server_ip) { +println!("=== PARSED {:?} {:?}", key_server_public, key_server_ip); + key_servers.insert(key_server_public, key_server_ip); + } +else { println!("=== ERROR parsing"); } + } + key_servers + }) + .unwrap_or_default(); + self.contract_addr = new_contract_address; + } } From 80b9e931f55f06034fc597a49174d13683f6288e Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 20 Jul 2017 12:19:29 +0300 Subject: [PATCH 06/28] fixed connection establishing --- .../src/key_server_cluster/cluster.rs | 44 ++++++++++++++++--- .../src/key_server_cluster/io/handshake.rs | 13 +++--- .../net/accept_connection.rs | 7 ++- secret_store/src/key_server_set.rs | 37 +++++++++------- 4 files changed, 67 insertions(+), 34 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 87c602eae..ff9ff9b22 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -289,8 +289,7 @@ impl ClusterCore { /// Accept connection future. fn accept_connection_future(handle: &Handle, data: Arc, stream: TcpStream, node_address: SocketAddr) -> BoxedEmptyFuture { - let disconnected_nodes = data.connections.disconnected_nodes().keys().cloned().collect(); - net_accept_connection(node_address, stream, handle, data.self_key_pair.clone(), disconnected_nodes) + net_accept_connection(node_address, stream, handle, data.self_key_pair.clone()) .then(move |result| ClusterCore::process_connection_result(data, true, result)) .then(|_| finished(())) .boxed() @@ -381,14 +380,16 @@ impl ClusterCore { finished(Ok(())).boxed() } }, - Ok(DeadlineStatus::Meet(Err(_))) => { + Ok(DeadlineStatus::Meet(Err(err))) => { + warn!(target: "secretstore_net", "{}: protocol error {} when establishind connection", data.self_key_pair.public(), err); finished(Ok(())).boxed() }, Ok(DeadlineStatus::Timeout) => { + warn!(target: "secretstore_net", "{}: timeout when establishind connection", data.self_key_pair.public()); finished(Ok(())).boxed() }, - Err(_) => { - // network error + Err(err) => { + warn!(target: "secretstore_net", "{}: network error {} when establishind connection", data.self_key_pair.public(), err); finished(Ok(())).boxed() }, } @@ -699,6 +700,12 @@ impl ClusterConnections { pub fn insert(&self, connection: Arc) -> bool { let mut data = self.data.write(); + if !data.nodes.contains_key(connection.node_id()) { + // incoming connections are checked here + trace!(target: "secretstore_net", "{}: ignoring unknown connection from {} at {}", self.self_node_id, connection.node_id(), connection.node_address()); + debug_assert!(connection.is_inbound()); + return false; + } if data.connections.contains_key(connection.node_id()) { // we have already connected to the same node // the agreement is that node with lower id must establish connection to node with higher id @@ -746,14 +753,37 @@ impl ClusterConnections { let mut new_nodes = self.key_server_set.get(); new_nodes.remove(&self.self_node_id); + let mut num_added_nodes = 0; + let mut num_removed_nodes = 0; + let mut num_changed_nodes = 0; + for obsolete_node in data.nodes.keys().cloned().collect::>() { if !new_nodes.contains_key(&obsolete_node) { + if let Entry::Occupied(entry) = data.connections.entry(obsolete_node) { + trace!(target: "secretstore_net", "{}: removing connection to {} at {}", self.self_node_id, entry.get().node_id(), entry.get().node_address()); + entry.remove(); + } + data.nodes.remove(&obsolete_node); - data.connections.remove(&obsolete_node); + num_removed_nodes += 1; } } + for (new_node_public, new_node_addr) in new_nodes { - data.nodes.insert(new_node_public, new_node_addr); + match data.nodes.insert(new_node_public, new_node_addr) { + None => num_added_nodes += 1, + Some(old_node_addr) => if new_node_addr != old_node_addr { + if let Entry::Occupied(entry) = data.connections.entry(new_node_public) { + trace!(target: "secretstore_net", "{}: removing connection to {} at {}", self.self_node_id, entry.get().node_id(), entry.get().node_address()); + entry.remove(); + } + num_changed_nodes += 1; + }, + } + } + + if num_added_nodes != 0 || num_removed_nodes != 0 || num_changed_nodes != 0 { + trace!(target: "secretstore_net", "{}: updated nodes set: removed {}, added {}, changed {}", self.self_node_id, num_removed_nodes, num_added_nodes, num_changed_nodes); } } } diff --git a/secret_store/src/key_server_cluster/io/handshake.rs b/secret_store/src/key_server_cluster/io/handshake.rs index 38d8a6ac1..90f4d04cc 100644 --- a/secret_store/src/key_server_cluster/io/handshake.rs +++ b/secret_store/src/key_server_cluster/io/handshake.rs @@ -45,7 +45,7 @@ pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Resul state: state, self_key_pair: self_key_pair, self_confirmation_plain: self_confirmation_plain.unwrap_or(Default::default()), - trusted_nodes: trusted_nodes, + trusted_nodes: Some(trusted_nodes), other_node_id: None, other_confirmation_plain: None, shared_key: None, @@ -53,7 +53,7 @@ pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Resul } /// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn accept_handshake(a: A, self_key_pair: KeyPair) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let (error, state) = match self_confirmation_plain.clone() { Ok(_) => (None, HandshakeState::ReceivePublicKey(read_message(a))), @@ -66,7 +66,7 @@ pub fn accept_handshake(a: A, self_key_pair: KeyPair, trusted_nodes: BTreeSet state: state, self_key_pair: self_key_pair, self_confirmation_plain: self_confirmation_plain.unwrap_or(Default::default()), - trusted_nodes: trusted_nodes, + trusted_nodes: None, other_node_id: None, other_confirmation_plain: None, shared_key: None, @@ -89,7 +89,7 @@ pub struct Handshake { state: HandshakeState, self_key_pair: KeyPair, self_confirmation_plain: H256, - trusted_nodes: BTreeSet, + trusted_nodes: Option>, other_node_id: Option, other_confirmation_plain: Option, shared_key: Option, @@ -172,7 +172,8 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { Err(err) => return Ok((stream, Err(err.into())).into()), }; - if !self.trusted_nodes.contains(&*message.node_id) { + if !self.trusted_nodes.as_ref().map(|tn| tn.contains(&*message.node_id)).unwrap_or(true) { +println!("=== HANDSHAKE - INVALID NODE: self.trusted_nodes = {:?}, message.node_id = {:?}", self.trusted_nodes, message.node_id); return Ok((stream, Err(Error::InvalidNodeId)).into()); } @@ -300,7 +301,7 @@ mod tests { let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); let shared_key = compute_shared_key(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap(); - let mut handshake = accept_handshake(io, self_key_pair, trusted_nodes); + let mut handshake = accept_handshake(io, self_key_pair); handshake.set_self_confirmation_plain(self_confirmation_plain); let handshake_result = handshake.wait().unwrap(); diff --git a/secret_store/src/key_server_cluster/net/accept_connection.rs b/secret_store/src/key_server_cluster/net/accept_connection.rs index 0daa8b2da..339625f3f 100644 --- a/secret_store/src/key_server_cluster/net/accept_connection.rs +++ b/secret_store/src/key_server_cluster/net/accept_connection.rs @@ -17,19 +17,18 @@ use std::io; use std::net::SocketAddr; use std::time::Duration; -use std::collections::BTreeSet; use futures::{Future, Poll}; use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; use ethkey::KeyPair; -use key_server_cluster::{Error, NodeId}; +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(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Deadline { +pub fn accept_connection(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: KeyPair) -> Deadline { let accept = AcceptConnection { - handshake: accept_handshake(stream, self_key_pair, trusted_nodes), + handshake: accept_handshake(stream, self_key_pair), address: address, }; diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index f9ec120fd..7b0bd5c9f 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -28,8 +28,8 @@ use types::all::Public; const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; // TODO: ethabi should be able to generate this. -const ADDED_EVENT_NAME: &'static [u8] = &*b"KeyServerAdded()"; -const REMOVED_EVENT_NAME: &'static [u8] = &*b"KeyServerRemoved()"; +const ADDED_EVENT_NAME: &'static [u8] = &*b"KeyServerAdded(address)"; +const REMOVED_EVENT_NAME: &'static [u8] = &*b"KeyServerRemoved(address)"; lazy_static! { static ref ADDED_EVENT_NAME_HASH: H256 = ADDED_EVENT_NAME.sha3(); @@ -83,7 +83,6 @@ impl KeyServerSet for OnChainKeyServerSet { impl ChainNotify for OnChainKeyServerSet { fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { -println!("=== new_blocks: imported {}, invalid: {}, enactd: {}, retracted: {}, sealed: {}, proposed: {}", _imported.len(), _invalid.len(), enacted.len(), retracted.len(), _sealed.len(), _proposed.len()); self.contract.lock().update(enacted, retracted) } } @@ -97,26 +96,33 @@ impl CachedContract { } } - pub fn update(&mut self, enacted: Vec, _retracted: Vec) { + pub fn update(&mut self, enacted: Vec, retracted: Vec) { if let Some(client) = self.client.upgrade() { let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); -println!("=== Registry address = {:?}", new_contract_addr); + // new contract installed if self.contract_addr.as_ref() != new_contract_addr.as_ref() { self.read_from_registry(&*client, new_contract_addr); return; } - // check for events - for enacted_hash in enacted { - let filter = Filter { - from_block: BlockId::Hash(enacted_hash.clone()), - to_block: BlockId::Hash(enacted_hash.clone()), + // check for contract events + let is_set_changed = self.contract_addr.is_some() && enacted.iter() + .chain(retracted.iter()) + .any(|block_hash| !client.logs(Filter { + from_block: BlockId::Hash(block_hash.clone()), + to_block: BlockId::Hash(block_hash.clone()), address: self.contract_addr.clone().map(|a| vec![a]), - topics: vec![Some(vec![*ADDED_EVENT_NAME_HASH]), Some(vec![*REMOVED_EVENT_NAME_HASH])], - limit: None, - }; - println!("=== Number of filtered log entries: {}", client.logs(filter).len()); + topics: vec![ + Some(vec![*ADDED_EVENT_NAME_HASH, *REMOVED_EVENT_NAME_HASH]), + None, + None, + None, + ], + limit: Some(1), + }).is_empty()); + if is_set_changed { + self.read_from_registry(&*client, new_contract_addr); } } } @@ -126,7 +132,6 @@ println!("=== Registry address = {:?}", new_contract_addr); } fn read_from_registry(&mut self, client: &Client, new_contract_address: Option
) { -println!("=== Installing contract from address: {:?}", new_contract_address); self.key_servers = new_contract_address.map(|contract_addr| { trace!(target: "secretstore", "Configuring for key server set contract from {}", contract_addr); @@ -146,10 +151,8 @@ println!("=== Installing contract from address: {:?}", new_contract_address); |a, d| future::done(client.call_contract(BlockId::Latest, a, d)), key_server).wait() .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); if let (Ok(key_server_public), Ok(key_server_ip)) = (key_server_public, key_server_ip) { -println!("=== PARSED {:?} {:?}", key_server_public, key_server_ip); key_servers.insert(key_server_public, key_server_ip); } -else { println!("=== ERROR parsing"); } } key_servers }) From 9a9c4f6ad6ee6e6beda9820ade823191987b98f9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 20 Jul 2017 12:25:41 +0300 Subject: [PATCH 07/28] removed println --- secret_store/src/key_server_cluster/io/handshake.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/secret_store/src/key_server_cluster/io/handshake.rs b/secret_store/src/key_server_cluster/io/handshake.rs index 90f4d04cc..df8f6cbf7 100644 --- a/secret_store/src/key_server_cluster/io/handshake.rs +++ b/secret_store/src/key_server_cluster/io/handshake.rs @@ -173,7 +173,6 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { }; if !self.trusted_nodes.as_ref().map(|tn| tn.contains(&*message.node_id)).unwrap_or(true) { -println!("=== HANDSHAKE - INVALID NODE: self.trusted_nodes = {:?}, message.node_id = {:?}", self.trusted_nodes, message.node_id); return Ok((stream, Err(Error::InvalidNodeId)).into()); } From a35db9f45476d44d77870f5cd1e52764a7676d07 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 20 Jul 2017 12:55:52 +0300 Subject: [PATCH 08/28] improved KeyServerSet tracing --- secret_store/src/key_server_set.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index 7b0bd5c9f..6b58e54de 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -27,8 +27,9 @@ use types::all::Public; const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; -// TODO: ethabi should be able to generate this. +/// Key server has been added to the set. const ADDED_EVENT_NAME: &'static [u8] = &*b"KeyServerAdded(address)"; +/// Key server has been removed from the set. const REMOVED_EVENT_NAME: &'static [u8] = &*b"KeyServerRemoved(address)"; lazy_static! { @@ -63,6 +64,7 @@ impl OnChainKeyServerSet { let mut cached_contract = CachedContract::new(client, key_servers); let key_server_contract_address = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); // only initialize from contract if it is installed. otherwise - use default nodes + // once the contract is installed, all default nodes are lost (if not in the contract' set) if key_server_contract_address.is_some() { cached_contract.read_from_registry(&*client, key_server_contract_address); } @@ -100,7 +102,7 @@ impl CachedContract { if let Some(client) = self.client.upgrade() { let new_contract_addr = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); - // new contract installed + // new contract installed => read nodes set from the contract if self.contract_addr.as_ref() != new_contract_addr.as_ref() { self.read_from_registry(&*client, new_contract_addr); return; @@ -121,6 +123,7 @@ impl CachedContract { ], limit: Some(1), }).is_empty()); + // to simplify processing - just re-read the whole nodes set from the contract if is_set_changed { self.read_from_registry(&*client, new_contract_addr); } @@ -150,8 +153,12 @@ impl CachedContract { let key_server_ip = contract.get_key_server_address( |a, d| future::done(client.call_contract(BlockId::Latest, a, d)), key_server).wait() .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); - if let (Ok(key_server_public), Ok(key_server_ip)) = (key_server_public, key_server_ip) { - key_servers.insert(key_server_public, key_server_ip); + + // only add successfully parsed nodes + match (key_server_public, key_server_ip) { + (Ok(key_server_public), Ok(key_server_ip)) => { key_servers.insert(key_server_public, key_server_ip); }, + (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 From b9ad093d06fc11f1dba8d30d5986a4aa8dd93cbe Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 20 Jul 2017 13:15:16 +0300 Subject: [PATCH 09/28] moved parsing to KeyServerSet --- secret_store/src/key_server_set.rs | 22 ++++++++++++++-------- secret_store/src/lib.rs | 4 +--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index 6b58e54de..302f76196 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -23,7 +23,7 @@ use ethcore::filter::Filter; use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use native_contracts::KeyServerSet as KeyServerSetContract; use util::{H256, Address, Bytes, Hashable}; -use types::all::Public; +use types::all::{Error, Public, NodeAddress}; const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; @@ -60,8 +60,8 @@ struct CachedContract { } impl OnChainKeyServerSet { - pub fn new(client: &Arc, key_servers: BTreeMap) -> Arc { - let mut cached_contract = CachedContract::new(client, key_servers); + pub fn new(client: &Arc, key_servers: BTreeMap) -> Result, Error> { + let mut cached_contract = CachedContract::new(client, key_servers)?; let key_server_contract_address = client.registry_address(KEY_SERVER_SET_CONTRACT_REGISTRY_NAME.to_owned()); // only initialize from contract if it is installed. otherwise - use default nodes // once the contract is installed, all default nodes are lost (if not in the contract' set) @@ -73,7 +73,7 @@ impl OnChainKeyServerSet { contract: Mutex::new(cached_contract), }); client.add_notify(key_server_set.clone()); - key_server_set + Ok(key_server_set) } } @@ -90,12 +90,18 @@ impl ChainNotify for OnChainKeyServerSet { } impl CachedContract { - pub fn new(client: &Arc, key_servers: BTreeMap) -> Self { - CachedContract { + pub fn new(client: &Arc, key_servers: BTreeMap) -> Result { + Ok(CachedContract { client: Arc::downgrade(client), contract_addr: None, - key_servers: key_servers, - } + key_servers: 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>>()?, + }) } pub fn update(&mut self, enacted: Vec, retracted: Vec) { diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 235d0edd9..9750f7223 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -72,9 +72,7 @@ pub fn start(client: Arc, config: ServiceConfiguration) -> Result Date: Thu, 20 Jul 2017 13:28:31 +0300 Subject: [PATCH 10/28] re-read only when blockchain is changed --- secret_store/src/acl_storage.rs | 6 ++++-- secret_store/src/key_server_set.rs | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 7ae72cbfc..37d5bcd25 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -64,8 +64,10 @@ impl AclStorage for OnChainAclStorage { } impl ChainNotify for OnChainAclStorage { - fn new_blocks(&self, _imported: Vec, _invalid: Vec, _enacted: Vec, _retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - self.contract.lock().update() + fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { + if !enacted.is_empty() || !retracted.is_empty() { + self.contract.lock().update() + } } } diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index 302f76196..47f033db1 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -85,7 +85,9 @@ impl KeyServerSet for OnChainKeyServerSet { impl ChainNotify for OnChainKeyServerSet { fn new_blocks(&self, _imported: Vec, _invalid: Vec, enacted: Vec, retracted: Vec, _sealed: Vec, _proposed: Vec, _duration: u64) { - self.contract.lock().update(enacted, retracted) + if !enacted.is_empty() || !retracted.is_empty() { + self.contract.lock().update(enacted, retracted) + } } } From 023e5b4b9086c85405746dc38b95fcd8c9786746 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 21 Jul 2017 10:49:10 +0300 Subject: [PATCH 11/28] do not try to connect if not a part of cluster --- secret_store/src/key_server_cluster/cluster.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index ff9ff9b22..831867029 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -751,7 +751,11 @@ impl ClusterConnections { pub fn update_nodes_set(&self) { let mut data = self.data.write(); let mut new_nodes = self.key_server_set.get(); - new_nodes.remove(&self.self_node_id); + // we do not need to connect to self + // + we do not need to try to connect to any other node if we are not the part of a cluster + if new_nodes.remove(&self.self_node_id).is_none() { + new_nodes.clear(); + } let mut num_added_nodes = 0; let mut num_removed_nodes = 0; From 5fb9652af56410a805da24e4f2177578f062be53 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 21 Jul 2017 11:25:26 +0300 Subject: [PATCH 12/28] improved logging --- secret_store/src/key_server_cluster/cluster.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 831867029..649ad4bc9 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -715,7 +715,8 @@ impl ClusterConnections { } } - trace!(target: "secretstore_net", "{}: inserting connection to {} at {}", self.self_node_id, connection.node_id(), connection.node_address()); + trace!(target: "secretstore_net", "{}: inserting connection to {} at {}. Connected to {} of {} nodes", + self.self_node_id, connection.node_id(), connection.node_address(), data.connections.len() + 1, data.nodes.len()); data.connections.insert(connection.node_id().clone(), connection); true } @@ -787,7 +788,8 @@ impl ClusterConnections { } if num_added_nodes != 0 || num_removed_nodes != 0 || num_changed_nodes != 0 { - trace!(target: "secretstore_net", "{}: updated nodes set: removed {}, added {}, changed {}", self.self_node_id, num_removed_nodes, num_added_nodes, num_changed_nodes); + trace!(target: "secretstore_net", "{}: updated nodes set: removed {}, added {}, changed {}. Connected to {} of {} nodes", + self.self_node_id, num_removed_nodes, num_added_nodes, num_changed_nodes, data.connections.len(), data.nodes.len()); } } } From b31b067743a62068425a9e87eee6edd9864c83f5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 24 Jul 2017 12:36:31 +0300 Subject: [PATCH 13/28] fixed tests --- secret_store/src/key_server.rs | 8 +++++- .../src/key_server_cluster/cluster.rs | 10 +++---- secret_store/src/key_server_cluster/mod.rs | 2 ++ secret_store/src/key_server_set.rs | 27 +++++++++++++++++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 969782ca2..c83e460f3 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -192,10 +192,13 @@ impl Drop for KeyServerCore { pub mod tests { use std::time; use std::sync::Arc; + use std::net::SocketAddr; + use std::collections::BTreeMap; use ethcrypto; use ethkey::{self, Secret, Random, Generator}; use acl_storage::tests::DummyAclStorage; use key_storage::tests::DummyKeyStorage; + use key_server_set::tests::MapKeyServerSet; use key_server_cluster::math; use util::H256; use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, @@ -253,8 +256,11 @@ pub mod tests { })).collect(), allow_connecting_to_higher_nodes: 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_servers: Vec<_> = configs.into_iter().map(|cfg| - KeyServerImpl::new(&cfg, Arc::new(DummyAclStorage::default()), Arc::new(DummyKeyStorage::default())).unwrap() + KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(key_servers_set.clone())), Arc::new(DummyAclStorage::default()), Arc::new(DummyKeyStorage::default())).unwrap() ).collect(); // wait until connections are established. It is fast => do not bother with events here diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 649ad4bc9..d77a82431 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -989,7 +989,7 @@ pub mod tests { use parking_lot::Mutex; use tokio_core::reactor::Core; use ethkey::{Random, Generator, Public}; - use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage}; + use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet}; use key_server_cluster::message::Message; use key_server_cluster::cluster::{Cluster, ClusterCore, ClusterConfiguration}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; @@ -1059,7 +1059,7 @@ pub mod tests { } pub fn all_connections_established(cluster: &Arc) -> bool { - cluster.config().nodes.keys() + cluster.config().key_server_set.get().keys() .filter(|p| *p != cluster.config().self_key_pair.public()) .all(|p| cluster.connection(p).is_some()) } @@ -1070,9 +1070,9 @@ pub mod tests { threads: 1, self_key_pair: key_pairs[i].clone(), listen_address: ("127.0.0.1".to_owned(), ports_begin + i as u16), - nodes: key_pairs.iter().enumerate() - .map(|(j, kp)| (kp.public().clone(), ("127.0.0.1".into(), ports_begin + j as u16))) - .collect(), + key_server_set: Arc::new(MapKeyServerSet::new(key_pairs.iter().enumerate() + .map(|(j, kp)| (kp.public().clone(), format!("127.0.0.1:{}", ports_begin + j as u16).parse().unwrap())) + .collect())), allow_connecting_to_higher_nodes: false, key_storage: Arc::new(DummyKeyStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()), diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 11c32d528..8f6ae4add 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -34,6 +34,8 @@ pub use self::decryption_session::Session as DecryptionSession; pub use super::key_storage::tests::DummyKeyStorage; #[cfg(test)] pub use super::acl_storage::tests::DummyAclStorage; +#[cfg(test)] +pub use super::key_server_set::tests::MapKeyServerSet; pub type SessionId = ServerKeyId; diff --git a/secret_store/src/key_server_set.rs b/secret_store/src/key_server_set.rs index 47f033db1..e17dceed5 100644 --- a/secret_store/src/key_server_set.rs +++ b/secret_store/src/key_server_set.rs @@ -175,3 +175,30 @@ impl CachedContract { self.contract_addr = new_contract_address; } } + +#[cfg(test)] +pub mod tests { + use std::collections::BTreeMap; + use std::net::SocketAddr; + use ethkey::Public; + use super::KeyServerSet; + + #[derive(Default)] + pub struct MapKeyServerSet { + nodes: BTreeMap, + } + + impl MapKeyServerSet { + pub fn new(nodes: BTreeMap) -> Self { + MapKeyServerSet { + nodes: nodes, + } + } + } + + impl KeyServerSet for MapKeyServerSet { + fn get(&self) -> BTreeMap { + self.nodes.clone() + } + } +} From 45f2b824110e1f7378217e8d6b73cffd261b6755 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 09:24:54 +0300 Subject: [PATCH 14/28] NodeKeyPAir trait --- secret_store/src/key_server.rs | 20 +++--- .../src/key_server_cluster/cluster.rs | 10 +-- .../src/key_server_cluster/io/handshake.rs | 48 +++++++------ .../src/key_server_cluster/io/message.rs | 15 ++--- secret_store/src/key_server_cluster/io/mod.rs | 2 +- secret_store/src/key_server_cluster/mod.rs | 3 + .../net/accept_connection.rs | 6 +- .../src/key_server_cluster/net/connect.rs | 8 +-- secret_store/src/key_storage.rs | 2 +- secret_store/src/lib.rs | 8 ++- secret_store/src/node_key_pair.rs | 67 +++++++++++++++++++ secret_store/src/traits.rs | 12 ++++ secret_store/src/types/all.rs | 2 - 13 files changed, 146 insertions(+), 57 deletions(-) create mode 100644 secret_store/src/node_key_pair.rs diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index c83e460f3..0944dd37c 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -26,7 +26,7 @@ use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; use super::key_server_set::KeyServerSet; use key_server_cluster::{math, ClusterCore}; -use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; +use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair}; use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, ClusterConfiguration, MessageHash, EncryptedMessageSignature}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration}; @@ -45,9 +45,9 @@ pub struct KeyServerCore { impl KeyServerImpl { /// Create new key server instance - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, acl_storage: Arc, key_storage: Arc) -> Result { + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc) -> Result { Ok(KeyServerImpl { - data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, acl_storage, key_storage)?)), + data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, self_key_pair, acl_storage, key_storage)?)), }) } @@ -144,10 +144,10 @@ impl MessageSigner for KeyServerImpl { } impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, acl_storage: Arc, key_storage: Arc) -> Result { + pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, acl_storage: Arc, key_storage: Arc) -> Result { let config = NetClusterConfiguration { threads: config.threads, - self_key_pair: ethkey::KeyPair::from_secret_slice(&config.self_private)?, + self_key_pair: self_key_pair, listen_address: (config.listener_address.address.clone(), config.listener_address.port), key_server_set: key_server_set, allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes, @@ -198,6 +198,7 @@ pub mod tests { use ethkey::{self, Secret, Random, Generator}; use acl_storage::tests::DummyAclStorage; use key_storage::tests::DummyKeyStorage; + use node_key_pair::PlainNodeKeyPair; use key_server_set::tests::MapKeyServerSet; use key_server_cluster::math; use util::H256; @@ -244,7 +245,7 @@ pub mod tests { let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); let configs: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { threads: 1, - self_private: (***key_pairs[i].secret()).into(), +// self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), listener_address: NodeAddress { address: "127.0.0.1".into(), port: start_port + (i as u16), @@ -259,8 +260,11 @@ pub mod tests { let key_servers_set: BTreeMap = configs[0].nodes.iter() .map(|(k, a)| (k.clone(), format!("{}:{}", a.address, a.port).parse().unwrap())) .collect(); - let key_servers: Vec<_> = configs.into_iter().map(|cfg| - KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(key_servers_set.clone())), Arc::new(DummyAclStorage::default()), Arc::new(DummyKeyStorage::default())).unwrap() + let key_servers: Vec<_> = configs.into_iter().enumerate().map(|(i, cfg)| + KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(key_servers_set.clone())), + Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), + Arc::new(DummyAclStorage::default()), + Arc::new(DummyKeyStorage::default())).unwrap() ).collect(); // wait until connections are established. It is fast => do not bother with events here diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index d77a82431..155dd4a01 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -28,7 +28,7 @@ use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; use util::H256; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, KeyServerSet}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, KeyServerSet, NodeKeyPair}; use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, DecryptionSessionWrapper, SigningSessionWrapper}; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, @@ -99,7 +99,7 @@ pub struct ClusterConfiguration { /// Allow connecting to 'higher' nodes. pub allow_connecting_to_higher_nodes: bool, /// KeyPair this node holds. - pub self_key_pair: KeyPair, + pub self_key_pair: Arc, /// Interface to listen to. pub listen_address: (String, u16), /// Cluster nodes set. @@ -146,7 +146,7 @@ pub struct ClusterData { /// Handle to the cpu thread pool. pool: CpuPool, /// KeyPair this node holds. - self_key_pair: KeyPair, + self_key_pair: Arc, /// Connections data. connections: ClusterConnections, /// Active sessions data. @@ -989,7 +989,7 @@ pub mod tests { use parking_lot::Mutex; use tokio_core::reactor::Core; use ethkey::{Random, Generator, Public}; - use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet}; + use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet, PlainNodeKeyPair}; use key_server_cluster::message::Message; use key_server_cluster::cluster::{Cluster, ClusterCore, ClusterConfiguration}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; @@ -1068,7 +1068,7 @@ pub mod tests { let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); let cluster_params: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { threads: 1, - self_key_pair: key_pairs[i].clone(), + self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), listen_address: ("127.0.0.1".to_owned(), ports_begin + i as u16), key_server_set: Arc::new(MapKeyServerSet::new(key_pairs.iter().enumerate() .map(|(j, kp)| (kp.public().clone(), format!("127.0.0.1:{}", ports_begin + j as u16).parse().unwrap())) diff --git a/secret_store/src/key_server_cluster/io/handshake.rs b/secret_store/src/key_server_cluster/io/handshake.rs index df8f6cbf7..bf52ab798 100644 --- a/secret_store/src/key_server_cluster/io/handshake.rs +++ b/secret_store/src/key_server_cluster/io/handshake.rs @@ -15,24 +15,25 @@ // along with Parity. If not, see . use std::io; +use std::sync::Arc; use std::collections::BTreeSet; use futures::{Future, Poll, Async}; use tokio_io::{AsyncRead, AsyncWrite}; -use ethkey::{Random, Generator, KeyPair, Secret, sign, verify_public}; +use ethkey::{Random, Generator, KeyPair, verify_public}; use util::H256; -use key_server_cluster::{NodeId, Error}; +use key_server_cluster::{NodeId, Error, NodeKeyPair}; 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, compute_shared_key}; + 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: KeyPair, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); handshake_with_plain_confirmation(a, self_confirmation_plain, self_key_pair, trusted_nodes) } /// Start handshake procedure with another node from the cluster and given plain confirmation. -pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Result, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Result, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { let (error, state) = match self_confirmation_plain.clone() .and_then(|c| Handshake::::make_public_key_message(self_key_pair.public().clone(), c)) { Ok(message) => (None, HandshakeState::SendPublicKey(write_message(a, message))), @@ -53,7 +54,7 @@ pub fn handshake_with_plain_confirmation(a: A, self_confirmation_plain: Resul } /// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: KeyPair) -> Handshake where A: AsyncWrite + AsyncRead { +pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let (error, state) = match self_confirmation_plain.clone() { Ok(_) => (None, HandshakeState::ReceivePublicKey(read_message(a))), @@ -87,7 +88,7 @@ pub struct Handshake { is_active: bool, error: Option<(A, Result)>, state: HandshakeState, - self_key_pair: KeyPair, + self_key_pair: Arc, self_confirmation_plain: H256, trusted_nodes: Option>, other_node_id: Option, @@ -117,9 +118,9 @@ impl Handshake where A: AsyncRead + AsyncWrite { }))) } - fn make_private_key_signature_message(secret: &Secret, confirmation_plain: &H256) -> Result { + fn make_private_key_signature_message(self_key_pair: &NodeKeyPair, confirmation_plain: &H256) -> Result { Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { - confirmation_signed: sign(secret, confirmation_plain)?.into(), + confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), }))) } } @@ -142,15 +143,15 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { read_message(stream) ), Async::NotReady) } else { - self.shared_key = match compute_shared_key(self.self_key_pair.secret(), + self.shared_key = match self.self_key_pair.compute_shared_key( self.other_node_id.as_ref().expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; other_node_id is filled in ReceivePublicKey; qed") - ) { + ).map_err(Into::into).and_then(|sk| fix_shared_key(sk.secret())) { Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), + Err(err) => return Ok((stream, Err(err.into())).into()), }; let message = match Handshake::::make_private_key_signature_message( - self.self_key_pair.secret(), + &*self.self_key_pair, self.other_confirmation_plain.as_ref().expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; other_confirmation_plain is filled in ReceivePublicKey; qed") ) { Ok(message) => message, @@ -179,15 +180,15 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { self.other_node_id = Some(message.node_id.into()); self.other_confirmation_plain = Some(message.confirmation_plain.into()); if self.is_active { - self.shared_key = match compute_shared_key(self.self_key_pair.secret(), + self.shared_key = match self.self_key_pair.compute_shared_key( self.other_node_id.as_ref().expect("filled couple of lines above; qed") - ) { + ).map_err(Into::into).and_then(|sk| fix_shared_key(sk.secret())) { Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), + Err(err) => return Ok((stream, Err(err.into())).into()), }; let message = match Handshake::::make_private_key_signature_message( - self.self_key_pair.secret(), + &*self.self_key_pair, self.other_confirmation_plain.as_ref().expect("filled couple of lines above; qed") ) { Ok(message) => message, @@ -248,11 +249,14 @@ impl Future for Handshake where A: AsyncRead + AsyncWrite { #[cfg(test)] mod tests { + use std::sync::Arc; use std::collections::BTreeSet; use futures::Future; use ethkey::{Random, Generator, sign}; + use ethcrypto::ecdh::agree; use util::H256; - use key_server_cluster::io::message::compute_shared_key; + use key_server_cluster::PlainNodeKeyPair; + use key_server_cluster::io::message::fix_shared_key; use key_server_cluster::io::message::tests::TestIo; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use super::{handshake_with_plain_confirmation, accept_handshake, HandshakeResult}; @@ -283,9 +287,9 @@ mod tests { let (self_confirmation_plain, io) = prepare_test_io(); let self_key_pair = io.self_key_pair().clone(); let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); - let shared_key = compute_shared_key(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap(); + let shared_key = fix_shared_key(&agree(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap()).unwrap(); - let handshake = handshake_with_plain_confirmation(io, Ok(self_confirmation_plain), self_key_pair, trusted_nodes); + let handshake = handshake_with_plain_confirmation(io, Ok(self_confirmation_plain), Arc::new(PlainNodeKeyPair::new(self_key_pair)), trusted_nodes); let handshake_result = handshake.wait().unwrap(); assert_eq!(handshake_result.1, Ok(HandshakeResult { node_id: handshake_result.0.peer_public().clone(), @@ -298,9 +302,9 @@ mod tests { let (self_confirmation_plain, io) = prepare_test_io(); let self_key_pair = io.self_key_pair().clone(); let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); - let shared_key = compute_shared_key(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap(); + let shared_key = fix_shared_key(&agree(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap()).unwrap(); - let mut handshake = accept_handshake(io, self_key_pair); + let mut handshake = accept_handshake(io, Arc::new(PlainNodeKeyPair::new(self_key_pair))); handshake.set_self_confirmation_plain(self_confirmation_plain); let handshake_result = handshake.wait().unwrap(); diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index 49b71e39d..5a6b50a3e 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -19,9 +19,8 @@ use std::u16; use std::ops::Deref; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use serde_json; -use ethcrypto::ecdh::agree; use ethcrypto::ecies::{encrypt_single_message, decrypt_single_message}; -use ethkey::{Public, Secret, KeyPair}; +use ethkey::{Secret, KeyPair}; use ethkey::math::curve_order; use util::{H256, U256}; use key_server_cluster::Error; @@ -154,12 +153,11 @@ pub fn decrypt_message(key: &KeyPair, payload: Vec) -> Result, Error Ok(decrypt_single_message(key.secret(), &payload)?) } -/// Compute shared encryption key. -pub fn compute_shared_key(self_secret: &Secret, other_public: &Public) -> Result { +/// 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 = agree(self_secret, other_public)?; - let shared_secret: H256 = (*shared_secret).into(); + let shared_secret: H256 = (**shared_secret).into(); let shared_secret: U256 = shared_secret.into(); let shared_secret: H256 = (shared_secret % curve_order()).into(); let shared_key_pair = KeyPair::from_secret_slice(&*shared_secret)?; @@ -204,8 +202,9 @@ pub mod tests { use futures::Poll; use tokio_io::{AsyncRead, AsyncWrite}; use ethkey::{KeyPair, Public}; + use ethcrypto::ecdh::agree; use key_server_cluster::message::Message; - use super::{MESSAGE_HEADER_SIZE, MessageHeader, compute_shared_key, encrypt_message, serialize_message, + use super::{MESSAGE_HEADER_SIZE, MessageHeader, fix_shared_key, encrypt_message, serialize_message, serialize_header, deserialize_header}; pub struct TestIo { @@ -217,7 +216,7 @@ pub mod tests { impl TestIo { pub fn new(self_key_pair: KeyPair, peer_public: Public) -> Self { - let shared_key_pair = compute_shared_key(self_key_pair.secret(), &peer_public).unwrap(); + let shared_key_pair = fix_shared_key(&agree(self_key_pair.secret(), &peer_public).unwrap()).unwrap(); TestIo { self_key_pair: self_key_pair, peer_public: peer_public, diff --git a/secret_store/src/key_server_cluster/io/mod.rs b/secret_store/src/key_server_cluster/io/mod.rs index 57071038e..dfea33683 100644 --- a/secret_store/src/key_server_cluster/io/mod.rs +++ b/secret_store/src/key_server_cluster/io/mod.rs @@ -26,7 +26,7 @@ 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, compute_shared_key}; + 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}; diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 8f6ae4add..102c3672f 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -20,6 +20,7 @@ use ethkey; use ethcrypto; use super::types::all::ServerKeyId; +pub use super::traits::NodeKeyPair; pub use super::types::all::{NodeId, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare}; @@ -30,6 +31,8 @@ pub use self::generation_session::Session as GenerationSession; pub use self::encryption_session::Session as EncryptionSession; pub use self::decryption_session::Session as DecryptionSession; +#[cfg(test)] +pub use super::node_key_pair::PlainNodeKeyPair; #[cfg(test)] pub use super::key_storage::tests::DummyKeyStorage; #[cfg(test)] diff --git a/secret_store/src/key_server_cluster/net/accept_connection.rs b/secret_store/src/key_server_cluster/net/accept_connection.rs index 339625f3f..d85e492dd 100644 --- a/secret_store/src/key_server_cluster/net/accept_connection.rs +++ b/secret_store/src/key_server_cluster/net/accept_connection.rs @@ -15,18 +15,18 @@ // along with Parity. If not, see . use std::io; +use std::sync::Arc; use std::net::SocketAddr; use std::time::Duration; use futures::{Future, Poll}; use tokio_core::reactor::Handle; use tokio_core::net::TcpStream; -use ethkey::KeyPair; -use key_server_cluster::Error; +use key_server_cluster::{Error, NodeKeyPair}; 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(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: KeyPair) -> Deadline { +pub fn accept_connection(address: SocketAddr, stream: TcpStream, handle: &Handle, self_key_pair: Arc) -> Deadline { let accept = AcceptConnection { handshake: accept_handshake(stream, self_key_pair), address: address, diff --git a/secret_store/src/key_server_cluster/net/connect.rs b/secret_store/src/key_server_cluster/net/connect.rs index 449168ab2..7515494e4 100644 --- a/secret_store/src/key_server_cluster/net/connect.rs +++ b/secret_store/src/key_server_cluster/net/connect.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::sync::Arc; use std::collections::BTreeSet; use std::io; use std::time::Duration; @@ -21,13 +22,12 @@ use std::net::SocketAddr; use futures::{Future, Poll, Async}; use tokio_core::reactor::Handle; use tokio_core::net::{TcpStream, TcpStreamNew}; -use ethkey::KeyPair; -use key_server_cluster::{Error, NodeId}; +use key_server_cluster::{Error, NodeId, NodeKeyPair}; 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, handle: &Handle, self_key_pair: KeyPair, trusted_nodes: BTreeSet) -> Deadline { +pub fn connect(address: &SocketAddr, handle: &Handle, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { let connect = Connect { state: ConnectState::TcpConnect(TcpStream::connect(address, handle)), address: address.clone(), @@ -48,7 +48,7 @@ enum ConnectState { pub struct Connect { state: ConnectState, address: SocketAddr, - self_key_pair: KeyPair, + self_key_pair: Arc, trusted_nodes: BTreeSet, } diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index d5af7a5fa..18c61c1bf 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -241,7 +241,7 @@ pub mod tests { data_path: path.as_str().to_owned(), cluster_config: ClusterConfiguration { threads: 1, - self_private: (**Random.generate().unwrap().secret().clone()).into(), + //self_private: (**Random.generate().unwrap().secret().clone()).into(), listener_address: NodeAddress { address: "0.0.0.0".to_owned(), port: 8083, diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 9750f7223..7e9897e60 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -59,22 +59,24 @@ mod key_server; mod key_storage; mod serialization; mod key_server_set; +mod node_key_pair; use std::sync::Arc; use ethcore::client::Client; pub use types::all::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; -pub use traits::{KeyServer}; +pub use traits::{NodeKeyPair, KeyServer}; +pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair}; /// Start new key server instance -pub fn start(client: Arc, config: ServiceConfiguration) -> Result, Error> { +pub fn start(client: Arc, self_key_pair: Arc, config: ServiceConfiguration) -> Result, Error> { use std::sync::Arc; let acl_storage = acl_storage::OnChainAclStorage::new(&client); let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone())?; let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); - let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, acl_storage, key_storage)?; + let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair, acl_storage, key_storage)?; let listener = http_listener::KeyServerHttpListener::start(&config.listener_address, key_server)?; Ok(Box::new(listener)) } diff --git a/secret_store/src/node_key_pair.rs b/secret_store/src/node_key_pair.rs new file mode 100644 index 000000000..8676dd16d --- /dev/null +++ b/secret_store/src/node_key_pair.rs @@ -0,0 +1,67 @@ +// Copyright 2015-2017 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 ethcrypto::ecdh::agree; +use ethkey::{KeyPair, Public, Signature, Error as EthKeyError, sign}; +use ethcore::account_provider::AccountProvider; +use util::H256; +use traits::NodeKeyPair; + +pub struct PlainNodeKeyPair { + key_pair: KeyPair, +} + +pub struct KeyStoreNodeKeyPair { + _account_provider: Arc, +} + +impl PlainNodeKeyPair { + pub fn new(key_pair: KeyPair) -> Self { + PlainNodeKeyPair { + key_pair: key_pair, + } + } +} + +impl NodeKeyPair for PlainNodeKeyPair { + fn public(&self) -> &Public { + self.key_pair.public() + } + + fn sign(&self, data: &H256) -> Result { + sign(self.key_pair.secret(), data) + } + + fn compute_shared_key(&self, peer_public: &Public) -> Result { + agree(self.key_pair.secret(), peer_public).map_err(|e| EthKeyError::Custom(e.into())) + .and_then(KeyPair::from_secret) + } +} + +impl NodeKeyPair for KeyStoreNodeKeyPair { + fn public(&self) -> &Public { + unimplemented!() + } + + fn sign(&self, _data: &H256) -> Result { + unimplemented!() + } + + fn compute_shared_key(&self, _peer_public: &Public) -> Result { + unimplemented!() + } +} diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 33a4eff3c..31da748e0 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -14,9 +14,21 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use ethkey::{KeyPair, Signature, Error as EthKeyError}; +use util::H256; use types::all::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, EncryptedDocumentKey, EncryptedDocumentKeyShadow}; +/// Node key pair. +pub trait NodeKeyPair: Send + Sync { + /// Public portion of key. + fn public(&self) -> &Public; + /// Sign data with node key. + fn sign(&self, data: &H256) -> Result; + /// Compute shared key to encrypt channel between two nodes. + fn compute_shared_key(&self, peer_public: &Public) -> Result; +} + /// Server key (SK) generator. pub trait ServerKeyGenerator { /// Generate new SK. diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 54fc8acae..8dc92f175 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -83,8 +83,6 @@ pub struct ServiceConfiguration { pub struct ClusterConfiguration { /// Number of threads reserved by cluster. pub threads: usize, - /// Private key this node holds. - pub self_private: Vec, // holds ethkey::Secret /// This node address. pub listener_address: NodeAddress, /// All cluster nodes addresses. From fb68b0924ab7902f301d697643a10d27f50fb739 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 09:56:23 +0300 Subject: [PATCH 15/28] fixed parity to use new trait --- parity/secretstore.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/parity/secretstore.rs b/parity/secretstore.rs index f215c937c..b29f43479 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -65,6 +65,7 @@ mod server { #[cfg(feature="secretstore")] mod server { + use std::sync::Arc; use ethcore_secretstore; use ethkey::KeyPair; use super::{Configuration, Dependencies}; @@ -86,7 +87,6 @@ mod server { data_path: conf.data_path.clone(), cluster_config: ethcore_secretstore::ClusterConfiguration { threads: 4, - self_private: (**self_secret).into(), listener_address: ethcore_secretstore::NodeAddress { address: conf.interface.clone(), port: conf.port, @@ -103,7 +103,8 @@ mod server { .map_err(|e| format!("valid secret is required when using secretstore. Error: {}", e))?; conf.cluster_config.nodes.insert(self_key_pair.public().clone(), conf.cluster_config.listener_address.clone()); - let key_server = ethcore_secretstore::start(deps.client, conf) + let node_key_pair = Arc::new(ethcore_secretstore::PlainNodeKeyPair::new(self_key_pair)); + let key_server = ethcore_secretstore::start(deps.client, node_key_pair, conf) .map_err(Into::::into)?; Ok(KeyServer { From 9e30d85fdc4c2c76d7fc0bada8302cff9b66a383 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 10:19:48 +0300 Subject: [PATCH 16/28] continue integrating with parity --- parity/configuration.rs | 8 ++++---- parity/secretstore.rs | 29 +++++++++++++++++++---------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/parity/configuration.rs b/parity/configuration.rs index fe397dff5..e037defa2 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -41,7 +41,7 @@ use ethcore_logger::Config as LogConfig; use dir::{self, Directories, default_hypervisor_path, default_local_path, default_data_path}; use dapps::Configuration as DappsConfiguration; use ipfs::Configuration as IpfsConfiguration; -use secretstore::Configuration as SecretStoreConfiguration; +use secretstore::{Configuration as SecretStoreConfiguration, NodeSecretKey}; use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack}; use run::RunCmd; use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat}; @@ -995,10 +995,10 @@ impl Configuration { self.interface(&self.args.flag_secretstore_http_interface) } - fn secretstore_self_secret(&self) -> Result, String> { + fn secretstore_self_secret(&self) -> Result, String> { match self.args.flag_secretstore_secret { - Some(ref s) => Ok(Some(s.parse() - .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?)), + Some(ref s) => Ok(Some(NodeSecretKey::Plain(s.parse() + .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?))), None => Ok(None), } } diff --git a/parity/secretstore.rs b/parity/secretstore.rs index b29f43479..7cdd26377 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -21,13 +21,20 @@ use ethcore::client::Client; use ethkey::{Secret, Public}; use helpers::replace_home; +#[derive(Debug, PartialEq, Clone)] +/// This node secret key. +pub enum NodeSecretKey { + /// Stored as plain text in configuration file. + Plain(Secret), +} + #[derive(Debug, PartialEq, Clone)] /// Secret store configuration pub struct Configuration { /// Is secret store functionality enabled? pub enabled: bool, /// This node secret. - pub self_secret: Option, + pub self_secret: Option, /// Other nodes IDs + addresses. pub nodes: BTreeMap, /// Interface to listen to @@ -66,9 +73,9 @@ mod server { #[cfg(feature="secretstore")] mod server { use std::sync::Arc; - use ethcore_secretstore; + use ethcore_secretstore::{self, NodeKeyPair}; use ethkey::KeyPair; - use super::{Configuration, Dependencies}; + use super::{Configuration, Dependencies, NodeSecretKey}; /// Key server pub struct KeyServer { @@ -77,8 +84,13 @@ mod server { impl KeyServer { /// Create new key server - pub fn new(conf: Configuration, deps: Dependencies) -> Result { - let self_secret = conf.self_secret.ok_or("self secret is required when using secretstore")?; + pub fn new(mut conf: Configuration, deps: Dependencies) -> Result { + let self_secret = match conf.self_secret.take() { + Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( + KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), + None => return Err("self secret is required when using secretstore".into()), + }; + let mut conf = ethcore_secretstore::ServiceConfiguration { listener_address: ethcore_secretstore::NodeAddress { address: conf.http_interface.clone(), @@ -99,12 +111,9 @@ mod server { }, }; - let self_key_pair = KeyPair::from_secret(self_secret.clone()) - .map_err(|e| format!("valid secret is required when using secretstore. Error: {}", e))?; - conf.cluster_config.nodes.insert(self_key_pair.public().clone(), conf.cluster_config.listener_address.clone()); + conf.cluster_config.nodes.insert(self_secret.public().clone(), conf.cluster_config.listener_address.clone()); - let node_key_pair = Arc::new(ethcore_secretstore::PlainNodeKeyPair::new(self_key_pair)); - let key_server = ethcore_secretstore::start(deps.client, node_key_pair, conf) + let key_server = ethcore_secretstore::start(deps.client, self_secret, conf) .map_err(Into::::into)?; Ok(KeyServer { From 2e9df2c39dbb6790d6c43befcf5214bcf650eeb7 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 16:30:24 +0300 Subject: [PATCH 17/28] updated parity for NodeKeyPair --- ethcore/src/account_provider/mod.rs | 5 +++++ parity/configuration.rs | 5 ++++- parity/run.rs | 4 +++- parity/secretstore.rs | 32 ++++++++++++++++++++++++++--- secret_store/src/node_key_pair.rs | 26 ++++++++++++++++++----- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index 249ca40af..769db692c 100755 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -519,6 +519,11 @@ impl AccountProvider { } } + /// Returns account public key. + pub fn account_public(&self, address: Address, password: &str) -> Result { + self.sstore.public(&self.sstore.account_ref(&address)?, password) + } + /// Returns each account along with name and meta. pub fn set_account_name(&self, address: Address, name: String) -> Result<(), Error> { self.sstore.set_name(&self.sstore.account_ref(&address)?, name)?; diff --git a/parity/configuration.rs b/parity/configuration.rs index e037defa2..09dbfeedf 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -997,8 +997,11 @@ impl Configuration { fn secretstore_self_secret(&self) -> Result, String> { match self.args.flag_secretstore_secret { - Some(ref s) => Ok(Some(NodeSecretKey::Plain(s.parse() + Some(ref s) if s.len() == 64 => Ok(Some(NodeSecretKey::Plain(s.parse() .map_err(|e| format!("Invalid secret store secret: {}. Error: {:?}", s, e))?))), + Some(ref s) if s.len() == 40 => Ok(Some(NodeSecretKey::KeyStore(s.parse() + .map_err(|e| format!("Invalid secret store secret address: {}. Error: {:?}", s, e))?))), + Some(_) => Err(format!("Invalid secret store secret. Must be either existing account address, or hex-encoded private key")), None => Ok(None), } } diff --git a/parity/run.rs b/parity/run.rs index 30f4c8759..8fc5da405 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -489,7 +489,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R } // Attempt to sign in the engine signer. - if !passwords.into_iter().any(|p| miner.set_engine_signer(engine_signer, p).is_ok()) { + if !passwords.iter().any(|p| miner.set_engine_signer(engine_signer, (*p).clone()).is_ok()) { return Err(format!("No valid password for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); } } @@ -705,6 +705,8 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R // secret store key server let secretstore_deps = secretstore::Dependencies { client: client.clone(), + account_provider: account_provider, + accounts_passwords: &passwords, }; let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps)?; diff --git a/parity/secretstore.rs b/parity/secretstore.rs index 7cdd26377..def2cd1a6 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -17,15 +17,19 @@ use std::collections::BTreeMap; use std::sync::Arc; use dir::default_data_path; +use ethcore::account_provider::AccountProvider; use ethcore::client::Client; use ethkey::{Secret, Public}; use helpers::replace_home; +use util::Address; #[derive(Debug, PartialEq, Clone)] /// This node secret key. pub enum NodeSecretKey { /// Stored as plain text in configuration file. Plain(Secret), + /// Stored as account in key store. + KeyStore(Address), } #[derive(Debug, PartialEq, Clone)] @@ -50,9 +54,13 @@ pub struct Configuration { } /// Secret store dependencies -pub struct Dependencies { +pub struct Dependencies<'a> { /// Blockchain client. pub client: Arc, + /// Account provider. + pub account_provider: Arc, + /// Passed accounts passwords. + pub accounts_passwords: &'a [String], } #[cfg(not(feature = "secretstore"))] @@ -73,7 +81,7 @@ mod server { #[cfg(feature="secretstore")] mod server { use std::sync::Arc; - use ethcore_secretstore::{self, NodeKeyPair}; + use ethcore_secretstore; use ethkey::KeyPair; use super::{Configuration, Dependencies, NodeSecretKey}; @@ -85,9 +93,27 @@ mod server { impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies) -> Result { - let self_secret = match conf.self_secret.take() { + let self_secret: Arc = match conf.self_secret.take() { Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), + Some(NodeSecretKey::KeyStore(account)) => { + // Check if account exists + if !deps.account_provider.has_account(account.clone()).unwrap_or(false) { + return Err(format!("Account {} passed as secret store node key is not found", account)); + } + + // Check if any passwords have been read from the password file(s) + if deps.accounts_passwords.is_empty() { + return Err(format!("No password found for the secret store node account {}", account)); + } + + // Attempt to sign in the engine signer. + let password = deps.accounts_passwords.iter() + .find(|p| deps.account_provider.sign(account.clone(), Some((*p).clone()), Default::default()).is_ok()) + .ok_or(format!("No valid password for the secret store node account {}", account))?; + Arc::new(ethcore_secretstore::KeyStoreNodeKeyPair::new(deps.account_provider, account, password.clone()) + .map_err(|e| format!("{}", e))?) + }, None => return Err("self secret is required when using secretstore".into()), }; diff --git a/secret_store/src/node_key_pair.rs b/secret_store/src/node_key_pair.rs index 8676dd16d..556625079 100644 --- a/secret_store/src/node_key_pair.rs +++ b/secret_store/src/node_key_pair.rs @@ -18,7 +18,7 @@ use std::sync::Arc; use ethcrypto::ecdh::agree; use ethkey::{KeyPair, Public, Signature, Error as EthKeyError, sign}; use ethcore::account_provider::AccountProvider; -use util::H256; +use util::{Address, H256}; use traits::NodeKeyPair; pub struct PlainNodeKeyPair { @@ -26,7 +26,10 @@ pub struct PlainNodeKeyPair { } pub struct KeyStoreNodeKeyPair { - _account_provider: Arc, + account_provider: Arc, + address: Address, + public: Public, + password: String, } impl PlainNodeKeyPair { @@ -52,13 +55,26 @@ impl NodeKeyPair for PlainNodeKeyPair { } } +impl KeyStoreNodeKeyPair { + pub fn new(account_provider: Arc, address: Address, password: String) -> Result { + let public = account_provider.account_public(address.clone(), &password).map_err(|e| EthKeyError::Custom(format!("{}", e)))?; + Ok(KeyStoreNodeKeyPair { + account_provider: account_provider, + address: address, + public: public, + password: password, + }) + } +} + impl NodeKeyPair for KeyStoreNodeKeyPair { fn public(&self) -> &Public { - unimplemented!() + &self.public } - fn sign(&self, _data: &H256) -> Result { - unimplemented!() + fn sign(&self, data: &H256) -> Result { + self.account_provider.sign(self.address.clone(), Some(self.password.clone()), data.clone()) + .map_err(|e| EthKeyError::Custom(format!("{}", e))) } fn compute_shared_key(&self, _peer_public: &Public) -> Result { From eb895fbb3199cd81c887c3048ba2c2090f927ac6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 17:54:32 +0300 Subject: [PATCH 18/28] completed KeyStoreNodeKeyPair --- Cargo.lock | 1 + ethcore/src/account_provider/mod.rs | 7 +++++++ ethstore/Cargo.toml | 1 + ethstore/src/account/safe_account.rs | 9 ++++++++- ethstore/src/ethstore.rs | 12 ++++++++++++ ethstore/src/lib.rs | 1 + ethstore/src/secret_store.rs | 2 ++ secret_store/src/key_server.rs | 1 - secret_store/src/node_key_pair.rs | 5 +++-- 9 files changed, 35 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ae33d7590..0c049c948 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -794,6 +794,7 @@ name = "ethstore" version = "0.1.0" dependencies = [ "ethcore-bigint 0.1.3", + "ethcore-util 1.8.0", "ethcrypto 0.1.0", "ethkey 0.2.0", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index 769db692c..752cec964 100755 --- a/ethcore/src/account_provider/mod.rs +++ b/ethcore/src/account_provider/mod.rs @@ -702,6 +702,13 @@ impl AccountProvider { Ok(self.sstore.decrypt(&account, &password, shared_mac, message)?) } + /// Agree on shared key. + pub fn agree(&self, address: Address, password: Option, other_public: &Public) -> Result { + let account = self.sstore.account_ref(&address)?; + let password = password.map(Ok).unwrap_or_else(|| self.password(&account))?; + Ok(self.sstore.agree(&account, &password, other_public)?) + } + /// Returns the underlying `SecretStore` reference if one exists. pub fn list_geth_accounts(&self, testnet: bool) -> Vec
{ self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect() diff --git a/ethstore/Cargo.toml b/ethstore/Cargo.toml index 117332022..9d8d2fce5 100755 --- a/ethstore/Cargo.toml +++ b/ethstore/Cargo.toml @@ -19,6 +19,7 @@ itertools = "0.5" parking_lot = "0.4" ethcrypto = { path = "../ethcrypto" } ethcore-bigint = { path = "../util/bigint" } +ethcore-util = { path = "../util" } smallvec = "0.4" parity-wordlist = "1.0" tempdir = "0.3" diff --git a/ethstore/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index e0512fe8d..478b796e6 100755 --- a/ethstore/src/account/safe_account.rs +++ b/ethstore/src/account/safe_account.rs @@ -14,7 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethkey::{KeyPair, sign, Address, Signature, Message, Public}; +use ethkey::{KeyPair, sign, Address, Signature, Message, Public, Secret}; +use crypto::ecdh::agree; use {json, Error, crypto}; use account::Version; use super::crypto::Crypto; @@ -135,6 +136,12 @@ impl SafeAccount { crypto::ecies::decrypt(&secret, shared_mac, message).map_err(From::from) } + /// Agree on shared key. + pub fn agree(&self, password: &str, other: &Public) -> Result { + let secret = self.crypto.secret(password)?; + agree(&secret, other).map_err(From::from) + } + /// Derive public key. pub fn public(&self, password: &str) -> Result { let secret = self.crypto.secret(password)?; diff --git a/ethstore/src/ethstore.rs b/ethstore/src/ethstore.rs index 246671990..d32fa9f62 100755 --- a/ethstore/src/ethstore.rs +++ b/ethstore/src/ethstore.rs @@ -97,6 +97,10 @@ impl SimpleSecretStore for EthStore { self.store.sign_derived(account_ref, password, derivation, message) } + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result { + self.store.agree(account, password, other) + } + fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let account = self.get(account)?; account.decrypt(password, shared_mac, message) @@ -509,6 +513,14 @@ impl SimpleSecretStore for EthMultiStore { Err(Error::InvalidPassword) } + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result { + let accounts = self.get_matching(account, password)?; + for account in accounts { + return account.agree(password, other); + } + Err(Error::InvalidPassword) + } + fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> { let is_vault_created = { // lock border let mut vaults = self.vaults.lock(); diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs index 65935f89c..311e9e73a 100755 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -35,6 +35,7 @@ extern crate ethcore_bigint as bigint; extern crate ethcrypto as crypto; extern crate ethkey as _ethkey; extern crate parity_wordlist; +extern crate ethcore_util as util; #[macro_use] extern crate log; diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs index 2deae023e..e364245b7 100755 --- a/ethstore/src/secret_store.rs +++ b/ethstore/src/secret_store.rs @@ -60,6 +60,8 @@ pub trait SimpleSecretStore: Send + Sync { fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message) -> Result; /// Decrypt a messages with given account. fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error>; + /// Agree on shared key. + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result; /// Returns all accounts in this secret store. fn accounts(&self) -> Result, Error>; diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 0944dd37c..6526bff68 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -245,7 +245,6 @@ pub mod tests { let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); let configs: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { threads: 1, -// self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), listener_address: NodeAddress { address: "127.0.0.1".into(), port: start_port + (i as u16), diff --git a/secret_store/src/node_key_pair.rs b/secret_store/src/node_key_pair.rs index 556625079..ce6c88a07 100644 --- a/secret_store/src/node_key_pair.rs +++ b/secret_store/src/node_key_pair.rs @@ -77,7 +77,8 @@ impl NodeKeyPair for KeyStoreNodeKeyPair { .map_err(|e| EthKeyError::Custom(format!("{}", e))) } - fn compute_shared_key(&self, _peer_public: &Public) -> Result { - unimplemented!() + fn compute_shared_key(&self, peer_public: &Public) -> Result { + KeyPair::from_secret(self.account_provider.agree(self.address.clone(), Some(self.password.clone()), peer_public) + .map_err(|e| EthKeyError::Custom(format!("{}", e)))?) } } From 4938dfd971a6524310611fbb0da0f0fdd02e1a72 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 25 Jul 2017 17:57:27 +0300 Subject: [PATCH 19/28] removed comment --- secret_store/src/key_storage.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index 18c61c1bf..08ebe6e1c 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -241,7 +241,6 @@ pub mod tests { data_path: path.as_str().to_owned(), cluster_config: ClusterConfiguration { threads: 1, - //self_private: (**Random.generate().unwrap().secret().clone()).into(), listener_address: NodeAddress { address: "0.0.0.0".to_owned(), port: 8083, From 417a037ac517d1e783f6c42c4fbc9e475a7ffffc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 26 Jul 2017 14:09:41 +0300 Subject: [PATCH 20/28] improved logging --- .../src/key_server_cluster/cluster.rs | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 155dd4a01..c267d1259 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -262,7 +262,7 @@ impl ClusterCore { fn connect_future(handle: &Handle, data: Arc, node_address: SocketAddr) -> BoxedEmptyFuture { let disconnected_nodes = data.connections.disconnected_nodes().keys().cloned().collect(); net_connect(&node_address, handle, data.self_key_pair.clone(), disconnected_nodes) - .then(move |result| ClusterCore::process_connection_result(data, false, result)) + .then(move |result| ClusterCore::process_connection_result(data, Some(node_address), result)) .then(|_| finished(())) .boxed() } @@ -290,7 +290,7 @@ impl ClusterCore { /// Accept connection future. fn accept_connection_future(handle: &Handle, data: Arc, stream: TcpStream, node_address: SocketAddr) -> BoxedEmptyFuture { net_accept_connection(node_address, stream, handle, data.self_key_pair.clone()) - .then(move |result| ClusterCore::process_connection_result(data, true, result)) + .then(move |result| ClusterCore::process_connection_result(data, None, result)) .then(|_| finished(())) .boxed() } @@ -370,10 +370,10 @@ impl ClusterCore { } /// Process connection future result. - fn process_connection_result(data: Arc, is_inbound: bool, result: Result>, io::Error>) -> IoFuture> { + fn process_connection_result(data: Arc, outbound_addr: Option, result: Result>, io::Error>) -> IoFuture> { match result { Ok(DeadlineStatus::Meet(Ok(connection))) => { - let connection = Connection::new(is_inbound, connection); + let connection = Connection::new(outbound_addr.is_none(), connection); if data.connections.insert(connection.clone()) { ClusterCore::process_connection_messages(data.clone(), connection) } else { @@ -381,15 +381,21 @@ impl ClusterCore { } }, Ok(DeadlineStatus::Meet(Err(err))) => { - warn!(target: "secretstore_net", "{}: protocol error {} when establishind connection", data.self_key_pair.public(), 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()); finished(Ok(())).boxed() }, Ok(DeadlineStatus::Timeout) => { - warn!(target: "secretstore_net", "{}: timeout when establishind connection", data.self_key_pair.public()); + 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()); finished(Ok(())).boxed() }, Err(err) => { - warn!(target: "secretstore_net", "{}: network error {} when establishind connection", data.self_key_pair.public(), 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()); finished(Ok(())).boxed() }, } From dcfb8c1a10f6590c5b565e71ec491fb9fba038dc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 26 Jul 2017 14:09:52 +0300 Subject: [PATCH 21/28] fixed generation session lags --- secret_store/src/key_server_cluster/generation_session.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index e94d5bd35..0ba82524e 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -399,7 +399,7 @@ impl SessionImpl { // check state if data.state != SessionState::WaitingForKeysDissemination { match data.state { - SessionState::WaitingForInitializationComplete => return Err(Error::TooEarlyForRequest), + SessionState::WaitingForInitializationComplete | SessionState::WaitingForInitializationConfirm(_) => return Err(Error::TooEarlyForRequest), _ => return Err(Error::InvalidStateForRequest), } } From c466def1e8bc2e9a25c1cf5434cd18134200be68 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 27 Jul 2017 11:33:09 +0300 Subject: [PATCH 22/28] improved logging --- parity/secretstore.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/parity/secretstore.rs b/parity/secretstore.rs index def2cd1a6..0f23ffdf5 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -117,7 +117,8 @@ mod server { None => return Err("self secret is required when using secretstore".into()), }; - let mut conf = ethcore_secretstore::ServiceConfiguration { + let key_server_name = format!("{}:{}", conf.interface, conf.port); + let mut cconf = ethcore_secretstore::ServiceConfiguration { listener_address: ethcore_secretstore::NodeAddress { address: conf.http_interface.clone(), port: conf.http_port, @@ -137,10 +138,10 @@ mod server { }, }; - conf.cluster_config.nodes.insert(self_secret.public().clone(), conf.cluster_config.listener_address.clone()); + cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - let key_server = ethcore_secretstore::start(deps.client, self_secret, conf) - .map_err(Into::::into)?; + let key_server = ethcore_secretstore::start(deps.client, self_secret, cconf) + .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { _key_server: key_server, From 7c05a906d095a0e1c0f6cac16eec88247612856c Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 27 Jul 2017 13:29:09 +0300 Subject: [PATCH 23/28] cli option to disable SS HTTP API --- parity/cli/config.full.toml | 3 ++- parity/cli/mod.rs | 5 ++++ parity/cli/usage.txt | 1 + parity/configuration.rs | 6 +++++ parity/secretstore.rs | 7 ++++-- secret_store/src/http_listener.rs | 25 ++++++++++++------- .../key_server_cluster/generation_session.rs | 2 +- secret_store/src/key_storage.rs | 5 +--- secret_store/src/lib.rs | 2 +- secret_store/src/types/all.rs | 4 +-- 10 files changed, 40 insertions(+), 20 deletions(-) diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 581871997..47ca9ffd8 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -76,8 +76,9 @@ path = "$HOME/.parity/dapps" user = "test_user" pass = "test_pass" -[secretstore] +[secretstore] disable = false +disable_http = false nodes = [] http_interface = "local" http_port = 8082 diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 262e054a2..27c5c40ff 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -216,6 +216,8 @@ usage! { // Secret Store flag_no_secretstore: bool = false, or |c: &Config| otry!(c.secretstore).disable.clone(), + flag_no_secretstore_http: bool = false, + or |c: &Config| otry!(c.secretstore).disable_http.clone(), flag_secretstore_secret: Option = None, or |c: &Config| otry!(c.secretstore).self_secret.clone().map(Some), flag_secretstore_nodes: String = "", @@ -510,6 +512,7 @@ struct Dapps { #[derive(Default, Debug, PartialEq, Deserialize)] struct SecretStore { disable: Option, + disable_http: Option, self_secret: Option, nodes: Option>, interface: Option, @@ -779,6 +782,7 @@ mod tests { flag_no_dapps: false, flag_no_secretstore: false, + flag_no_secretstore_http: false, flag_secretstore_secret: None, flag_secretstore_nodes: "".into(), flag_secretstore_interface: "local".into(), @@ -1009,6 +1013,7 @@ mod tests { }), secretstore: Some(SecretStore { disable: None, + disable_http: None, self_secret: None, nodes: None, interface: None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index dc4796e05..38c76b71f 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -228,6 +228,7 @@ API and Console Options: Secret Store Options: --no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore}) + --no-secretstore-http Disable Secret Store HTTP API. (default: {flag_no_secretstore_http}) --secretstore-secret SECRET Hex-encoded secret key of this node. (required, default: {flag_secretstore_secret:?}). --secretstore-nodes NODES Comma-separated list of other secret store cluster nodes in form diff --git a/parity/configuration.rs b/parity/configuration.rs index 09dbfeedf..b17eeadef 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -586,6 +586,7 @@ impl Configuration { fn secretstore_config(&self) -> Result { Ok(SecretStoreConfiguration { enabled: self.secretstore_enabled(), + http_enabled: self.secretstore_http_enabled(), self_secret: self.secretstore_self_secret()?, nodes: self.secretstore_nodes()?, interface: self.secretstore_interface(), @@ -1050,6 +1051,10 @@ impl Configuration { !self.args.flag_no_secretstore && cfg!(feature = "secretstore") } + fn secretstore_http_enabled(&self) -> bool { + !self.args.flag_no_secretstore_http && cfg!(feature = "secretstore") + } + fn ui_enabled(&self) -> bool { if self.args.flag_force_ui { return true; @@ -1331,6 +1336,7 @@ mod tests { no_persistent_txqueue: false, }; expected.secretstore_conf.enabled = cfg!(feature = "secretstore"); + expected.secretstore_conf.http_enabled = cfg!(feature = "secretstore"); assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected)); } diff --git a/parity/secretstore.rs b/parity/secretstore.rs index 0f23ffdf5..ef577c988 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -37,6 +37,8 @@ pub enum NodeSecretKey { pub struct Configuration { /// Is secret store functionality enabled? pub enabled: bool, + /// Is HTTP API enabled? + pub http_enabled: bool, /// This node secret. pub self_secret: Option, /// Other nodes IDs + addresses. @@ -119,10 +121,10 @@ mod server { let key_server_name = format!("{}:{}", conf.interface, conf.port); let mut cconf = ethcore_secretstore::ServiceConfiguration { - listener_address: ethcore_secretstore::NodeAddress { + listener_address: if conf.http_enabled { Some(ethcore_secretstore::NodeAddress { address: conf.http_interface.clone(), port: conf.http_port, - }, + }) } else { None }, data_path: conf.data_path.clone(), cluster_config: ethcore_secretstore::ClusterConfiguration { threads: 4, @@ -157,6 +159,7 @@ impl Default for Configuration { let data_dir = default_data_path(); Configuration { enabled: true, + http_enabled: true, self_secret: None, nodes: BTreeMap::new(), interface: "127.0.0.1".to_owned(), diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 1f7f14ede..86688618a 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -39,7 +39,7 @@ use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddr /// To sign message with server key: GET /{server_key_id}/{signature}/{message_hash} pub struct KeyServerHttpListener { - _http_server: HttpListening, + http_server: Option, handler: Arc>, } @@ -74,19 +74,26 @@ struct KeyServerSharedHttpHandler { impl KeyServerHttpListener where T: KeyServer + 'static { /// Start KeyServer http listener - pub fn start(listener_address: &NodeAddress, key_server: T) -> Result { + pub fn start(listener_address: Option, key_server: T) -> Result { let shared_handler = Arc::new(KeyServerSharedHttpHandler { key_server: key_server, }); - let handler = KeyServerHttpHandler { + /*let handler = KeyServerHttpHandler { handler: shared_handler.clone(), - }; + };*/ - let listener_addr: &str = &format!("{}:{}", listener_address.address, listener_address.port); + let http_server = listener_address + .map(|listener_address| format!("{}:{}", listener_address.address, listener_address.port)) + .map(|listener_address| HttpServer::http(&listener_address).expect("cannot start HttpServer")) + .map(|http_server| http_server.handle(KeyServerHttpHandler { + handler: shared_handler.clone(), + }).expect("cannot start HttpServer")); + + /*let listener_addr: &str = &format!("{}:{}", listener_address.address, listener_address.port); let http_server = HttpServer::http(&listener_addr).expect("cannot start HttpServer"); - let http_server = http_server.handle(handler).expect("cannot start HttpServer"); + let http_server = http_server.handle(handler).expect("cannot start HttpServer");*/ let listener = KeyServerHttpListener { - _http_server: http_server, + http_server: http_server, handler: shared_handler, }; Ok(listener) @@ -128,7 +135,7 @@ impl MessageSigner for KeyServerHttpListener where T: KeyServer + 'static impl Drop for KeyServerHttpListener where T: KeyServer + 'static { fn drop(&mut self) { // ignore error as we are dropping anyway - let _ = self._http_server.close(); + self.http_server.take().map(|mut s| { let _ = s.close(); }); } } @@ -318,7 +325,7 @@ mod tests { fn http_listener_successfully_drops() { let key_server = DummyKeyServer; let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; - let listener = KeyServerHttpListener::start(&address, key_server).unwrap(); + let listener = KeyServerHttpListener::start(Some(address), key_server).unwrap(); drop(listener); } diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index 0ba82524e..ade78bc57 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -1104,7 +1104,7 @@ pub mod tests { secret1: math::generate_random_scalar().unwrap().into(), secret2: math::generate_random_scalar().unwrap().into(), publics: vec![math::generate_random_point().unwrap().into()], - }).unwrap_err(), Error::InvalidStateForRequest); + }).unwrap_err(), Error::TooEarlyForRequest); } #[test] diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index 08ebe6e1c..20b5eaf6c 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -234,10 +234,7 @@ pub mod tests { fn persistent_key_storage() { let path = RandomTempPath::create_dir(); let config = ServiceConfiguration { - listener_address: NodeAddress { - address: "0.0.0.0".to_owned(), - port: 8082, - }, + listener_address: None, data_path: path.as_str().to_owned(), cluster_config: ClusterConfiguration { threads: 1, diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 7e9897e60..6ead7c657 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -77,6 +77,6 @@ pub fn start(client: Arc, self_key_pair: Arc, config: Servi let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone())?; let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair, acl_storage, key_storage)?; - let listener = http_listener::KeyServerHttpListener::start(&config.listener_address, key_server)?; + let listener = http_listener::KeyServerHttpListener::start(config.listener_address, key_server)?; Ok(Box::new(listener)) } diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 8dc92f175..6bc0d9c87 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -69,8 +69,8 @@ pub struct NodeAddress { #[binary] /// Secret store configuration pub struct ServiceConfiguration { - /// HTTP listener address. - pub listener_address: NodeAddress, + /// HTTP listener address. If None, HTTP API is disabled. + pub listener_address: Option, /// Data directory path for secret store pub data_path: String, /// Cluster configuration. From c345bc3d856e48c3c37f0a56bbea7cc1eabc74dc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 27 Jul 2017 15:48:07 +0300 Subject: [PATCH 24/28] cli option to disable SS ACL check --- parity/cli/config.full.toml | 1 + parity/cli/mod.rs | 5 ++ parity/cli/usage.txt | 1 + parity/configuration.rs | 5 ++ parity/secretstore.rs | 4 ++ secret_store/src/acl_storage.rs | 57 ++++++++----------- secret_store/src/key_server.rs | 2 +- .../key_server_cluster/decryption_session.rs | 2 +- secret_store/src/key_server_cluster/mod.rs | 2 +- .../src/key_server_cluster/signing_session.rs | 2 +- secret_store/src/key_storage.rs | 1 + secret_store/src/lib.rs | 6 +- secret_store/src/types/all.rs | 2 + 13 files changed, 53 insertions(+), 37 deletions(-) diff --git a/parity/cli/config.full.toml b/parity/cli/config.full.toml index 47ca9ffd8..75677ed6c 100644 --- a/parity/cli/config.full.toml +++ b/parity/cli/config.full.toml @@ -79,6 +79,7 @@ pass = "test_pass" [secretstore] disable = false disable_http = false +disable_acl_check = false nodes = [] http_interface = "local" http_port = 8082 diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 27c5c40ff..50032767a 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -218,6 +218,8 @@ usage! { or |c: &Config| otry!(c.secretstore).disable.clone(), flag_no_secretstore_http: bool = false, or |c: &Config| otry!(c.secretstore).disable_http.clone(), + flag_no_secretstore_acl_check: bool = false, + or |c: &Config| otry!(c.secretstore).disable_acl_check.clone(), flag_secretstore_secret: Option = None, or |c: &Config| otry!(c.secretstore).self_secret.clone().map(Some), flag_secretstore_nodes: String = "", @@ -513,6 +515,7 @@ struct Dapps { struct SecretStore { disable: Option, disable_http: Option, + disable_acl_check: Option, self_secret: Option, nodes: Option>, interface: Option, @@ -783,6 +786,7 @@ mod tests { flag_no_secretstore: false, flag_no_secretstore_http: false, + flag_no_secretstore_acl_check: false, flag_secretstore_secret: None, flag_secretstore_nodes: "".into(), flag_secretstore_interface: "local".into(), @@ -1014,6 +1018,7 @@ mod tests { secretstore: Some(SecretStore { disable: None, disable_http: None, + disable_acl_check: None, self_secret: None, nodes: None, interface: None, diff --git a/parity/cli/usage.txt b/parity/cli/usage.txt index 38c76b71f..da36c4a2b 100644 --- a/parity/cli/usage.txt +++ b/parity/cli/usage.txt @@ -229,6 +229,7 @@ API and Console Options: Secret Store Options: --no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore}) --no-secretstore-http Disable Secret Store HTTP API. (default: {flag_no_secretstore_http}) + --no-acl-check Disable ACL check (useful for test environments). (default: {flag_no_secretstore_acl_check}) --secretstore-secret SECRET Hex-encoded secret key of this node. (required, default: {flag_secretstore_secret:?}). --secretstore-nodes NODES Comma-separated list of other secret store cluster nodes in form diff --git a/parity/configuration.rs b/parity/configuration.rs index b17eeadef..0583bdb81 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -587,6 +587,7 @@ impl Configuration { Ok(SecretStoreConfiguration { enabled: self.secretstore_enabled(), http_enabled: self.secretstore_http_enabled(), + acl_check_enabled: self.secretstore_acl_check_enabled(), self_secret: self.secretstore_self_secret()?, nodes: self.secretstore_nodes()?, interface: self.secretstore_interface(), @@ -1055,6 +1056,10 @@ impl Configuration { !self.args.flag_no_secretstore_http && cfg!(feature = "secretstore") } + fn secretstore_acl_check_enabled(&self) -> bool { + !self.args.flag_no_secretstore_acl_check + } + fn ui_enabled(&self) -> bool { if self.args.flag_force_ui { return true; diff --git a/parity/secretstore.rs b/parity/secretstore.rs index ef577c988..8094ef323 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -39,6 +39,8 @@ pub struct Configuration { pub enabled: bool, /// Is HTTP API enabled? pub http_enabled: bool, + /// Is ACL check enabled. + pub acl_check_enabled: bool, /// This node secret. pub self_secret: Option, /// Other nodes IDs + addresses. @@ -126,6 +128,7 @@ mod server { port: conf.http_port, }) } else { None }, data_path: conf.data_path.clone(), + acl_check_enabled: conf.acl_check_enabled, cluster_config: ethcore_secretstore::ClusterConfiguration { threads: 4, listener_address: ethcore_secretstore::NodeAddress { @@ -160,6 +163,7 @@ impl Default for Configuration { Configuration { enabled: true, http_enabled: true, + acl_check_enabled: true, self_secret: None, nodes: BTreeMap::new(), interface: "127.0.0.1".to_owned(), diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 37d5bcd25..0a3ee9276 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -15,8 +15,9 @@ // along with Parity. If not, see . use std::sync::{Arc, Weak}; +use std::collections::{HashMap, HashSet}; use futures::{future, Future}; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use ethkey::public_to_address; use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify}; use native_contracts::SecretStoreAclStorage; @@ -47,6 +48,12 @@ struct CachedContract { contract: Option, } +#[derive(Default, Debug)] +/// Dummy ACL storage implementation (check always passed). +pub struct DummyAclStorage { + prohibited: RwLock>>, +} + impl OnChainAclStorage { pub fn new(client: &Arc) -> Arc { let acl_storage = Arc::new(OnChainAclStorage { @@ -113,36 +120,22 @@ impl CachedContract { } } -#[cfg(test)] -pub mod tests { - use std::collections::{HashMap, HashSet}; - use parking_lot::RwLock; - use types::all::{Error, ServerKeyId, Public}; - use super::AclStorage; - - #[derive(Default, Debug)] - /// Dummy ACL storage implementation - pub struct DummyAclStorage { - prohibited: RwLock>>, - } - - impl DummyAclStorage { - #[cfg(test)] - /// Prohibit given requestor access to given document - pub fn prohibit(&self, public: Public, document: ServerKeyId) { - self.prohibited.write() - .entry(public) - .or_insert_with(Default::default) - .insert(document); - } - } - - impl AclStorage for DummyAclStorage { - fn check(&self, public: &Public, document: &ServerKeyId) -> Result { - Ok(self.prohibited.read() - .get(public) - .map(|docs| !docs.contains(document)) - .unwrap_or(true)) - } +impl DummyAclStorage { + #[cfg(test)] + /// Prohibit given requestor access to given document + pub fn prohibit(&self, public: Public, document: ServerKeyId) { + self.prohibited.write() + .entry(public) + .or_insert_with(Default::default) + .insert(document); + } +} + +impl AclStorage for DummyAclStorage { + fn check(&self, public: &Public, document: &ServerKeyId) -> Result { + Ok(self.prohibited.read() + .get(public) + .map(|docs| !docs.contains(document)) + .unwrap_or(true)) } } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 6526bff68..f9fad19df 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -196,7 +196,7 @@ pub mod tests { use std::collections::BTreeMap; use ethcrypto; use ethkey::{self, Secret, Random, Generator}; - use acl_storage::tests::DummyAclStorage; + use acl_storage::DummyAclStorage; use key_storage::tests::DummyKeyStorage; use node_key_pair::PlainNodeKeyPair; use key_server_set::tests::MapKeyServerSet; diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 6a806bb92..afc73f858 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -467,7 +467,7 @@ impl Ord for DecryptionSessionId { mod tests { use std::sync::Arc; use std::collections::BTreeMap; - use super::super::super::acl_storage::tests::DummyAclStorage; + use super::super::super::acl_storage::DummyAclStorage; use ethkey::{self, KeyPair, Random, Generator, Public, Secret}; use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::tests::DummyCluster; diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 102c3672f..4fcda1539 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -36,7 +36,7 @@ pub use super::node_key_pair::PlainNodeKeyPair; #[cfg(test)] pub use super::key_storage::tests::DummyKeyStorage; #[cfg(test)] -pub use super::acl_storage::tests::DummyAclStorage; +pub use super::acl_storage::DummyAclStorage; #[cfg(test)] pub use super::key_server_set::tests::MapKeyServerSet; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 00246ae64..e647c8b14 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -572,7 +572,7 @@ mod tests { use std::collections::{BTreeMap, VecDeque}; use ethkey::{self, Random, Generator, Public}; use util::H256; - use super::super::super::acl_storage::tests::DummyAclStorage; + use super::super::super::acl_storage::DummyAclStorage; use key_server_cluster::{NodeId, SessionId, SessionMeta, Error, KeyStorage}; use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::generation_session::{Session as GenerationSession}; diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index 20b5eaf6c..2fad4cdf7 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -235,6 +235,7 @@ pub mod tests { let path = RandomTempPath::create_dir(); let config = ServiceConfiguration { listener_address: None, + acl_check_enabled: true, data_path: path.as_str().to_owned(), cluster_config: ClusterConfiguration { threads: 1, diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 6ead7c657..d7f35a55a 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -73,7 +73,11 @@ pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair}; pub fn start(client: Arc, self_key_pair: Arc, config: ServiceConfiguration) -> Result, Error> { use std::sync::Arc; - let acl_storage = acl_storage::OnChainAclStorage::new(&client); + let acl_storage: Arc = if config.acl_check_enabled { + acl_storage::OnChainAclStorage::new(&client) + } else { + Arc::new(acl_storage::DummyAclStorage::default()) + }; let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone())?; let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?); let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair, acl_storage, key_storage)?; diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 6bc0d9c87..6867c82f3 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -71,6 +71,8 @@ pub struct NodeAddress { pub struct ServiceConfiguration { /// HTTP listener address. If None, HTTP API is disabled. pub listener_address: Option, + /// Is ACL check enabled. If false, everyone has access to all keys. Useful for tests only. + pub acl_check_enabled: bool, /// Data directory path for secret store pub data_path: String, /// Cluster configuration. From 47c058a337f760e7f3435fc6d5dbaca12340da66 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 2 Aug 2017 12:05:47 +0300 Subject: [PATCH 25/28] fixed warning --- secret_store/src/key_server_cluster/io/deadline.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/secret_store/src/key_server_cluster/io/deadline.rs b/secret_store/src/key_server_cluster/io/deadline.rs index 501a69057..10f485b7d 100644 --- a/secret_store/src/key_server_cluster/io/deadline.rs +++ b/secret_store/src/key_server_cluster/io/deadline.rs @@ -19,7 +19,7 @@ use std::time::Duration; use futures::{Future, Select, BoxFuture, Poll, Async}; use tokio_core::reactor::{Handle, Timeout}; -type DeadlineBox where F: Future = BoxFuture, F::Error>; +type DeadlineBox = BoxFuture::Item>, ::Error>; /// Complete a passed future or fail if it is not completed within timeout. pub fn deadline(duration: Duration, handle: &Handle, future: F) -> Result, io::Error> From 45087599efdf8ac5f80f6e1df112d747131b91c0 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 9 Aug 2017 12:33:41 +0300 Subject: [PATCH 26/28] lost commit --- Cargo.lock | 1 - ethstore/Cargo.toml | 1 - ethstore/src/lib.rs | 1 - 3 files changed, 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f5ed0a21..e2b4da1eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -847,7 +847,6 @@ name = "ethstore" version = "0.1.0" dependencies = [ "ethcore-bigint 0.1.3", - "ethcore-util 1.8.0", "ethcrypto 0.1.0", "ethkey 0.2.0", "itertools 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/ethstore/Cargo.toml b/ethstore/Cargo.toml index 0f91a663c..200dec366 100755 --- a/ethstore/Cargo.toml +++ b/ethstore/Cargo.toml @@ -19,7 +19,6 @@ itertools = "0.5" parking_lot = "0.4" ethcrypto = { path = "../ethcrypto" } ethcore-bigint = { path = "../util/bigint" } -ethcore-util = { path = "../util" } smallvec = "0.4" parity-wordlist = "1.0" tempdir = "0.3" diff --git a/ethstore/src/lib.rs b/ethstore/src/lib.rs index 311e9e73a..65935f89c 100755 --- a/ethstore/src/lib.rs +++ b/ethstore/src/lib.rs @@ -35,7 +35,6 @@ extern crate ethcore_bigint as bigint; extern crate ethcrypto as crypto; extern crate ethkey as _ethkey; extern crate parity_wordlist; -extern crate ethcore_util as util; #[macro_use] extern crate log; From cc95edf4dce6cdc40ab013744837f5602d55a8cb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 9 Aug 2017 18:17:28 +0300 Subject: [PATCH 27/28] fixed grumbles --- parity/secretstore.rs | 5 +++++ secret_store/src/http_listener.rs | 6 ------ secret_store/src/key_server_cluster/decryption_session.rs | 2 +- secret_store/src/key_server_cluster/signing_session.rs | 2 +- secret_store/src/key_storage.rs | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/parity/secretstore.rs b/parity/secretstore.rs index 8094ef323..eb5922540 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -87,6 +87,7 @@ mod server { use std::sync::Arc; use ethcore_secretstore; use ethkey::KeyPair; + use ansi_term::Colour::Red; use super::{Configuration, Dependencies, NodeSecretKey}; /// Key server @@ -97,6 +98,10 @@ mod server { impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies) -> Result { + if !conf.acl_check_enabled { + warn!("Running SecretStore with disabled ACL check: {}", Red.bold().paint("everyone has access to stored keys")); + } + let self_secret: Arc = match conf.self_secret.take() { Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 86688618a..883389365 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -78,9 +78,6 @@ impl KeyServerHttpListener where T: KeyServer + 'static { let shared_handler = Arc::new(KeyServerSharedHttpHandler { key_server: key_server, }); - /*let handler = KeyServerHttpHandler { - handler: shared_handler.clone(), - };*/ let http_server = listener_address .map(|listener_address| format!("{}:{}", listener_address.address, listener_address.port)) @@ -89,9 +86,6 @@ impl KeyServerHttpListener where T: KeyServer + 'static { handler: shared_handler.clone(), }).expect("cannot start HttpServer")); - /*let listener_addr: &str = &format!("{}:{}", listener_address.address, listener_address.port); - let http_server = HttpServer::http(&listener_addr).expect("cannot start HttpServer"); - let http_server = http_server.handle(handler).expect("cannot start HttpServer");*/ let listener = KeyServerHttpListener { http_server: http_server, handler: shared_handler, diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index afc73f858..bc3c6aad0 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -467,7 +467,7 @@ impl Ord for DecryptionSessionId { mod tests { use std::sync::Arc; use std::collections::BTreeMap; - use super::super::super::acl_storage::DummyAclStorage; + use acl_storage::DummyAclStorage; use ethkey::{self, KeyPair, Random, Generator, Public, Secret}; use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::tests::DummyCluster; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index e647c8b14..e56306142 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -572,7 +572,7 @@ mod tests { use std::collections::{BTreeMap, VecDeque}; use ethkey::{self, Random, Generator, Public}; use util::H256; - use super::super::super::acl_storage::DummyAclStorage; + use acl_storage::DummyAclStorage; use key_server_cluster::{NodeId, SessionId, SessionMeta, Error, KeyStorage}; use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::generation_session::{Session as GenerationSession}; diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index 2fad4cdf7..fdbb5fa40 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -200,7 +200,7 @@ pub mod tests { use devtools::RandomTempPath; use ethkey::{Random, Generator, Public, Secret}; use util::Database; - use super::super::types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; + use types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; use super::{DB_META_KEY_VERSION, KeyStorage, PersistentKeyStorage, DocumentKeyShare, SerializableDocumentKeyShareV0, SerializableDocumentKeyShareV1, upgrade_db}; From a02db13278eb831adf8bcb32aa95fa8658733e51 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 16 Aug 2017 09:40:00 +0300 Subject: [PATCH 28/28] moved attributes under docs --- secret_store/src/acl_storage.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index 0a3ee9276..5b8284c6f 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -48,8 +48,8 @@ struct CachedContract { contract: Option, } -#[derive(Default, Debug)] /// Dummy ACL storage implementation (check always passed). +#[derive(Default, Debug)] pub struct DummyAclStorage { prohibited: RwLock>>, } @@ -121,8 +121,8 @@ impl CachedContract { } impl DummyAclStorage { + /// Prohibit given requestor access to given documents #[cfg(test)] - /// Prohibit given requestor access to given document pub fn prohibit(&self, public: Public, document: ServerKeyId) { self.prohibited.write() .entry(public)