Merge pull request #7336 from paritytech/secretstore_rpc_sign_raw_hash

SecretStore: secretstore_signRawHash method
This commit is contained in:
Marek Kotewicz 2017-12-29 11:10:29 +01:00 committed by GitHub
commit 483480d7a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 57 additions and 28 deletions

View File

@ -454,7 +454,7 @@ usage! {
"--jsonrpc-interface=[IP]", "--jsonrpc-interface=[IP]",
"Specify the hostname portion of the JSONRPC API server, IP should be an interface's IP address, or all (all interfaces) or local.", "Specify the hostname portion of the JSONRPC API server, IP should be an interface's IP address, or all (all interfaces) or local.",
ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")), ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| otry!(c.rpc).apis.as_ref().map(|vec| vec.join(",")),
"--jsonrpc-apis=[APIS]", "--jsonrpc-apis=[APIS]",
"Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are all, safe, web3, eth, net, personal, parity, parity_set, traces, rpc, parity_accounts. You can also disable a specific API by putting '-' in the front: all,-personal.", "Specify the APIs available through the JSONRPC interface. APIS is a comma-delimited list of API name. Possible name are all, safe, web3, eth, net, personal, parity, parity_set, traces, rpc, parity_accounts. You can also disable a specific API by putting '-' in the front: all,-personal.",
@ -487,7 +487,7 @@ usage! {
"--ws-interface=[IP]", "--ws-interface=[IP]",
"Specify the hostname portion of the WebSockets server, IP should be an interface's IP address, or all (all interfaces) or local.", "Specify the hostname portion of the WebSockets server, IP should be an interface's IP address, or all (all interfaces) or local.",
ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")), ARG arg_ws_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| otry!(c.websockets).apis.as_ref().map(|vec| vec.join(",")),
"--ws-apis=[APIS]", "--ws-apis=[APIS]",
"Specify the APIs available through the WebSockets interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, pubsub, net, personal, parity, parity_set, traces, rpc, parity_accounts..", "Specify the APIs available through the WebSockets interface. APIS is a comma-delimited list of API name. Possible name are web3, eth, pubsub, net, personal, parity, parity_set, traces, rpc, parity_accounts..",
@ -508,7 +508,7 @@ usage! {
"--ipc-path=[PATH]", "--ipc-path=[PATH]",
"Specify custom path for JSON-RPC over IPC service.", "Specify custom path for JSON-RPC over IPC service.",
ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,secretstore,shh,shh_pubsub", or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")), ARG arg_ipc_apis: (String) = "web3,eth,pubsub,net,parity,parity_pubsub,parity_accounts,traces,rpc,shh,shh_pubsub", or |c: &Config| otry!(c.ipc).apis.as_ref().map(|vec| vec.join(",")),
"--ipc-apis=[APIS]", "--ipc-apis=[APIS]",
"Specify custom API set available via JSON-RPC over IPC.", "Specify custom API set available via JSON-RPC over IPC.",

View File

@ -66,7 +66,7 @@ pub enum Api {
Traces, Traces,
/// Rpc (Safe) /// Rpc (Safe)
Rpc, Rpc,
/// SecretStore (Safe) /// SecretStore (UNSAFE: arbitrary hash signing)
SecretStore, SecretStore,
/// Whisper (Safe) /// Whisper (Safe)
// TODO: _if_ someone guesses someone else's key or filter IDs they can remove // TODO: _if_ someone guesses someone else's key or filter IDs they can remove
@ -602,7 +602,6 @@ impl ApiSet {
Api::EthPubSub, Api::EthPubSub,
Api::Parity, Api::Parity,
Api::Rpc, Api::Rpc,
Api::SecretStore,
Api::Whisper, Api::Whisper,
Api::WhisperPubSub, Api::WhisperPubSub,
].into_iter().cloned().collect(); ].into_iter().cloned().collect();
@ -627,6 +626,7 @@ impl ApiSet {
public_list.insert(Api::ParityAccounts); public_list.insert(Api::ParityAccounts);
public_list.insert(Api::ParitySet); public_list.insert(Api::ParitySet);
public_list.insert(Api::Signer); public_list.insert(Api::Signer);
public_list.insert(Api::SecretStore);
public_list public_list
}, },
ApiSet::All => { ApiSet::All => {
@ -636,6 +636,7 @@ impl ApiSet {
public_list.insert(Api::ParitySet); public_list.insert(Api::ParitySet);
public_list.insert(Api::Signer); public_list.insert(Api::Signer);
public_list.insert(Api::Personal); public_list.insert(Api::Personal);
public_list.insert(Api::SecretStore);
public_list public_list
}, },
ApiSet::PubSub => [ ApiSet::PubSub => [
@ -686,7 +687,7 @@ mod test {
fn test_api_set_unsafe_context() { fn test_api_set_unsafe_context() {
let expected = vec![ let expected = vec![
// make sure this list contains only SAFE methods // make sure this list contains only SAFE methods
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub,
].into_iter().collect(); ].into_iter().collect();
assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected);
} }
@ -695,7 +696,7 @@ mod test {
fn test_api_set_ipc_context() { fn test_api_set_ipc_context() {
let expected = vec![ let expected = vec![
// safe // safe
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub,
// semi-safe // semi-safe
Api::ParityAccounts Api::ParityAccounts
].into_iter().collect(); ].into_iter().collect();
@ -737,7 +738,7 @@ mod test {
#[test] #[test]
fn test_safe_parsing() { fn test_safe_parsing() {
assert_eq!("safe".parse::<ApiSet>().unwrap(), ApiSet::List(vec![ assert_eq!("safe".parse::<ApiSet>().unwrap(), ApiSet::List(vec![
Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub,
].into_iter().collect())); ].into_iter().collect()));
} }
} }

View File

@ -28,7 +28,7 @@ use v1::helpers::errors;
use v1::helpers::accounts::unwrap_provider; use v1::helpers::accounts::unwrap_provider;
use v1::helpers::secretstore::{encrypt_document, decrypt_document, decrypt_document_with_shadow, ordered_servers_keccak}; use v1::helpers::secretstore::{encrypt_document, decrypt_document, decrypt_document_with_shadow, ordered_servers_keccak};
use v1::traits::SecretStore; use v1::traits::SecretStore;
use v1::types::{H160, H512, Bytes}; use v1::types::{H160, H256, H512, Bytes};
/// Parity implementation. /// Parity implementation.
pub struct SecretStoreClient { pub struct SecretStoreClient {
@ -84,12 +84,15 @@ impl SecretStore for SecretStoreClient {
.map(Into::into) .map(Into::into)
} }
fn sign_servers_set(&self, address: H160, password: String, servers_set: BTreeSet<H512>) -> Result<Bytes> { fn servers_set_hash(&self, servers_set: BTreeSet<H512>) -> Result<H256> {
let servers_set_keccak_value = ordered_servers_keccak(servers_set); Ok(ordered_servers_keccak(servers_set))
}
fn sign_raw_hash(&self, address: H160, password: String, raw_hash: H256) -> Result<Bytes> {
let store = self.account_provider()?; let store = self.account_provider()?;
store store
.sign(address.into(), Some(password), servers_set_keccak_value.into()) .sign(address.into(), Some(password), raw_hash.into())
.map(|s| Bytes::new((*s).to_vec())) .map(|s| Bytes::new((*s).to_vec()))
.map_err(|e| errors::account("Could not sign servers set.", e)) .map_err(|e| errors::account("Could not sign raw hash.", e))
} }
} }

View File

@ -25,6 +25,7 @@ use v1::metadata::Metadata;
use v1::SecretStoreClient; use v1::SecretStoreClient;
use v1::traits::secretstore::SecretStore; use v1::traits::secretstore::SecretStore;
use v1::helpers::secretstore::ordered_servers_keccak; use v1::helpers::secretstore::ordered_servers_keccak;
use v1::types::H256;
struct Dependencies { struct Dependencies {
pub accounts: Arc<AccountProvider>, pub accounts: Arc<AccountProvider>,
@ -100,7 +101,29 @@ fn rpc_secretstore_shadow_decrypt() {
} }
#[test] #[test]
fn rpc_secretstore_sign_servers_set() { fn rpc_secretstore_servers_set_hash() {
let deps = Dependencies::new();
let io = deps.default_client();
// execute hashing request
let hashing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_serversSetHash", "params":[
["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]
], "id": 1}"#;
let hashing_response = io.handle_request_sync(&hashing_request).unwrap();
let hashing_response = hashing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, "");
let hashing_response = hashing_response.replace(r#"","id":1}"#, "");
let hash: H256 = hashing_response.parse().unwrap();
let servers_set_keccak = ordered_servers_keccak(vec![
"843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(),
"07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap()
].into_iter().collect());
assert_eq!(hash, servers_set_keccak);
}
#[test]
fn rpc_secretstore_sign_raw_hash() {
let deps = Dependencies::new(); let deps = Dependencies::new();
let io = deps.default_client(); let io = deps.default_client();
@ -110,19 +133,14 @@ fn rpc_secretstore_sign_servers_set() {
deps.accounts.insert_account(key_pair.secret().clone(), "password").unwrap(); deps.accounts.insert_account(key_pair.secret().clone(), "password").unwrap();
// execute signing request // execute signing request
let signing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_signServersSet", "params":[ let signing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_signRawHash", "params":[
"0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password", "0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password", "0x0000000000000000000000000000000000000000000000000000000000000001"
["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]
], "id": 1}"#; ], "id": 1}"#;
let signing_response = io.handle_request_sync(&signing_request).unwrap(); let signing_response = io.handle_request_sync(&signing_request).unwrap();
let signing_response = signing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, ""); let signing_response = signing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, "");
let signing_response = signing_response.replace(r#"","id":1}"#, ""); let signing_response = signing_response.replace(r#"","id":1}"#, "");
let signature: Signature = signing_response.parse().unwrap(); let signature: Signature = signing_response.parse().unwrap();
let servers_set_keccak = ordered_servers_keccak(vec![ let hash = "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap();
"843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(), assert!(verify_public(key_pair.public(), &signature, &hash).unwrap());
"07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap()
].into_iter().collect());
assert!(verify_public(key_pair.public(), &signature, &servers_set_keccak.into()).unwrap());
} }

View File

@ -19,7 +19,7 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use jsonrpc_core::Result; use jsonrpc_core::Result;
use v1::types::{H160, H512, Bytes}; use v1::types::{H160, H256, H512, Bytes};
build_rpc_trait! { build_rpc_trait! {
/// Parity-specific rpc interface. /// Parity-specific rpc interface.
@ -39,9 +39,16 @@ build_rpc_trait! {
#[rpc(name = "secretstore_shadowDecrypt")] #[rpc(name = "secretstore_shadowDecrypt")]
fn shadow_decrypt(&self, H160, String, H512, H512, Vec<Bytes>, Bytes) -> Result<Bytes>; fn shadow_decrypt(&self, H160, String, H512, H512, Vec<Bytes>, Bytes) -> Result<Bytes>;
/// Sign servers set for use in ServersSetChange session. /// Calculates the hash (keccak256) of servers set for using in ServersSetChange session.
/// Arguments: `account`, `password`, `servers_set`. /// Returned hash must be signed later by using `secretstore_signRawHash` method.
#[rpc(name = "secretstore_signServersSet")] /// Arguments: `servers_set`.
fn sign_servers_set(&self, H160, String, BTreeSet<H512>) -> Result<Bytes>; #[rpc(name = "secretstore_serversSetHash")]
fn servers_set_hash(&self, BTreeSet<H512>) -> Result<H256>;
/// Generate recoverable ECDSA signature of raw hash.
/// Passed hash is treated as an input to the `sign` function (no prefixes added, no hash function is applied).
/// Arguments: `account`, `password`, `raw_hash`.
#[rpc(name = "secretstore_signRawHash")]
fn sign_raw_hash(&self, H160, String, H256) -> Result<Bytes>;
} }
} }