From 2806865811af0602c2381c61c535bd053b96108a Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 12 Feb 2018 12:09:45 +0300 Subject: [PATCH 1/2] SecretStore: secretstore_generateDocumentKey RPC --- rpc/src/v1/helpers/secretstore.rs | 38 +++++++++++++++++++++-- rpc/src/v1/impls/secretstore.rs | 9 ++++-- rpc/src/v1/tests/mocked/secretstore.rs | 14 +++++++++ rpc/src/v1/traits/secretstore.rs | 7 ++++- rpc/src/v1/types/mod.rs | 2 ++ rpc/src/v1/types/secretstore.rs | 43 ++++++++++++++++++++++++++ 6 files changed, 108 insertions(+), 5 deletions(-) create mode 100644 rpc/src/v1/types/secretstore.rs diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index 4834611d83c..56d5e5e70f0 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -16,17 +16,26 @@ 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(server_key_public: Public) -> Result { + // generate random plain document key + let document_key = Random.generate().map_err(errors::encryption)?; + + // encrypt this key using server key + encrypt_secret(document_key.public(), &server_key_public) +} + /// Encrypt document with distributely generated key. pub fn encrypt_document(key: Bytes, document: Bytes) -> Result { // make document key @@ -114,6 +123,31 @@ fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_sha Ok(decrypted_shadow) } +fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result { + // 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(EncryptedDocumentKey { + common_point: common_point.into(), + encrypted_point: encrypted_point.into(), + }) +} + #[cfg(test)] mod tests { use bytes::Bytes; diff --git a/rpc/src/v1/impls/secretstore.rs b/rpc/src/v1/impls/secretstore.rs index 14cef558768..d87d79c7eab 100644 --- a/rpc/src/v1/impls/secretstore.rs +++ b/rpc/src/v1/impls/secretstore.rs @@ -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,10 @@ impl SecretStoreClient { } impl SecretStore for SecretStoreClient { + fn generate_document_key(&self, server_key_public: H512) -> Result { + generate_document_key(server_key_public.into()) + } + fn encrypt(&self, address: H160, password: String, key: Bytes, data: Bytes) -> Result { encrypt_document(self.decrypt_key(address, password, key)?, data.0) .map(Into::into) diff --git a/rpc/src/v1/tests/mocked/secretstore.rs b/rpc/src/v1/tests/mocked/secretstore.rs index e209f6a9676..509fe625992 100644 --- a/rpc/src/v1/tests/mocked/secretstore.rs +++ b/rpc/src/v1/tests/mocked/secretstore.rs @@ -144,3 +144,17 @@ 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(); + + // execute generation request + let generation_request = r#"{"jsonrpc": "2.0", "method": "secretstore_generateDocumentKey", "params":[ + "0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91" + ], "id": 1}"#; + let _ = io.handle_request_sync(&generation_request).unwrap(); + + // the only thing we can check is that key is generated without errors, because generation is randomized +} diff --git a/rpc/src/v1/traits/secretstore.rs b/rpc/src/v1/traits/secretstore.rs index e3ce241850d..bde17806aac 100644 --- a/rpc/src/v1/traits/secretstore.rs +++ b/rpc/src/v1/traits/secretstore.rs @@ -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: `server_key_public`. + #[rpc(name = "secretstore_generateDocumentKey")] + fn generate_document_key(&self, H512) -> Result; + /// Encrypt data with key, received from secret store. /// Arguments: `account`, `password`, `key`, `data`. #[rpc(name = "secretstore_encrypt")] diff --git a/rpc/src/v1/types/mod.rs b/rpc/src/v1/types/mod.rs index c6963f2cf6f..65d1feeb4c3 100644 --- a/rpc/src/v1/types/mod.rs +++ b/rpc/src/v1/types/mod.rs @@ -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, diff --git a/rpc/src/v1/types/secretstore.rs b/rpc/src/v1/types/secretstore.rs new file mode 100644 index 00000000000..00218191190 --- /dev/null +++ b/rpc/src/v1/types/secretstore.rs @@ -0,0 +1,43 @@ +// 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 . + +use v1::types::H512; + +/// Sync info +#[derive(Default, Debug, Serialize, PartialEq)] +pub struct EncryptedDocumentKey { + /// Common encryption point. + pub common_point: H512, + /// Ecnrypted point. + pub encrypted_point: H512, +} + +#[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(), + }; + + let serialized = serde_json::to_string(&initial).unwrap(); + assert_eq!(serialized, r#"{"common_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","encrypted_point":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002"}"#); + } +} From a0bb41771ceaeac502e9f3019e4149ec6546f046 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 12 Feb 2018 12:49:27 +0300 Subject: [PATCH 2/2] SecretStore: return encrypted_key from secretstore_generateDocumentKey --- rpc/src/v1/helpers/secretstore.rs | 23 +++++++++++++++-------- rpc/src/v1/impls/secretstore.rs | 7 +++++-- rpc/src/v1/tests/mocked/secretstore.rs | 23 +++++++++++++++++++---- rpc/src/v1/traits/secretstore.rs | 4 ++-- rpc/src/v1/types/secretstore.rs | 19 ++++++++++++++----- 5 files changed, 55 insertions(+), 21 deletions(-) diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index 56d5e5e70f0..fbcc167e239 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -28,12 +28,22 @@ use tiny_keccak::Keccak; const INIT_VEC_LEN: usize = 16; /// Generate document key to store in secret store. -pub fn generate_document_key(server_key_public: Public) -> Result { +pub fn generate_document_key(account_public: Public, server_key_public: Public) -> Result { // generate random plain document key let document_key = Random.generate().map_err(errors::encryption)?; - // encrypt this key using server key - encrypt_secret(document_key.public(), &server_key_public) + // 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. @@ -123,7 +133,7 @@ fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_sha Ok(decrypted_shadow) } -fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result { +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 @@ -142,10 +152,7 @@ fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result Result { - generate_document_key(server_key_public.into()) + fn generate_document_key(&self, address: H160, password: String, server_key_public: H512) -> Result { + 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 { diff --git a/rpc/src/v1/tests/mocked/secretstore.rs b/rpc/src/v1/tests/mocked/secretstore.rs index 509fe625992..d3ab781bcfd 100644 --- a/rpc/src/v1/tests/mocked/secretstore.rs +++ b/rpc/src/v1/tests/mocked/secretstore.rs @@ -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, @@ -150,11 +151,25 @@ 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 _ = io.handle_request_sync(&generation_request).unwrap(); - - // the only thing we can check is that key is generated without errors, because generation is randomized + 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()); } diff --git a/rpc/src/v1/traits/secretstore.rs b/rpc/src/v1/traits/secretstore.rs index bde17806aac..6d2e5669c00 100644 --- a/rpc/src/v1/traits/secretstore.rs +++ b/rpc/src/v1/traits/secretstore.rs @@ -25,9 +25,9 @@ build_rpc_trait! { /// Parity-specific rpc interface. pub trait SecretStore { /// Generate document key to store in secret store. - /// Arguments: `server_key_public`. + /// Arguments: `account`, `password`, `server_key_public`. #[rpc(name = "secretstore_generateDocumentKey")] - fn generate_document_key(&self, H512) -> Result; + fn generate_document_key(&self, H160, String, H512) -> Result; /// Encrypt data with key, received from secret store. /// Arguments: `account`, `password`, `key`, `data`. diff --git a/rpc/src/v1/types/secretstore.rs b/rpc/src/v1/types/secretstore.rs index 00218191190..4388b308ba2 100644 --- a/rpc/src/v1/types/secretstore.rs +++ b/rpc/src/v1/types/secretstore.rs @@ -14,15 +14,18 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use v1::types::H512; +use v1::types::{Bytes, H512}; -/// Sync info +/// Encrypted document key. #[derive(Default, Debug, Serialize, PartialEq)] +#[cfg_attr(test, derive(Deserialize))] pub struct EncryptedDocumentKey { - /// Common encryption point. + /// Common encryption point. Pass this to Secret Store 'Document key storing session' pub common_point: H512, - /// Ecnrypted point. + /// 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)] @@ -35,9 +38,15 @@ mod tests { 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"}"#); + 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()); } }