Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

SecretStore: encrypt messages using private key from key store #6146

Merged
merged 21 commits into from
Aug 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions ethcore/src/account_provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,11 @@ impl AccountProvider {
}
}

/// Returns account public key.
pub fn account_public(&self, address: Address, password: &str) -> Result<Public, Error> {
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)?;
Expand Down Expand Up @@ -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<String>, other_public: &Public) -> Result<Secret, SignError> {
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<Address> {
self.sstore.list_geth_accounts(testnet).into_iter().map(|a| Address::from(a).into()).collect()
Expand Down
9 changes: 8 additions & 1 deletion ethstore/src/account/safe_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

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;
Expand Down Expand Up @@ -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<Secret, Error> {
let secret = self.crypto.secret(password)?;
agree(&secret, other).map_err(From::from)
}

/// Derive public key.
pub fn public(&self, password: &str) -> Result<Public, Error> {
let secret = self.crypto.secret(password)?;
Expand Down
24 changes: 18 additions & 6 deletions ethstore/src/ethstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Secret, Error> {
self.store.agree(account, password, other)
}

fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error> {
let account = self.get(account)?;
account.decrypt(password, shared_mac, message)
Expand Down Expand Up @@ -495,18 +499,26 @@ impl SimpleSecretStore for EthMultiStore {

fn sign(&self, account: &StoreAccountRef, password: &str, message: &Message) -> Result<Signature, Error> {
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<Vec<u8>, 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<Secret, Error> {
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> {
Expand Down
2 changes: 2 additions & 0 deletions ethstore/src/secret_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub trait SimpleSecretStore: Send + Sync {
fn sign_derived(&self, account_ref: &StoreAccountRef, password: &str, derivation: Derivation, message: &Message) -> Result<Signature, Error>;
/// Decrypt a messages with given account.
fn decrypt(&self, account: &StoreAccountRef, password: &str, shared_mac: &[u8], message: &[u8]) -> Result<Vec<u8>, Error>;
/// Agree on shared key.
fn agree(&self, account: &StoreAccountRef, password: &str, other: &Public) -> Result<Secret, Error>;

/// Returns all accounts in this secret store.
fn accounts(&self) -> Result<Vec<StoreAccountRef>, Error>;
Expand Down
11 changes: 7 additions & 4 deletions parity/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -989,10 +989,13 @@ impl Configuration {
self.interface(&self.args.flag_secretstore_http_interface)
}

fn secretstore_self_secret(&self) -> Result<Option<Secret>, String> {
fn secretstore_self_secret(&self) -> Result<Option<NodeSecretKey>, 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),
}
}
Expand Down
4 changes: 3 additions & 1 deletion parity/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,7 +507,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> 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));
}
}
Expand Down Expand Up @@ -734,6 +734,8 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> 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)?;

Expand Down
56 changes: 46 additions & 10 deletions parity/secretstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,28 @@
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
pub struct Configuration {
/// Is secret store functionality enabled?
pub enabled: bool,
/// This node secret.
pub self_secret: Option<Secret>,
pub self_secret: Option<NodeSecretKey>,
/// Other nodes IDs + addresses.
pub nodes: BTreeMap<Public, (String, u16)>,
/// Interface to listen to
Expand All @@ -43,9 +54,13 @@ pub struct Configuration {
}

/// Secret store dependencies
pub struct Dependencies {
pub struct Dependencies<'a> {
/// Blockchain client.
pub client: Arc<Client>,
/// Account provider.
pub account_provider: Arc<AccountProvider>,
/// Passed accounts passwords.
pub accounts_passwords: &'a [String],
}

#[cfg(not(feature = "secretstore"))]
Expand All @@ -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 {
Expand All @@ -76,8 +92,31 @@ mod server {

impl KeyServer {
/// Create new key server
pub fn new(conf: Configuration, deps: Dependencies) -> Result<Self, String> {
let self_secret = conf.self_secret.ok_or("self secret is required when using secretstore")?;
pub fn new(mut conf: Configuration, deps: Dependencies) -> Result<Self, String> {
let self_secret: Arc<ethcore_secretstore::NodeKeyPair> = 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(),
Expand All @@ -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,
Expand All @@ -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::<String>::into)?;

Ok(KeyServer {
Expand Down
19 changes: 11 additions & 8 deletions secret_store/src/key_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -45,9 +45,9 @@ pub struct KeyServerCore {

impl KeyServerImpl {
/// Create new key server instance
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<KeyServerSet>, acl_storage: Arc<AclStorage>, key_storage: Arc<KeyStorage>) -> Result<Self, Error> {
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<KeyServerSet>, self_key_pair: Arc<NodeKeyPair>, acl_storage: Arc<AclStorage>, key_storage: Arc<KeyStorage>) -> Result<Self, Error> {
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)?)),
})
}

Expand Down Expand Up @@ -144,10 +144,10 @@ impl MessageSigner for KeyServerImpl {
}

impl KeyServerCore {
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<KeyServerSet>, acl_storage: Arc<AclStorage>, key_storage: Arc<KeyStorage>) -> Result<Self, Error> {
pub fn new(config: &ClusterConfiguration, key_server_set: Arc<KeyServerSet>, self_key_pair: Arc<NodeKeyPair>, acl_storage: Arc<AclStorage>, key_storage: Arc<KeyStorage>) -> Result<Self, Error> {
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,
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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),
Expand All @@ -259,8 +259,11 @@ pub mod tests {
let key_servers_set: BTreeMap<Public, SocketAddr> = 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
Expand Down
10 changes: 5 additions & 5 deletions secret_store/src/key_server_cluster/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<NodeKeyPair>,
/// Interface to listen to.
pub listen_address: (String, u16),
/// Cluster nodes set.
Expand Down Expand Up @@ -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<NodeKeyPair>,
/// Connections data.
connections: ClusterConnections,
/// Active sessions data.
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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()))
Expand Down
Loading