SecretStore: secretstore_generateDocumentKey RPC (#7864)

* SecretStore: secretstore_generateDocumentKey RPC

* SecretStore: return encrypted_key from secretstore_generateDocumentKey
This commit is contained in:
Svyatoslav Nikolsky 2018-03-01 12:00:44 +03:00 committed by Marek Kotewicz
parent 0da6c7eb45
commit 6d5b13c80c
6 changed files with 143 additions and 6 deletions

View File

@ -16,17 +16,36 @@
use std::collections::BTreeSet;
use rand::{Rng, OsRng};
use ethkey::{Public, Secret, math};
use ethkey::{Public, Secret, Random, Generator, math};
use crypto;
use bytes::Bytes;
use jsonrpc_core::Error;
use v1::helpers::errors;
use v1::types::{H256, H512};
use v1::types::{H256, H512, EncryptedDocumentKey};
use tiny_keccak::Keccak;
/// Initialization vector length.
const INIT_VEC_LEN: usize = 16;
/// Generate document key to store in secret store.
pub fn generate_document_key(account_public: Public, server_key_public: Public) -> Result<EncryptedDocumentKey, Error> {
// generate random plain document key
let document_key = Random.generate().map_err(errors::encryption)?;
// encrypt document key using server key
let (common_point, encrypted_point) = encrypt_secret(document_key.public(), &server_key_public)?;
// ..and now encrypt document key with account public
let encrypted_key = crypto::ecies::encrypt(&account_public, &crypto::DEFAULT_MAC, document_key.public())
.map_err(errors::encryption)?;
Ok(EncryptedDocumentKey {
common_point: common_point.into(),
encrypted_point: encrypted_point.into(),
encrypted_key: encrypted_key.into(),
})
}
/// Encrypt document with distributely generated key.
pub fn encrypt_document(key: Bytes, document: Bytes) -> Result<Bytes, Error> {
// make document key
@ -114,6 +133,28 @@ fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_sha
Ok(decrypted_shadow)
}
fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result<(Public, Public), Error> {
// TODO: it is copypaste of `encrypt_secret` from secret_store/src/key_server_cluster/math.rs
// use shared version from SS math library, when it'll be available
let key_pair = Random.generate()
.map_err(errors::encryption)?;
// k * T
let mut common_point = math::generation_point();
math::public_mul_secret(&mut common_point, key_pair.secret())
.map_err(errors::encryption)?;
// M + k * y
let mut encrypted_point = joint_public.clone();
math::public_mul_secret(&mut encrypted_point, key_pair.secret())
.map_err(errors::encryption)?;
math::public_add(&mut encrypted_point, secret)
.map_err(errors::encryption)?;
Ok((common_point, encrypted_point))
}
#[cfg(test)]
mod tests {
use bytes::Bytes;

View File

@ -26,9 +26,10 @@ use ethcore::account_provider::AccountProvider;
use jsonrpc_core::Result;
use v1::helpers::errors;
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::{generate_document_key, encrypt_document,
decrypt_document, decrypt_document_with_shadow, ordered_servers_keccak};
use v1::traits::SecretStore;
use v1::types::{H160, H256, H512, Bytes};
use v1::types::{H160, H256, H512, Bytes, EncryptedDocumentKey};
/// Parity implementation.
pub struct SecretStoreClient {
@ -64,6 +65,13 @@ impl SecretStoreClient {
}
impl SecretStore for SecretStoreClient {
fn generate_document_key(&self, address: H160, password: String, server_key_public: H512) -> Result<EncryptedDocumentKey> {
let store = self.account_provider()?;
let account_public = store.account_public(address.into(), &password)
.map_err(|e| errors::account("Could not read account public.", e))?;
generate_document_key(account_public, server_key_public.into())
}
fn encrypt(&self, address: H160, password: String, key: Bytes, data: Bytes) -> Result<Bytes> {
encrypt_document(self.decrypt_key(address, password, key)?, data.0)
.map(Into::into)

View File

@ -16,6 +16,7 @@
use std::sync::Arc;
use crypto::DEFAULT_MAC;
use ethcore::account_provider::AccountProvider;
use ethkey::{KeyPair, Signature, verify_public};
@ -25,7 +26,7 @@ use v1::metadata::Metadata;
use v1::SecretStoreClient;
use v1::traits::secretstore::SecretStore;
use v1::helpers::secretstore::ordered_servers_keccak;
use v1::types::H256;
use v1::types::{H256, EncryptedDocumentKey};
struct Dependencies {
pub accounts: Arc<AccountProvider>,
@ -144,3 +145,31 @@ fn rpc_secretstore_sign_raw_hash() {
let hash = "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap();
assert!(verify_public(key_pair.public(), &signature, &hash).unwrap());
}
#[test]
fn rpc_secretstore_generate_document_key() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account
let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4".parse().unwrap();
let key_pair = KeyPair::from_secret(secret).unwrap();
deps.accounts.insert_account(key_pair.secret().clone(), "password").unwrap();
// execute generation request
let generation_request = r#"{"jsonrpc": "2.0", "method": "secretstore_generateDocumentKey", "params":[
"0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password",
"0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91"
], "id": 1}"#;
let generation_response = io.handle_request_sync(&generation_request).unwrap();
let generation_response = generation_response.replace(r#"{"jsonrpc":"2.0","result":"#, "");
let generation_response = generation_response.replace(r#","id":1}"#, "");
let generation_response: EncryptedDocumentKey = serde_json::from_str(&generation_response).unwrap();
// the only thing we can check is that 'encrypted_key' can be decrypted by passed account
assert!(deps.accounts.decrypt(
"00dfE63B22312ab4329aD0d28CaD8Af987A01932".parse().unwrap(),
Some("password".into()),
&DEFAULT_MAC,
&generation_response.encrypted_key.0).is_ok());
}

View File

@ -19,11 +19,16 @@
use std::collections::BTreeSet;
use jsonrpc_core::Result;
use v1::types::{H160, H256, H512, Bytes};
use v1::types::{H160, H256, H512, Bytes, EncryptedDocumentKey};
build_rpc_trait! {
/// Parity-specific rpc interface.
pub trait SecretStore {
/// Generate document key to store in secret store.
/// Arguments: `account`, `password`, `server_key_public`.
#[rpc(name = "secretstore_generateDocumentKey")]
fn generate_document_key(&self, H160, String, H512) -> Result<EncryptedDocumentKey>;
/// Encrypt data with key, received from secret store.
/// Arguments: `account`, `password`, `key`, `data`.
#[rpc(name = "secretstore_encrypt")]

View File

@ -35,6 +35,7 @@ mod node_kind;
mod provenance;
mod receipt;
mod rpc_settings;
mod secretstore;
mod sync;
mod trace;
mod trace_filter;
@ -67,6 +68,7 @@ pub use self::node_kind::{NodeKind, Availability, Capability};
pub use self::provenance::{Origin, DappId};
pub use self::receipt::Receipt;
pub use self::rpc_settings::RpcSettings;
pub use self::secretstore::EncryptedDocumentKey;
pub use self::sync::{
SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo,
TransactionStats, ChainStatus, EthProtocolInfo, PipProtocolInfo,

View 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/>.
use v1::types::{Bytes, H512};
/// Encrypted document key.
#[derive(Default, Debug, Serialize, PartialEq)]
#[cfg_attr(test, derive(Deserialize))]
pub struct EncryptedDocumentKey {
/// Common encryption point. Pass this to Secret Store 'Document key storing session'
pub common_point: H512,
/// Ecnrypted point. Pass this to Secret Store 'Document key storing session'.
pub encrypted_point: H512,
/// Document key itself, encrypted with passed account public. Pass this to 'secretstore_encrypt'.
pub encrypted_key: Bytes,
}
#[cfg(test)]
mod tests {
use serde_json;
use super::EncryptedDocumentKey;
#[test]
fn test_serialize_encrypted_document_key() {
let initial = EncryptedDocumentKey {
common_point: 1.into(),
encrypted_point: 2.into(),
encrypted_key: vec![3].into(),
};
let serialized = serde_json::to_string(&initial).unwrap();
assert_eq!(serialized, r#"{"common_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","encrypted_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002","encrypted_key":"0x03"}"#);
let deserialized: EncryptedDocumentKey = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.common_point, 1.into());
assert_eq!(deserialized.encrypted_point, 2.into());
assert_eq!(deserialized.encrypted_key, vec![3].into());
}
}