diff --git a/ethcore/src/account_provider/mod.rs b/ethcore/src/account_provider/mod.rs index 249ca40aff7..752cec9649f 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)?; @@ -697,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/src/account/safe_account.rs b/ethstore/src/account/safe_account.rs index e0512fe8d46..478b796e646 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 246671990d9..f3bb2407134 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) @@ -495,18 +499,26 @@ impl SimpleSecretStore for EthMultiStore { fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result { let accounts = self.get_matching(account, password)?; - for account in accounts { - return account.sign(password, message); + match accounts.first() { + Some(ref account) => account.sign(password, message), + None => Err(Error::InvalidPassword), } - Err(Error::InvalidPassword) } fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result, Error> { let accounts = self.get_matching(account, password)?; - for account in accounts { - return account.decrypt(password, shared_mac, message); + match accounts.first() { + Some(ref account) => account.decrypt(password, shared_mac, message), + None => Err(Error::InvalidPassword), + } + } + + fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result { + let accounts = self.get_matching(account, password)?; + match accounts.first() { + Some(ref account) => account.agree(password, other), + None => Err(Error::InvalidPassword), } - Err(Error::InvalidPassword) } fn create_vault(&self, name: &str, password: &str) -> Result<(), Error> { diff --git a/ethstore/src/secret_store.rs b/ethstore/src/secret_store.rs index 2deae023e80..e364245b799 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/parity/configuration.rs b/parity/configuration.rs index 7a80f8f9328..3e9599c12ce 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}; @@ -989,10 +989,13 @@ 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) 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 c08294a428b..9596cea5dbe 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -507,7 +507,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)); } } @@ -734,6 +734,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 f215c937c23..def2cd1a6da 100644 --- a/parity/secretstore.rs +++ b/parity/secretstore.rs @@ -17,9 +17,20 @@ 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)] /// Secret store configuration @@ -27,7 +38,7 @@ 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 @@ -43,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"))] @@ -65,9 +80,10 @@ mod server { #[cfg(feature="secretstore")] mod server { + use std::sync::Arc; use ethcore_secretstore; use ethkey::KeyPair; - use super::{Configuration, Dependencies}; + use super::{Configuration, Dependencies, NodeSecretKey}; /// Key server pub struct KeyServer { @@ -76,8 +92,31 @@ 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: 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()), + }; + let mut conf = ethcore_secretstore::ServiceConfiguration { listener_address: ethcore_secretstore::NodeAddress { address: conf.http_interface.clone(), @@ -86,7 +125,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, @@ -99,11 +137,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 key_server = ethcore_secretstore::start(deps.client, conf) + let key_server = ethcore_secretstore::start(deps.client, self_secret, conf) .map_err(Into::::into)?; Ok(KeyServer { diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index c83e460f313..6526bff6817 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,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_private: (***key_pairs[i].secret()).into(), listener_address: NodeAddress { address: "127.0.0.1".into(), port: start_port + (i as u16), @@ -259,8 +259,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 d77a8243108..155dd4a011b 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 df8f6cbf7e2..bf52ab798e6 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 49b71e39d8d..5a6b50a3ee8 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 57071038e53..dfea336830f 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 8f6ae4addb8..102c3672fc8 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 339625f3ff6..d85e492dd7b 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 449168ab268..7515494e449 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 d5af7a5fa1a..08ebe6e1c98 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, diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 9750f7223f0..7e9897e6068 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 00000000000..ce6c88a07b7 --- /dev/null +++ b/secret_store/src/node_key_pair.rs @@ -0,0 +1,84 @@ +// 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::{Address, H256}; +use traits::NodeKeyPair; + +pub struct PlainNodeKeyPair { + key_pair: KeyPair, +} + +pub struct KeyStoreNodeKeyPair { + account_provider: Arc, + address: Address, + public: Public, + password: String, +} + +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 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 { + &self.public + } + + 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 { + KeyPair::from_secret(self.account_provider.agree(self.address.clone(), Some(self.password.clone()), peer_public) + .map_err(|e| EthKeyError::Custom(format!("{}", e)))?) + } +} diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 33a4eff3c7a..31da748e010 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 54fc8acaed6..8dc92f175e3 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.