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

Commit

Permalink
Secretstore RPCs + integration (#5439)
Browse files Browse the repository at this point in the history
* ECDKG protocol prototype

* added test for enc/dec math

* get rid of decryption_session

* added licenses

* fix after merge

* get rid of unused serde dependency

* doc

* decryption session [without commutative enc]

* failed_dec_session

* fixed tests

* added commen

* added more decryption session tests

* helper to localize an issue

* more computations to localize error

* decryption_session::SessionParams

* added tests for EC math to localize problem

* secretstore network transport

* encryption_session_works_over_network

* network errors processing

* connecting to KeyServer

* licenses

* get rid of debug println-s

* fixed secretstore args

* encryption results are stored in KS database

* decryption protocol works over network

* enc/dec Session traits

* fixing warnings

* fix after merge

* on-chain ACL checker proto

* fixed compilation

* fixed compilation

* finally fixed <odd>-of-N-scheme

* temporary commented test

* 1-of-N works in math

* scheme 1-of-N works

* updated AclStorage with real contract ABI

* remove unnecessary unsafety

* fixed grumbles

* wakeup on access denied

* encrypt secretstore messages

* 'shadow' decryption

* fix grumbles

* lost files

* secretstore cli-options

* decryption seccion when ACL check failed on master

* disallow regenerating key for existing document

* removed obsolete TODO

* fix after merge

* switched to tokio_io

* fix after merge

* fix after merge

* fix after merge

* fix after merge

* fix after merge

* fixed test

* fix after merge

* encryption session errors are now fatal

* session timeouts

* autorestart decryption session

* remove sessions on completion

* exclude disconnected nodes from decryption session

* test for enc/dec session over network with 1 node

* remove debug printlns

* fixed 1-of-1 scheme

* drop for KeyServerHttpListener

* Use standard encryption and decryption (as in RPC)

* added some tests

* moved DEFAULT_MAC to ethcrypto

* rpc_secretstore_encrypt_and_decrypt

* serialization with "0x" prefix (RPC compatibility)

* secretstore RPC API

* fix after merge

* fixed typo

* secretstore_shadowDecrypt RPC

* enable secretstore RPCs by default

* fixed test

* SecStore RPCs available without SecStore feature

* fixed grumbles

* lost files

* added password argument to Parity RPCs

* update docs

* lost file
  • Loading branch information
svyatonik authored and gavofyork committed May 5, 2017
1 parent 0d89203 commit 8b9adb4
Show file tree
Hide file tree
Showing 24 changed files with 497 additions and 57 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions ethcrypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ pub const KEY_LENGTH: usize = 32;
pub const KEY_ITERATIONS: usize = 10240;
pub const KEY_LENGTH_AES: usize = KEY_LENGTH / 2;

/// Default MAC to use (in RPC).
pub const DEFAULT_MAC: [u8; 2] = [0, 0];

#[derive(PartialEq, Debug)]
pub enum ScryptError {
// log(N) < r / 16
Expand Down
6 changes: 3 additions & 3 deletions parity/cli/config.full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,21 +50,21 @@ disable = false
port = 8545
interface = "local"
cors = "null"
apis = ["web3", "eth", "net", "parity", "traces", "rpc"]
apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]
hosts = ["none"]

[websockets]
disable = false
port = 8546
interface = "local"
origins = ["none"]
apis = ["web3", "eth", "net", "parity", "traces", "rpc"]
apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"]
hosts = ["none"]

[ipc]
disable = false
path = "$HOME/.parity/jsonrpc.ipc"
apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc"]
apis = ["web3", "eth", "net", "parity", "parity_accounts", "personal", "traces", "rpc", "secretstore"]

[dapps]
disable = false
Expand Down
12 changes: 6 additions & 6 deletions parity/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ usage! {
or |c: &Config| otry!(c.rpc).interface.clone(),
flag_jsonrpc_cors: Option<String> = None,
or |c: &Config| otry!(c.rpc).cors.clone().map(Some),
flag_jsonrpc_apis: String = "web3,eth,net,parity,traces,rpc",
flag_jsonrpc_apis: String = "web3,eth,net,parity,traces,rpc,secretstore",
or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
flag_jsonrpc_hosts: String = "none",
or |c: &Config| otry!(c.rpc).hosts.as_ref().map(|vec| vec.join(",")),
Expand All @@ -179,7 +179,7 @@ usage! {
or |c: &Config| otry!(c.websockets).port.clone(),
flag_ws_interface: String = "local",
or |c: &Config| otry!(c.websockets).interface.clone(),
flag_ws_apis: String = "web3,eth,net,parity,traces,rpc",
flag_ws_apis: String = "web3,eth,net,parity,traces,rpc,secretstore",
or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")),
flag_ws_origins: String = "none",
or |c: &Config| otry!(c.websockets).origins.as_ref().map(|vec| vec.join(",")),
Expand All @@ -191,7 +191,7 @@ usage! {
or |c: &Config| otry!(c.ipc).disable.clone(),
flag_ipc_path: String = "$BASE/jsonrpc.ipc",
or |c: &Config| otry!(c.ipc).path.clone(),
flag_ipc_apis: String = "web3,eth,net,parity,parity_accounts,traces,rpc",
flag_ipc_apis: String = "web3,eth,net,parity,parity_accounts,traces,rpc,secretstore",
or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")),

// DAPPS
Expand Down Expand Up @@ -723,22 +723,22 @@ mod tests {
flag_jsonrpc_port: 8545u16,
flag_jsonrpc_interface: "local".into(),
flag_jsonrpc_cors: Some("null".into()),
flag_jsonrpc_apis: "web3,eth,net,parity,traces,rpc".into(),
flag_jsonrpc_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(),
flag_jsonrpc_hosts: "none".into(),
flag_jsonrpc_threads: None,

// WS
flag_no_ws: false,
flag_ws_port: 8546u16,
flag_ws_interface: "local".into(),
flag_ws_apis: "web3,eth,net,parity,traces,rpc".into(),
flag_ws_apis: "web3,eth,net,parity,traces,rpc,secretstore".into(),
flag_ws_origins: "none".into(),
flag_ws_hosts: "none".into(),

// IPC
flag_no_ipc: false,
flag_ipc_path: "$HOME/.parity/jsonrpc.ipc".into(),
flag_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc".into(),
flag_ipc_apis: "web3,eth,net,parity,parity_accounts,personal,traces,rpc,secretstore".into(),

// DAPPS
flag_dapps_path: "$HOME/.parity/dapps".into(),
Expand Down
30 changes: 21 additions & 9 deletions parity/rpc_apis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ pub enum Api {
Traces,
/// Rpc (Safe)
Rpc,
/// SecretStore (Safe)
SecretStore,
}

impl FromStr for Api {
Expand All @@ -78,6 +80,7 @@ impl FromStr for Api {
"parity_set" => Ok(ParitySet),
"traces" => Ok(Traces),
"rpc" => Ok(Rpc),
"secretstore" => Ok(SecretStore),
api => Err(format!("Unknown api: {}", api))
}
}
Expand Down Expand Up @@ -156,6 +159,7 @@ fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
Api::ParitySet => ("parity_set", "1.0"),
Api::Traces => ("traces", "1.0"),
Api::Rpc => ("rpc", "1.0"),
Api::SecretStore => ("secretstore", "1.0"),
};
modules.insert(name.into(), version.into());
}
Expand Down Expand Up @@ -295,7 +299,10 @@ impl Dependencies for FullDependencies {
Api::Rpc => {
let modules = to_modules(&apis);
handler.extend_with(RpcClient::new(modules).to_delegate());
}
},
Api::SecretStore => {
handler.extend_with(SecretStoreClient::new(&self.secret_store).to_delegate());
},
}
}
}
Expand Down Expand Up @@ -424,7 +431,11 @@ impl Dependencies for LightDependencies {
Api::Rpc => {
let modules = to_modules(&apis);
handler.extend_with(RpcClient::new(modules).to_delegate());
}
},
Api::SecretStore => {
let secret_store = Some(self.secret_store.clone());
handler.extend_with(SecretStoreClient::new(&secret_store).to_delegate());
},
}
}
}
Expand All @@ -438,7 +449,7 @@ impl ApiSet {

pub fn list_apis(&self) -> HashSet<Api> {
let mut public_list = vec![
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Rpc,
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Rpc, Api::SecretStore,
].into_iter().collect();
match *self {
ApiSet::List(ref apis) => apis.clone(),
Expand Down Expand Up @@ -496,6 +507,7 @@ mod test {
assert_eq!(Api::ParitySet, "parity_set".parse().unwrap());
assert_eq!(Api::Traces, "traces".parse().unwrap());
assert_eq!(Api::Rpc, "rpc".parse().unwrap());
assert_eq!(Api::SecretStore, "secretstore".parse().unwrap());
assert!("rp".parse::<Api>().is_err());
}

Expand All @@ -513,7 +525,7 @@ mod test {
fn test_api_set_unsafe_context() {
let expected = vec![
// make sure this list contains only SAFE methods
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc, Api::SecretStore
].into_iter().collect();
assert_eq!(ApiSet::UnsafeContext.list_apis(), expected);
}
Expand All @@ -522,7 +534,7 @@ mod test {
fn test_api_set_ipc_context() {
let expected = vec![
// safe
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc,
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc, Api::SecretStore,
// semi-safe
Api::ParityAccounts
].into_iter().collect();
Expand All @@ -533,7 +545,7 @@ mod test {
fn test_api_set_safe_context() {
let expected = vec![
// safe
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc,
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc, Api::SecretStore,
// semi-safe
Api::ParityAccounts,
// Unsafe
Expand All @@ -545,7 +557,7 @@ mod test {
#[test]
fn test_all_apis() {
assert_eq!("all".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc,
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc, Api::SecretStore,
Api::ParityAccounts,
Api::ParitySet, Api::Signer,
Api::Personal
Expand All @@ -555,7 +567,7 @@ mod test {
#[test]
fn test_all_without_personal_apis() {
assert_eq!("personal,all,-personal".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc,
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc, Api::SecretStore,
Api::ParityAccounts,
Api::ParitySet, Api::Signer,
].into_iter().collect()));
Expand All @@ -564,7 +576,7 @@ mod test {
#[test]
fn test_safe_parsing() {
assert_eq!("safe".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc,
Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc, Api::SecretStore,
].into_iter().collect()));
}
}
1 change: 1 addition & 0 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ transient-hashmap = "0.4"
cid = "0.2.1"
multihash = "0.5"
rust-crypto = "0.2.36"
rand = "0.3"

jsonrpc-core = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
jsonrpc-http-server = { git = "https://github.com/paritytech/jsonrpc.git", branch = "parity-1.7" }
Expand Down
1 change: 1 addition & 0 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ extern crate transient_hashmap;
extern crate cid;
extern crate multihash;
extern crate crypto as rust_crypto;
extern crate rand;

extern crate jsonrpc_core;
extern crate jsonrpc_http_server as http;
Expand Down
4 changes: 1 addition & 3 deletions rpc/src/v1/helpers/dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ use ethcore::miner::MinerService;
use ethcore::client::MiningBlockChainClient;
use ethcore::transaction::{Action, SignedTransaction, PendingTransaction, Transaction};
use ethcore::account_provider::AccountProvider;
use crypto::DEFAULT_MAC;

use jsonrpc_core::Error;
use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
Expand Down Expand Up @@ -400,9 +401,6 @@ impl Dispatcher for LightDispatcher {
}
}

/// default MAC to use.
pub const DEFAULT_MAC: [u8; 2] = [0, 0];

/// Single-use account token.
pub type AccountToken = String;

Expand Down
1 change: 1 addition & 0 deletions rpc/src/v1/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub mod light_fetch;
pub mod informant;
pub mod oneshot;
pub mod ipfs;
pub mod secretstore;

mod network_settings;
mod poll_manager;
Expand Down
127 changes: 127 additions & 0 deletions rpc/src/v1/helpers/secretstore.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// 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::iter::repeat;
use rand::{Rng, OsRng};
use ethkey::{Public, Secret, math};
use crypto;
use util::Bytes;
use jsonrpc_core::Error;
use v1::helpers::errors;

/// Initialization vector length.
const INIT_VEC_LEN: usize = 16;

/// Encrypt document with distributely generated key.
pub fn encrypt_document(key: Bytes, document: Bytes) -> Result<Bytes, Error> {
// make document key
let key = into_document_key(key)?;

// use symmetric encryption to encrypt document
let iv = initialization_vector();
let mut encrypted_document = Vec::with_capacity(document.len() + iv.len());
encrypted_document.extend(repeat(0).take(document.len()));
crypto::aes::encrypt(&key, &iv, &document, &mut encrypted_document);
encrypted_document.extend_from_slice(&iv);

Ok(encrypted_document)
}

/// Decrypt document with distributely generated key.
pub fn decrypt_document(key: Bytes, mut encrypted_document: Bytes) -> Result<Bytes, Error> {
// initialization vector takes INIT_VEC_LEN bytes
let encrypted_document_len = encrypted_document.len();
if encrypted_document_len < INIT_VEC_LEN {
return Err(errors::invalid_params("encrypted_document", "invalid encrypted data"));
}

// make document key
let key = into_document_key(key)?;

// use symmetric decryption to decrypt document
let iv = encrypted_document.split_off(encrypted_document_len - INIT_VEC_LEN);
let mut document = Vec::with_capacity(encrypted_document_len - INIT_VEC_LEN);
document.extend(repeat(0).take(encrypted_document_len - INIT_VEC_LEN));
crypto::aes::decrypt(&key, &iv, &encrypted_document, &mut document);

Ok(document)
}

pub fn decrypt_document_with_shadow(decrypted_secret: Public, common_point: Public, shadows: Vec<Secret>, encrypted_document: Bytes) -> Result<Bytes, Error> {
let key = decrypt_with_shadow_coefficients(decrypted_secret, common_point, shadows)?;
decrypt_document(key.to_vec(), encrypted_document)
}

fn into_document_key(key: Bytes) -> Result<Bytes, Error> {
// key is a previously distributely generated Public
if key.len() != 64 {
return Err(errors::invalid_params("key", "invalid public key length"));
}

// use x coordinate of distributely generated point as encryption key
Ok(key[..INIT_VEC_LEN].into())
}

fn initialization_vector() -> [u8; INIT_VEC_LEN] {
let mut result = [0u8; INIT_VEC_LEN];
let mut rng = OsRng::new().unwrap();
rng.fill_bytes(&mut result);
result
}

fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_shadow_point: Public, shadow_coefficients: Vec<Secret>) -> Result<Public, Error> {
let mut shadow_coefficients_sum = shadow_coefficients[0].clone();
for shadow_coefficient in shadow_coefficients.iter().skip(1) {
shadow_coefficients_sum.add(shadow_coefficient)
.map_err(errors::encryption_error)?;
}

math::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum)
.map_err(errors::encryption_error)?;
math::public_add(&mut decrypted_shadow, &common_shadow_point)
.map_err(errors::encryption_error)?;
Ok(decrypted_shadow)
}

#[cfg(test)]
mod tests {
use util::Bytes;
use rustc_serialize::hex::FromHex;
use super::{encrypt_document, decrypt_document, decrypt_document_with_shadow};

#[test]
fn encrypt_and_decrypt_document() {
let document_key: Bytes = "cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325".from_hex().unwrap();
let document: Bytes = b"Hello, world!!!"[..].into();

let encrypted_document = encrypt_document(document_key.clone(), document.clone()).unwrap();
assert!(document != encrypted_document);

let decrypted_document = decrypt_document(document_key.clone(), encrypted_document).unwrap();
assert_eq!(decrypted_document, document);
}

#[test]
fn encrypt_and_shadow_decrypt_document() {
let document: Bytes = "deadbeef".from_hex().unwrap();
let encrypted_document = "2ddec1f96229efa2916988d8b2a82a47ef36f71c".from_hex().unwrap();
let decrypted_secret = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap();
let common_point = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap();
let shadows = vec!["46f542416216f66a7d7881f5a283d2a1ab7a87b381cbc5f29d0b093c7c89ee31".parse().unwrap()];
let decrypted_document = decrypt_document_with_shadow(decrypted_secret, common_point, shadows, encrypted_document).unwrap();
assert_eq!(decrypted_document, document);
}
}
Loading

0 comments on commit 8b9adb4

Please sign in to comment.