Secret store - initial version (#4567)
* initial secret store commit * various fixes * license * (sstore, secstore) -> secretstore * marked KeyServer trait as IPC-ready * fixed style * ignore requests with Origin header * fixed tests * fixed Origin header check
This commit is contained in:
parent
68a25d9e14
commit
b9665c7cfe
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -20,6 +20,7 @@ dependencies = [
|
||||
"ethcore-light 1.6.0",
|
||||
"ethcore-logger 1.6.0",
|
||||
"ethcore-rpc 1.6.0",
|
||||
"ethcore-secretstore 1.0.0",
|
||||
"ethcore-signer 1.6.0",
|
||||
"ethcore-stratum 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
@ -635,6 +636,23 @@ dependencies = [
|
||||
"transient-hashmap 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-secretstore"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"ethcore-devtools 1.6.0",
|
||||
"ethcore-ipc 1.6.0",
|
||||
"ethcore-ipc-codegen 1.6.0",
|
||||
"ethcore-ipc-nano 1.6.0",
|
||||
"ethcore-util 1.6.0",
|
||||
"ethcrypto 0.1.0",
|
||||
"ethkey 0.2.0",
|
||||
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ethcore-signer"
|
||||
version = "1.6.0"
|
||||
|
@ -49,6 +49,7 @@ parity-updater = { path = "updater" }
|
||||
parity-reactor = { path = "util/reactor" }
|
||||
ethcore-dapps = { path = "dapps", optional = true }
|
||||
clippy = { version = "0.0.103", optional = true}
|
||||
ethcore-secretstore = { path = "secret_store", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
ethcore-ipc-tests = { path = "ipc/tests" }
|
||||
@ -83,6 +84,7 @@ evm-debug = ["ethcore/evm-debug"]
|
||||
evm-debug-tests = ["ethcore/evm-debug-tests"]
|
||||
slow-blocks = ["ethcore/slow-blocks"]
|
||||
final = ["ethcore-util/final"]
|
||||
secretstore = ["ethcore-secretstore"]
|
||||
|
||||
[[bin]]
|
||||
path = "parity/main.rs"
|
||||
|
@ -185,7 +185,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> {
|
||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()))?;
|
||||
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(false, false)?;
|
||||
cmd.dirs.create_dirs(false, false, false)?;
|
||||
|
||||
// prepare client config
|
||||
let mut client_config = to_client_config(
|
||||
@ -356,7 +356,7 @@ fn start_client(
|
||||
execute_upgrades(&dirs.base, &db_dirs, algorithm, compaction.compaction_profile(db_dirs.db_root_path().as_path()))?;
|
||||
|
||||
// create dirs used by parity
|
||||
dirs.create_dirs(false, false)?;
|
||||
dirs.create_dirs(false, false, false)?;
|
||||
|
||||
// prepare client config
|
||||
let client_config = to_client_config(
|
||||
|
@ -67,6 +67,12 @@ path = "$HOME/.parity/dapps"
|
||||
user = "test_user"
|
||||
pass = "test_pass"
|
||||
|
||||
[secretstore]
|
||||
disable = false
|
||||
port = 8082
|
||||
interface = "local"
|
||||
path = "$HOME/.parity/secretstore"
|
||||
|
||||
[ipfs]
|
||||
enable = false
|
||||
port = 5001
|
||||
|
@ -38,6 +38,9 @@ port = 8080
|
||||
user = "username"
|
||||
pass = "password"
|
||||
|
||||
[secretstore]
|
||||
port = 8082
|
||||
|
||||
[ipfs]
|
||||
enable = false
|
||||
port = 5001
|
||||
|
@ -189,6 +189,16 @@ usage! {
|
||||
or |c: &Config| otry!(c.dapps).pass.clone().map(Some),
|
||||
flag_dapps_apis_all: bool = false, or |_| None,
|
||||
|
||||
// Secret Store
|
||||
flag_no_secretstore: bool = false,
|
||||
or |c: &Config| otry!(c.secretstore).disable.clone(),
|
||||
flag_secretstore_port: u16 = 8082u16,
|
||||
or |c: &Config| otry!(c.secretstore).port.clone(),
|
||||
flag_secretstore_interface: String = "local",
|
||||
or |c: &Config| otry!(c.secretstore).interface.clone(),
|
||||
flag_secretstore_path: String = "$BASE/secretstore",
|
||||
or |c: &Config| otry!(c.secretstore).path.clone(),
|
||||
|
||||
// IPFS
|
||||
flag_ipfs_api: bool = false,
|
||||
or |c: &Config| otry!(c.ipfs).enable.clone(),
|
||||
@ -327,6 +337,7 @@ struct Config {
|
||||
rpc: Option<Rpc>,
|
||||
ipc: Option<Ipc>,
|
||||
dapps: Option<Dapps>,
|
||||
secretstore: Option<SecretStore>,
|
||||
ipfs: Option<Ipfs>,
|
||||
mining: Option<Mining>,
|
||||
footprint: Option<Footprint>,
|
||||
@ -416,6 +427,14 @@ struct Dapps {
|
||||
pass: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
||||
struct SecretStore {
|
||||
disable: Option<bool>,
|
||||
port: Option<u16>,
|
||||
interface: Option<String>,
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, RustcDecodable)]
|
||||
struct Ipfs {
|
||||
enable: Option<bool>,
|
||||
@ -495,7 +514,8 @@ struct Misc {
|
||||
mod tests {
|
||||
use super::{
|
||||
Args, ArgsError,
|
||||
Config, Operating, Account, Ui, Network, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint, Snapshots, VM, Misc
|
||||
Config, Operating, Account, Ui, Network, Rpc, Ipc, Dapps, Ipfs, Mining, Footprint,
|
||||
Snapshots, VM, Misc, SecretStore,
|
||||
};
|
||||
use toml;
|
||||
|
||||
@ -650,6 +670,11 @@ mod tests {
|
||||
flag_dapps_pass: Some("test_pass".into()),
|
||||
flag_dapps_apis_all: false,
|
||||
|
||||
flag_no_secretstore: false,
|
||||
flag_secretstore_port: 8082u16,
|
||||
flag_secretstore_interface: "local".into(),
|
||||
flag_secretstore_path: "$HOME/.parity/secretstore".into(),
|
||||
|
||||
// IPFS
|
||||
flag_ipfs_api: false,
|
||||
flag_ipfs_api_port: 5001u16,
|
||||
@ -839,6 +864,12 @@ mod tests {
|
||||
user: Some("username".into()),
|
||||
pass: Some("password".into())
|
||||
}),
|
||||
secretstore: Some(SecretStore {
|
||||
disable: None,
|
||||
port: Some(8082),
|
||||
interface: None,
|
||||
path: None,
|
||||
}),
|
||||
ipfs: Some(Ipfs {
|
||||
enable: Some(false),
|
||||
port: Some(5001)
|
||||
|
@ -179,6 +179,16 @@ API and Console Options:
|
||||
--ipfs-api-port PORT Configure on which port the IPFS HTTP API should listen.
|
||||
(default: {flag_ipfs_api_port})
|
||||
|
||||
Secret Store Options:
|
||||
--no-secretstore Disable Secret Store functionality. (default: {flag_no_secretstore})
|
||||
--secretstore-port PORT Specify the port portion for Secret Store Key Server
|
||||
(default: {flag_secretstore_port}).
|
||||
--secretstore-interface IP Specify the hostname portion for Secret Store Key Server, IP
|
||||
should be an interface's IP address, or local
|
||||
(default: {flag_secretstore_interface}).
|
||||
--secretstore-path PATH Specify directory where Secret Store should save its data.
|
||||
(default: {flag_secretstore_path})
|
||||
|
||||
Sealing/Mining Options:
|
||||
--author ADDRESS Specify the block author (aka "coinbase") address
|
||||
for sending block rewards from sealed blocks.
|
||||
|
@ -39,6 +39,7 @@ use dir::{self, Directories, default_hypervisor_path, default_local_path, defaul
|
||||
use dapps::Configuration as DappsConfiguration;
|
||||
use ipfs::Configuration as IpfsConfiguration;
|
||||
use signer::{Configuration as SignerConfiguration};
|
||||
use secretstore::Configuration as SecretStoreConfiguration;
|
||||
use updater::{UpdatePolicy, UpdateFilter, ReleaseTrack};
|
||||
use run::RunCmd;
|
||||
use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, KillBlockchain, ExportState, DataFormat};
|
||||
@ -121,6 +122,7 @@ impl Configuration {
|
||||
let dapps_conf = self.dapps_config();
|
||||
let ipfs_conf = self.ipfs_config();
|
||||
let signer_conf = self.signer_config();
|
||||
let secretstore_conf = self.secretstore_config();
|
||||
let format = self.format()?;
|
||||
|
||||
let cmd = if self.args.flag_version {
|
||||
@ -346,6 +348,7 @@ impl Configuration {
|
||||
dapps_conf: dapps_conf,
|
||||
ipfs_conf: ipfs_conf,
|
||||
signer_conf: signer_conf,
|
||||
secretstore_conf: secretstore_conf,
|
||||
dapp: self.dapp_to_open()?,
|
||||
ui: self.args.cmd_ui,
|
||||
name: self.args.flag_identity,
|
||||
@ -542,6 +545,15 @@ impl Configuration {
|
||||
}
|
||||
}
|
||||
|
||||
fn secretstore_config(&self) -> SecretStoreConfiguration {
|
||||
SecretStoreConfiguration {
|
||||
enabled: self.secretstore_enabled(),
|
||||
interface: self.secretstore_interface(),
|
||||
port: self.args.flag_secretstore_port,
|
||||
data_path: self.directories().secretstore,
|
||||
}
|
||||
}
|
||||
|
||||
fn ipfs_config(&self) -> IpfsConfiguration {
|
||||
IpfsConfiguration {
|
||||
enabled: self.args.flag_ipfs_api,
|
||||
@ -787,6 +799,7 @@ impl Configuration {
|
||||
let db_path = replace_home_for_db(&data_path, &local_path, &base_db_path);
|
||||
let keys_path = replace_home(&data_path, &self.args.flag_keys_path);
|
||||
let dapps_path = replace_home(&data_path, &self.args.flag_dapps_path);
|
||||
let secretstore_path = replace_home(&data_path, &self.args.flag_secretstore_path);
|
||||
let ui_path = replace_home(&data_path, &self.args.flag_ui_path);
|
||||
|
||||
if self.args.flag_geth && !cfg!(windows) {
|
||||
@ -810,6 +823,7 @@ impl Configuration {
|
||||
db: db_path,
|
||||
dapps: dapps_path,
|
||||
signer: ui_path,
|
||||
secretstore: secretstore_path,
|
||||
}
|
||||
}
|
||||
|
||||
@ -851,6 +865,13 @@ impl Configuration {
|
||||
}.into()
|
||||
}
|
||||
|
||||
fn secretstore_interface(&self) -> String {
|
||||
match self.args.flag_secretstore_interface.as_str() {
|
||||
"local" => "127.0.0.1",
|
||||
x => x,
|
||||
}.into()
|
||||
}
|
||||
|
||||
fn stratum_interface(&self) -> String {
|
||||
match self.args.flag_stratum_interface.as_str() {
|
||||
"local" => "127.0.0.1",
|
||||
@ -863,6 +884,10 @@ impl Configuration {
|
||||
!self.args.flag_dapps_off && !self.args.flag_no_dapps && cfg!(feature = "dapps")
|
||||
}
|
||||
|
||||
fn secretstore_enabled(&self) -> bool {
|
||||
!self.args.flag_no_secretstore && cfg!(feature = "secretstore")
|
||||
}
|
||||
|
||||
fn ui_enabled(&self) -> bool {
|
||||
if self.args.flag_force_ui {
|
||||
return true;
|
||||
@ -1083,7 +1108,7 @@ mod tests {
|
||||
fn test_run_cmd() {
|
||||
let args = vec!["parity"];
|
||||
let conf = parse(&args);
|
||||
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(RunCmd {
|
||||
let mut expected = RunCmd {
|
||||
cache_config: Default::default(),
|
||||
dirs: Default::default(),
|
||||
spec: Default::default(),
|
||||
@ -1113,6 +1138,7 @@ mod tests {
|
||||
dapps_conf: Default::default(),
|
||||
ipfs_conf: Default::default(),
|
||||
signer_conf: Default::default(),
|
||||
secretstore_conf: Default::default(),
|
||||
ui: false,
|
||||
dapp: None,
|
||||
name: "".into(),
|
||||
@ -1123,7 +1149,9 @@ mod tests {
|
||||
check_seal: true,
|
||||
download_old_blocks: true,
|
||||
verifier_settings: Default::default(),
|
||||
}));
|
||||
};
|
||||
expected.secretstore_conf.enabled = cfg!(feature = "secretstore");
|
||||
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -45,6 +45,7 @@ pub struct Directories {
|
||||
pub keys: String,
|
||||
pub signer: String,
|
||||
pub dapps: String,
|
||||
pub secretstore: String,
|
||||
}
|
||||
|
||||
impl Default for Directories {
|
||||
@ -57,12 +58,13 @@ impl Default for Directories {
|
||||
keys: replace_home(&data_dir, "$BASE/keys"),
|
||||
signer: replace_home(&data_dir, "$BASE/signer"),
|
||||
dapps: replace_home(&data_dir, "$BASE/dapps"),
|
||||
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Directories {
|
||||
pub fn create_dirs(&self, dapps_enabled: bool, signer_enabled: bool) -> Result<(), String> {
|
||||
pub fn create_dirs(&self, dapps_enabled: bool, signer_enabled: bool, secretstore_enabled: bool) -> Result<(), String> {
|
||||
fs::create_dir_all(&self.base).map_err(|e| e.to_string())?;
|
||||
fs::create_dir_all(&self.db).map_err(|e| e.to_string())?;
|
||||
fs::create_dir_all(&self.keys).map_err(|e| e.to_string())?;
|
||||
@ -72,6 +74,9 @@ impl Directories {
|
||||
if dapps_enabled {
|
||||
fs::create_dir_all(&self.dapps).map_err(|e| e.to_string())?;
|
||||
}
|
||||
if secretstore_enabled {
|
||||
fs::create_dir_all(&self.secretstore).map_err(|e| e.to_string())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -241,6 +246,7 @@ mod tests {
|
||||
keys: replace_home(&data_dir, "$BASE/keys"),
|
||||
signer: replace_home(&data_dir, "$BASE/signer"),
|
||||
dapps: replace_home(&data_dir, "$BASE/dapps"),
|
||||
secretstore: replace_home(&data_dir, "$BASE/secretstore"),
|
||||
};
|
||||
assert_eq!(expected, Directories::default());
|
||||
}
|
||||
|
@ -66,6 +66,10 @@ extern crate log as rlog;
|
||||
|
||||
#[cfg(feature="stratum")]
|
||||
extern crate ethcore_stratum;
|
||||
|
||||
#[cfg(feature="secretstore")]
|
||||
extern crate ethcore_secretstore;
|
||||
|
||||
#[cfg(feature = "dapps")]
|
||||
extern crate ethcore_dapps;
|
||||
|
||||
@ -101,6 +105,7 @@ mod rpc_apis;
|
||||
mod run;
|
||||
mod signer;
|
||||
mod snapshot;
|
||||
mod secretstore;
|
||||
mod upgrade;
|
||||
mod url;
|
||||
mod user_defaults;
|
||||
|
@ -49,6 +49,7 @@ use user_defaults::UserDefaults;
|
||||
use dapps;
|
||||
use ipfs;
|
||||
use signer;
|
||||
use secretstore;
|
||||
use modules;
|
||||
use rpc_apis;
|
||||
use rpc;
|
||||
@ -96,6 +97,7 @@ pub struct RunCmd {
|
||||
pub dapps_conf: dapps::Configuration,
|
||||
pub ipfs_conf: ipfs::Configuration,
|
||||
pub signer_conf: signer::Configuration,
|
||||
pub secretstore_conf: secretstore::Configuration,
|
||||
pub dapp: Option<String>,
|
||||
pub ui: bool,
|
||||
pub name: String,
|
||||
@ -190,7 +192,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path()))?;
|
||||
|
||||
// create dirs used by parity
|
||||
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.signer_conf.enabled)?;
|
||||
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.signer_conf.enabled, cmd.secretstore_conf.enabled)?;
|
||||
|
||||
// run in daemon mode
|
||||
if let Some(pid_file) = cmd.daemon {
|
||||
@ -422,6 +424,10 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
};
|
||||
let signer_server = signer::start(cmd.signer_conf.clone(), signer_deps)?;
|
||||
|
||||
// secret store key server
|
||||
let secretstore_deps = secretstore::Dependencies { };
|
||||
let secretstore_key_server = secretstore::start(cmd.secretstore_conf.clone(), secretstore_deps);
|
||||
|
||||
// the ipfs server
|
||||
let ipfs_server = match cmd.ipfs_conf.enabled {
|
||||
true => Some(ipfs::start_server(cmd.ipfs_conf.port, client.clone())?),
|
||||
@ -484,7 +490,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
let restart = wait_for_exit(panic_handler, Some(updater), can_restart);
|
||||
|
||||
// drop this stuff as soon as exit detected.
|
||||
drop((http_server, ipc_server, dapps_server, signer_server, ipfs_server, event_loop));
|
||||
drop((http_server, ipc_server, dapps_server, signer_server, secretstore_key_server, ipfs_server, event_loop));
|
||||
|
||||
info!("Finishing work, please wait...");
|
||||
|
||||
|
105
parity/secretstore.rs
Normal file
105
parity/secretstore.rs
Normal file
@ -0,0 +1,105 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use dir::default_data_path;
|
||||
use helpers::replace_home;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Secret store configuration
|
||||
pub struct Configuration {
|
||||
/// Is secret store functionality enabled?
|
||||
pub enabled: bool,
|
||||
/// Interface to listen to
|
||||
pub interface: String,
|
||||
/// Port to listen to
|
||||
pub port: u16,
|
||||
/// Data directory path for secret store
|
||||
pub data_path: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
/// Secret store dependencies
|
||||
pub struct Dependencies {
|
||||
// the only dependency will be BlockChainClient
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "secretstore"))]
|
||||
mod server {
|
||||
use super::{Configuration, Dependencies};
|
||||
|
||||
/// Noop key server implementation
|
||||
pub struct KeyServer;
|
||||
|
||||
impl KeyServer {
|
||||
/// Create new noop key server
|
||||
pub fn new(_conf: Configuration, _deps: Dependencies) -> Result<Self, String> {
|
||||
Ok(KeyServer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="secretstore")]
|
||||
mod server {
|
||||
use ethcore_secretstore;
|
||||
use super::{Configuration, Dependencies};
|
||||
|
||||
/// Key server
|
||||
pub struct KeyServer {
|
||||
_key_server: Box<ethcore_secretstore::KeyServer>,
|
||||
}
|
||||
|
||||
impl KeyServer {
|
||||
/// Create new key server
|
||||
pub fn new(conf: Configuration, _deps: Dependencies) -> Result<Self, String> {
|
||||
let conf = ethcore_secretstore::ServiceConfiguration {
|
||||
listener_addr: conf.interface,
|
||||
listener_port: conf.port,
|
||||
data_path: conf.data_path,
|
||||
};
|
||||
|
||||
let key_server = ethcore_secretstore::start(conf)
|
||||
.map_err(Into::<String>::into)?;
|
||||
|
||||
Ok(KeyServer {
|
||||
_key_server: key_server,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub use self::server::KeyServer;
|
||||
|
||||
impl Default for Configuration {
|
||||
fn default() -> Self {
|
||||
let data_dir = default_data_path();
|
||||
Configuration {
|
||||
enabled: true,
|
||||
interface: "127.0.0.1".to_owned(),
|
||||
port: 8082,
|
||||
data_path: replace_home(&data_dir, "$BASE/secretstore"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start secret store-related functionality
|
||||
pub fn start(conf: Configuration, deps: Dependencies) -> Result<Option<KeyServer>, String> {
|
||||
if !conf.enabled {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
KeyServer::new(conf, deps)
|
||||
.map(|s| Some(s))
|
||||
}
|
25
secret_store/Cargo.toml
Normal file
25
secret_store/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
description = "Ethcore Secret Store"
|
||||
name = "ethcore-secretstore"
|
||||
version = "1.0.0"
|
||||
license = "GPL-3.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
ethcore-ipc-codegen = { path = "../ipc/codegen" }
|
||||
|
||||
[dependencies]
|
||||
log = "0.3"
|
||||
parking_lot = "0.3"
|
||||
hyper = { version = "0.9", default-features = false }
|
||||
url = "1.0"
|
||||
ethcore-devtools = { path = "../devtools" }
|
||||
ethcore-util = { path = "../util" }
|
||||
ethcore-ipc = { path = "../ipc/rpc" }
|
||||
ethcore-ipc-nano = { path = "../ipc/nano" }
|
||||
ethcrypto = { path = "../ethcrypto" }
|
||||
ethkey = { path = "../ethkey" }
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
22
secret_store/build.rs
Normal file
22
secret_store/build.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
extern crate ethcore_ipc_codegen;
|
||||
|
||||
fn main() {
|
||||
ethcore_ipc_codegen::derive_binary("src/types/mod.rs.in").unwrap();
|
||||
ethcore_ipc_codegen::derive_ipc_cond("src/traits.rs", cfg!(feature="ipc")).unwrap();
|
||||
}
|
51
secret_store/src/acl_storage.rs
Normal file
51
secret_store/src/acl_storage.rs
Normal file
@ -0,0 +1,51 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use parking_lot::RwLock;
|
||||
use types::all::{Error, DocumentAddress, Public};
|
||||
|
||||
/// ACL storage of Secret Store
|
||||
pub trait AclStorage: Send + Sync {
|
||||
/// Check if requestor with `public` key can access document with hash `document`
|
||||
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error>;
|
||||
}
|
||||
|
||||
/// Dummy ACL storage implementation
|
||||
#[derive(Default, Debug)]
|
||||
pub struct DummyAclStorage {
|
||||
prohibited: RwLock<HashMap<Public, HashSet<DocumentAddress>>>,
|
||||
}
|
||||
|
||||
impl DummyAclStorage {
|
||||
#[cfg(test)]
|
||||
/// Prohibit given requestor access to given document
|
||||
pub fn prohibit(&self, public: Public, document: DocumentAddress) {
|
||||
self.prohibited.write()
|
||||
.entry(public)
|
||||
.or_insert_with(Default::default)
|
||||
.insert(document);
|
||||
}
|
||||
}
|
||||
|
||||
impl AclStorage for DummyAclStorage {
|
||||
fn check(&self, public: &Public, document: &DocumentAddress) -> Result<bool, Error> {
|
||||
Ok(self.prohibited.read()
|
||||
.get(public)
|
||||
.map(|docs| !docs.contains(document))
|
||||
.unwrap_or(true))
|
||||
}
|
||||
}
|
176
secret_store/src/http_listener.rs
Normal file
176
secret_store/src/http_listener.rs
Normal file
@ -0,0 +1,176 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use hyper::header;
|
||||
use hyper::uri::RequestUri;
|
||||
use hyper::method::Method as HttpMethod;
|
||||
use hyper::status::StatusCode as HttpStatusCode;
|
||||
use hyper::server::{Server as HttpServer, Request as HttpRequest, Response as HttpResponse, Handler as HttpHandler,
|
||||
Listening as HttpListening};
|
||||
use url::percent_encoding::percent_decode;
|
||||
|
||||
use util::ToPretty;
|
||||
use traits::KeyServer;
|
||||
use types::all::{Error, ServiceConfiguration, RequestSignature, DocumentAddress, DocumentEncryptedKey};
|
||||
|
||||
/// Key server http-requests listener
|
||||
pub struct KeyServerHttpListener<T: KeyServer + 'static> {
|
||||
_http_server: HttpListening,
|
||||
handler: Arc<KeyServerSharedHttpHandler<T>>,
|
||||
}
|
||||
|
||||
/// Parsed http request
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
enum Request {
|
||||
/// Invalid request
|
||||
Invalid,
|
||||
/// Request encryption key of given document for given requestor
|
||||
GetDocumentKey(DocumentAddress, RequestSignature),
|
||||
}
|
||||
|
||||
/// Cloneable http handler
|
||||
struct KeyServerHttpHandler<T: KeyServer + 'static> {
|
||||
handler: Arc<KeyServerSharedHttpHandler<T>>,
|
||||
}
|
||||
|
||||
/// Shared http handler
|
||||
struct KeyServerSharedHttpHandler<T: KeyServer + 'static> {
|
||||
key_server: T,
|
||||
}
|
||||
|
||||
impl<T> KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
/// Start KeyServer http listener
|
||||
pub fn start(config: ServiceConfiguration, key_server: T) -> Result<Self, Error> {
|
||||
let shared_handler = Arc::new(KeyServerSharedHttpHandler {
|
||||
key_server: key_server,
|
||||
});
|
||||
let handler = KeyServerHttpHandler {
|
||||
handler: shared_handler.clone(),
|
||||
};
|
||||
|
||||
let listener_addr: &str = &format!("{}:{}", config.listener_addr, config.listener_port);
|
||||
let http_server = HttpServer::http(&listener_addr).unwrap();
|
||||
let http_server = http_server.handle(handler).unwrap();
|
||||
let listener = KeyServerHttpListener {
|
||||
_http_server: http_server,
|
||||
handler: shared_handler,
|
||||
};
|
||||
Ok(listener)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> KeyServer for KeyServerHttpListener<T> where T: KeyServer + 'static {
|
||||
fn document_key(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result<DocumentEncryptedKey, Error> {
|
||||
self.handler.key_server.document_key(signature, document)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> HttpHandler for KeyServerHttpHandler<T> where T: KeyServer + 'static {
|
||||
fn handle(&self, req: HttpRequest, mut res: HttpResponse) {
|
||||
if req.method != HttpMethod::Get {
|
||||
warn!(target: "secretstore", "Ignoring {}-request {}", req.method, req.uri);
|
||||
*res.status_mut() = HttpStatusCode::NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
if req.headers.has::<header::Origin>() {
|
||||
warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method, req.uri);
|
||||
*res.status_mut() = HttpStatusCode::NotFound;
|
||||
return;
|
||||
}
|
||||
|
||||
match req.uri {
|
||||
RequestUri::AbsolutePath(ref path) => match parse_request(&path) {
|
||||
Request::GetDocumentKey(document, signature) => {
|
||||
let document_key = self.handler.key_server.document_key(&signature, &document)
|
||||
.map_err(|err| {
|
||||
warn!(target: "secretstore", "GetDocumentKey request {} has failed with: {}", req.uri, err);
|
||||
err
|
||||
});
|
||||
match document_key {
|
||||
Ok(document_key) => {
|
||||
let document_key = document_key.to_hex().into_bytes();
|
||||
res.headers_mut().set(header::ContentType::plaintext());
|
||||
if let Err(err) = res.send(&document_key) {
|
||||
// nothing to do, but log error
|
||||
warn!(target: "secretstore", "GetDocumentKey request {} response has failed with: {}", req.uri, err);
|
||||
}
|
||||
},
|
||||
Err(Error::BadSignature) => *res.status_mut() = HttpStatusCode::BadRequest,
|
||||
Err(Error::AccessDenied) => *res.status_mut() = HttpStatusCode::Forbidden,
|
||||
Err(Error::DocumentNotFound) => *res.status_mut() = HttpStatusCode::NotFound,
|
||||
Err(Error::Database(_)) => *res.status_mut() = HttpStatusCode::InternalServerError,
|
||||
Err(Error::Internal(_)) => *res.status_mut() = HttpStatusCode::InternalServerError,
|
||||
}
|
||||
},
|
||||
Request::Invalid => {
|
||||
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req.method, req.uri);
|
||||
*res.status_mut() = HttpStatusCode::BadRequest;
|
||||
},
|
||||
},
|
||||
_ => {
|
||||
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req.method, req.uri);
|
||||
*res.status_mut() = HttpStatusCode::NotFound;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_request(uri_path: &str) -> Request {
|
||||
let uri_path = match percent_decode(uri_path.as_bytes()).decode_utf8() {
|
||||
Ok(path) => path,
|
||||
Err(_) => return Request::Invalid,
|
||||
};
|
||||
|
||||
let path: Vec<String> = uri_path.trim_left_matches('/').split('/').map(Into::into).collect();
|
||||
if path.len() != 2 || path[0].is_empty() || path[1].is_empty() {
|
||||
return Request::Invalid;
|
||||
}
|
||||
|
||||
let document = DocumentAddress::from_str(&path[0]);
|
||||
let signature = RequestSignature::from_str(&path[1]);
|
||||
match (document, signature) {
|
||||
(Ok(document), Ok(signature)) => Request::GetDocumentKey(document, signature),
|
||||
_ => Request::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use super::super::RequestSignature;
|
||||
use super::{parse_request, Request};
|
||||
|
||||
#[test]
|
||||
fn parse_request_successful() {
|
||||
assert_eq!(parse_request("/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"),
|
||||
Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
||||
RequestSignature::from_str("a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01").unwrap()));
|
||||
assert_eq!(parse_request("/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"),
|
||||
Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
|
||||
RequestSignature::from_str("a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01").unwrap()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_request_failed() {
|
||||
assert_eq!(parse_request("/0000000000000000000000000000000000000000000000000000000000000001"), Request::Invalid);
|
||||
assert_eq!(parse_request("/0000000000000000000000000000000000000000000000000000000000000001/"), Request::Invalid);
|
||||
assert_eq!(parse_request("/a/b"), Request::Invalid);
|
||||
assert_eq!(parse_request("/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002"), Request::Invalid);
|
||||
}
|
||||
}
|
124
secret_store/src/key_server.rs
Normal file
124
secret_store/src/key_server.rs
Normal file
@ -0,0 +1,124 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethcrypto;
|
||||
use ethkey;
|
||||
use super::acl_storage::AclStorage;
|
||||
use super::key_storage::KeyStorage;
|
||||
use traits::KeyServer;
|
||||
use types::all::{Error, RequestSignature, DocumentAddress, DocumentEncryptedKey};
|
||||
|
||||
/// Secret store key server implementation
|
||||
pub struct KeyServerImpl<T: AclStorage, U: KeyStorage> {
|
||||
acl_storage: T,
|
||||
key_storage: U,
|
||||
}
|
||||
|
||||
impl<T, U> KeyServerImpl<T, U> where T: AclStorage, U: KeyStorage {
|
||||
/// Create new key server instance
|
||||
pub fn new(acl_storage: T, key_storage: U) -> Self {
|
||||
KeyServerImpl {
|
||||
acl_storage: acl_storage,
|
||||
key_storage: key_storage,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> KeyServer for KeyServerImpl<T, U> where T: AclStorage, U: KeyStorage {
|
||||
fn document_key(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result<DocumentEncryptedKey, Error> {
|
||||
// recover requestor' public key from signature
|
||||
let public = ethkey::recover(signature, document)
|
||||
.map_err(|_| Error::BadSignature)?;
|
||||
|
||||
// check that requestor has access to the document
|
||||
if !self.acl_storage.check(&public, document)? {
|
||||
return Err(Error::AccessDenied);
|
||||
}
|
||||
|
||||
// read unencrypted document key
|
||||
let document_key = self.key_storage.get(document)?;
|
||||
// encrypt document key with requestor public key
|
||||
let document_key = ethcrypto::ecies::encrypt_single_message(&public, &document_key)
|
||||
.map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))?;
|
||||
Ok(document_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::str::FromStr;
|
||||
use ethcrypto;
|
||||
use ethkey::{self, Secret};
|
||||
use acl_storage::DummyAclStorage;
|
||||
use key_storage::KeyStorage;
|
||||
use key_storage::tests::DummyKeyStorage;
|
||||
use super::super::{Error, RequestSignature, DocumentAddress};
|
||||
use super::{KeyServer, KeyServerImpl};
|
||||
|
||||
const DOCUMENT1: &'static str = "0000000000000000000000000000000000000000000000000000000000000001";
|
||||
const DOCUMENT2: &'static str = "0000000000000000000000000000000000000000000000000000000000000002";
|
||||
const KEY1: &'static str = "key1";
|
||||
const PRIVATE1: &'static str = "03055e18a8434dcc9061cc1b81c4ef84dc7cf4574d755e52cdcf0c8898b25b11";
|
||||
const PUBLIC2: &'static str = "dfe62f56bb05fbd85b485bac749f3410309e24b352bac082468ce151e9ddb94fa7b5b730027fe1c7c5f3d5927621d269f91aceb5caa3c7fe944677a22f88a318";
|
||||
const PRIVATE2: &'static str = "0eb3816f4f705fa0fd952fb27b71b8c0606f09f4743b5b65cbc375bd569632f2";
|
||||
|
||||
fn create_key_server() -> KeyServerImpl<DummyAclStorage, DummyKeyStorage> {
|
||||
let acl_storage = DummyAclStorage::default();
|
||||
let key_storage = DummyKeyStorage::default();
|
||||
key_storage.insert(DOCUMENT1.into(), KEY1.into()).unwrap();
|
||||
acl_storage.prohibit(PUBLIC2.into(), DOCUMENT1.into());
|
||||
KeyServerImpl::new(acl_storage, key_storage)
|
||||
}
|
||||
|
||||
fn make_signature(secret: &str, document: &'static str) -> RequestSignature {
|
||||
let secret = Secret::from_str(secret).unwrap();
|
||||
let document: DocumentAddress = document.into();
|
||||
ethkey::sign(&secret, &document).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_key_succeeds() {
|
||||
let key_server = create_key_server();
|
||||
let signature = make_signature(PRIVATE1, DOCUMENT1);
|
||||
let document_key = key_server.document_key(&signature, &DOCUMENT1.into()).unwrap();
|
||||
let document_key = ethcrypto::ecies::decrypt_single_message(&Secret::from_str(PRIVATE1).unwrap(), &document_key);
|
||||
assert_eq!(document_key, Ok(KEY1.into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_key_fails_when_bad_signature() {
|
||||
let key_server = create_key_server();
|
||||
let signature = RequestSignature::default();
|
||||
let document_key = key_server.document_key(&signature, &DOCUMENT1.into());
|
||||
assert_eq!(document_key, Err(Error::BadSignature));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_key_fails_when_acl_check_fails() {
|
||||
let key_server = create_key_server();
|
||||
let signature = make_signature(PRIVATE2, DOCUMENT1);
|
||||
let document_key = key_server.document_key(&signature, &DOCUMENT1.into());
|
||||
assert_eq!(document_key, Err(Error::AccessDenied));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_key_fails_when_document_not_found() {
|
||||
let key_server = create_key_server();
|
||||
let signature = make_signature(PRIVATE1, DOCUMENT2);
|
||||
let document_key = key_server.document_key(&signature, &DOCUMENT2.into());
|
||||
assert_eq!(document_key, Err(Error::DocumentNotFound));
|
||||
}
|
||||
}
|
115
secret_store/src/key_storage.rs
Normal file
115
secret_store/src/key_storage.rs
Normal file
@ -0,0 +1,115 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::path::PathBuf;
|
||||
use util::Database;
|
||||
use types::all::{Error, ServiceConfiguration, DocumentAddress, DocumentKey};
|
||||
|
||||
/// Document encryption keys storage
|
||||
pub trait KeyStorage: Send + Sync {
|
||||
/// Insert document encryption key
|
||||
fn insert(&self, document: DocumentAddress, key: DocumentKey) -> Result<(), Error>;
|
||||
/// Get document encryption key
|
||||
fn get(&self, document: &DocumentAddress) -> Result<DocumentKey, Error>;
|
||||
}
|
||||
|
||||
/// Persistent document encryption keys storage
|
||||
pub struct PersistentKeyStorage {
|
||||
db: Database,
|
||||
}
|
||||
|
||||
impl PersistentKeyStorage {
|
||||
/// Create new persistent document encryption keys storage
|
||||
pub fn new(config: &ServiceConfiguration) -> Result<Self, Error> {
|
||||
let mut db_path = PathBuf::from(&config.data_path);
|
||||
db_path.push("db");
|
||||
let db_path = db_path.to_str().ok_or(Error::Database("Invalid secretstore path".to_owned()))?;
|
||||
|
||||
Ok(PersistentKeyStorage {
|
||||
db: Database::open_default(&db_path).map_err(Error::Database)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl KeyStorage for PersistentKeyStorage {
|
||||
fn insert(&self, document: DocumentAddress, key: DocumentKey) -> Result<(), Error> {
|
||||
let mut batch = self.db.transaction();
|
||||
batch.put(None, &document, &key);
|
||||
self.db.write(batch).map_err(Error::Database)
|
||||
}
|
||||
|
||||
fn get(&self, document: &DocumentAddress) -> Result<DocumentKey, Error> {
|
||||
self.db.get(None, document)
|
||||
.map_err(Error::Database)?
|
||||
.ok_or(Error::DocumentNotFound)
|
||||
.map(|key| key.to_vec())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use std::collections::HashMap;
|
||||
use parking_lot::RwLock;
|
||||
use devtools::RandomTempPath;
|
||||
use super::super::types::all::{Error, ServiceConfiguration, DocumentAddress, DocumentKey};
|
||||
use super::{KeyStorage, PersistentKeyStorage};
|
||||
|
||||
#[derive(Default)]
|
||||
/// In-memory document encryption keys storage
|
||||
pub struct DummyKeyStorage {
|
||||
keys: RwLock<HashMap<DocumentAddress, DocumentKey>>,
|
||||
}
|
||||
|
||||
impl KeyStorage for DummyKeyStorage {
|
||||
fn insert(&self, document: DocumentAddress, key: DocumentKey) -> Result<(), Error> {
|
||||
self.keys.write().insert(document, key);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get(&self, document: &DocumentAddress) -> Result<DocumentKey, Error> {
|
||||
self.keys.read().get(document).cloned().ok_or(Error::DocumentNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn persistent_key_storage() {
|
||||
let path = RandomTempPath::create_dir();
|
||||
let config = ServiceConfiguration {
|
||||
listener_addr: "0.0.0.0".to_owned(),
|
||||
listener_port: 8082,
|
||||
data_path: path.as_str().to_owned(),
|
||||
};
|
||||
|
||||
let key1 = DocumentAddress::from(1);
|
||||
let value1: DocumentKey = vec![0x77, 0x88];
|
||||
let key2 = DocumentAddress::from(2);
|
||||
let value2: DocumentKey = vec![0x11, 0x22];
|
||||
let key3 = DocumentAddress::from(3);
|
||||
|
||||
let key_storage = PersistentKeyStorage::new(&config).unwrap();
|
||||
key_storage.insert(key1.clone(), value1.clone()).unwrap();
|
||||
key_storage.insert(key2.clone(), value2.clone()).unwrap();
|
||||
assert_eq!(key_storage.get(&key1), Ok(value1.clone()));
|
||||
assert_eq!(key_storage.get(&key2), Ok(value2.clone()));
|
||||
assert_eq!(key_storage.get(&key3), Err(Error::DocumentNotFound));
|
||||
drop(key_storage);
|
||||
|
||||
let key_storage = PersistentKeyStorage::new(&config).unwrap();
|
||||
assert_eq!(key_storage.get(&key1), Ok(value1));
|
||||
assert_eq!(key_storage.get(&key2), Ok(value2));
|
||||
assert_eq!(key_storage.get(&key3), Err(Error::DocumentNotFound));
|
||||
}
|
||||
}
|
52
secret_store/src/lib.rs
Normal file
52
secret_store/src/lib.rs
Normal file
@ -0,0 +1,52 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate hyper;
|
||||
extern crate parking_lot;
|
||||
extern crate url;
|
||||
|
||||
extern crate ethcore_devtools as devtools;
|
||||
extern crate ethcore_util as util;
|
||||
extern crate ethcore_ipc as ipc;
|
||||
extern crate ethcrypto;
|
||||
extern crate ethkey;
|
||||
|
||||
mod types;
|
||||
|
||||
mod traits {
|
||||
#![allow(dead_code, unused_assignments, unused_variables, missing_docs)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/traits.rs"));
|
||||
}
|
||||
|
||||
mod acl_storage;
|
||||
mod http_listener;
|
||||
mod key_server;
|
||||
mod key_storage;
|
||||
|
||||
pub use types::all::{DocumentAddress, DocumentKey, DocumentEncryptedKey, RequestSignature, Public,
|
||||
Error, ServiceConfiguration};
|
||||
pub use traits::{KeyServer};
|
||||
|
||||
/// Start new key server instance
|
||||
pub fn start(config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> {
|
||||
let acl_storage = acl_storage::DummyAclStorage::default();
|
||||
let key_storage = key_storage::PersistentKeyStorage::new(&config)?;
|
||||
let key_server = key_server::KeyServerImpl::new(acl_storage, key_storage);
|
||||
let listener = http_listener::KeyServerHttpListener::start(config, key_server)?;
|
||||
Ok(Box::new(listener))
|
||||
}
|
24
secret_store/src/traits.rs
Normal file
24
secret_store/src/traits.rs
Normal file
@ -0,0 +1,24 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use types::all::{Error, RequestSignature, DocumentAddress, DocumentEncryptedKey};
|
||||
|
||||
#[ipc(client_ident="RemoteKeyServer")]
|
||||
/// Secret store key server
|
||||
pub trait KeyServer: Send + Sync {
|
||||
/// Request encryption key of given document for given requestor
|
||||
fn document_key(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result<DocumentEncryptedKey, Error>;
|
||||
}
|
77
secret_store/src/types/all.rs
Normal file
77
secret_store/src/types/all.rs
Normal file
@ -0,0 +1,77 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use ethkey;
|
||||
use util;
|
||||
|
||||
/// Document address type.
|
||||
pub type DocumentAddress = util::H256;
|
||||
/// Document key type.
|
||||
pub type DocumentKey = util::Bytes;
|
||||
/// Encrypted key type.
|
||||
pub type DocumentEncryptedKey = util::Bytes;
|
||||
/// Request signature type.
|
||||
pub type RequestSignature = ethkey::Signature;
|
||||
/// Public key type.
|
||||
pub use ethkey::Public;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
#[binary]
|
||||
/// Secret store error
|
||||
pub enum Error {
|
||||
/// Bad signature is passed
|
||||
BadSignature,
|
||||
/// Access to resource is denied
|
||||
AccessDenied,
|
||||
/// Requested document not found
|
||||
DocumentNotFound,
|
||||
/// Database-related error
|
||||
Database(String),
|
||||
/// Internal error
|
||||
Internal(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[binary]
|
||||
/// Secret store configuration
|
||||
pub struct ServiceConfiguration {
|
||||
/// Interface to listen to
|
||||
pub listener_addr: String,
|
||||
/// Port to listen to
|
||||
pub listener_port: u16,
|
||||
/// Data directory path for secret store
|
||||
pub data_path: String,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
Error::BadSignature => write!(f, "Bad signature"),
|
||||
Error::AccessDenied => write!(f, "Access dened"),
|
||||
Error::DocumentNotFound => write!(f, "Document not found"),
|
||||
Error::Database(ref msg) => write!(f, "Database error: {}", msg),
|
||||
Error::Internal(ref msg) => write!(f, "Internal error: {}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<String> for Error {
|
||||
fn into(self) -> String {
|
||||
format!("{}", self)
|
||||
}
|
||||
}
|
20
secret_store/src/types/mod.rs
Normal file
20
secret_store/src/types/mod.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Types used in the public api
|
||||
|
||||
#![allow(dead_code, unused_assignments, unused_variables)] // codegen issues
|
||||
include!(concat!(env!("OUT_DIR"), "/mod.rs.in"));
|
17
secret_store/src/types/mod.rs.in
Normal file
17
secret_store/src/types/mod.rs.in
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod all;
|
Loading…
Reference in New Issue
Block a user