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

SecretStore: secretstore_generateDocumentKey RPC #7864

Merged
merged 2 commits into from
Mar 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions rpc/src/v1/helpers/secretstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
12 changes: 10 additions & 2 deletions rpc/src/v1/impls/secretstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
Expand Down
31 changes: 30 additions & 1 deletion rpc/src/v1/tests/mocked/secretstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

use std::sync::Arc;

use crypto::DEFAULT_MAC;
use ethcore::account_provider::AccountProvider;
use ethkey::{KeyPair, Signature, verify_public};

Expand All @@ -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>,
Expand Down Expand Up @@ -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());
}
7 changes: 6 additions & 1 deletion rpc/src/v1/traits/secretstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down
2 changes: 2 additions & 0 deletions rpc/src/v1/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ mod node_kind;
mod provenance;
mod receipt;
mod rpc_settings;
mod secretstore;
mod sync;
mod trace;
mod trace_filter;
Expand Down Expand Up @@ -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,
Expand Down
52 changes: 52 additions & 0 deletions rpc/src/v1/types/secretstore.rs
Original file line number Diff line number Diff line change
@@ -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());
}
}