SecretStore: Kovan integration initial commit

This commit is contained in:
Svyatoslav Nikolsky
2017-11-14 14:26:31 +03:00
parent 361debd277
commit abfb9fccd3
9 changed files with 276 additions and 70 deletions

View File

@@ -54,12 +54,12 @@ mod types;
mod traits;
mod acl_storage;
mod http_listener;
mod key_server;
mod key_storage;
mod serialization;
mod key_server_set;
mod node_key_pair;
mod listener;
use std::sync::Arc;
use ethcore::client::Client;
@@ -71,8 +71,6 @@ pub use self::node_key_pair::{PlainNodeKeyPair, KeyStoreNodeKeyPair};
/// Start new key server instance
pub fn start(client: Arc<Client>, self_key_pair: Arc<NodeKeyPair>, config: ServiceConfiguration) -> Result<Box<KeyServer>, Error> {
use std::sync::Arc;
let acl_storage: Arc<acl_storage::AclStorage> = if config.acl_check_enabled {
acl_storage::OnChainAclStorage::new(&client)
} else {
@@ -80,7 +78,12 @@ pub fn start(client: Arc<Client>, self_key_pair: Arc<NodeKeyPair>, config: Servi
};
let key_server_set = key_server_set::OnChainKeyServerSet::new(&client, config.cluster_config.nodes.clone())?;
let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(&config)?);
let key_server = key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair, acl_storage, key_storage)?;
let listener = http_listener::KeyServerHttpListener::start(config.listener_address, key_server)?;
let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set, self_key_pair.clone(), acl_storage, key_storage)?);
let http_listener = match config.listener_address {
Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, key_server.clone())?),
None => None,
};
let contract_listener = listener::service_contract_listener::ServiceContractListener::new(&client, key_server.clone(), self_key_pair);
let listener = listener::Listener::new(key_server, Some(http_listener), Some(contract_listener));
Ok(Box::new(listener))
}

View File

@@ -25,9 +25,9 @@ use serde::Serialize;
use serde_json;
use url::percent_encoding::percent_decode;
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
use traits::KeyServer;
use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic};
use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddress, RequestSignature, ServerKeyId,
use types::all::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId,
EncryptedDocumentKey, EncryptedDocumentKeyShadow};
/// Key server http-requests listener. Available requests:
@@ -38,9 +38,9 @@ use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddr
/// To get document key shadow: GET /shadow/{server_key_id}/{signature}
/// To sign message with server key: GET /{server_key_id}/{signature}/{message_hash}
pub struct KeyServerHttpListener<T: KeyServer + 'static> {
http_server: Option<HttpListening>,
handler: Arc<KeyServerSharedHttpHandler<T>>,
pub struct KeyServerHttpListener {
http_server: HttpListening,
_handler: Arc<KeyServerSharedHttpHandler>,
}
/// Parsed http request
@@ -63,77 +63,44 @@ enum Request {
}
/// Cloneable http handler
struct KeyServerHttpHandler<T: KeyServer + 'static> {
handler: Arc<KeyServerSharedHttpHandler<T>>,
struct KeyServerHttpHandler {
handler: Arc<KeyServerSharedHttpHandler>,
}
/// Shared http handler
struct KeyServerSharedHttpHandler<T: KeyServer + 'static> {
key_server: T,
struct KeyServerSharedHttpHandler {
key_server: Arc<KeyServer>,
}
impl<T> KeyServerHttpListener<T> where T: KeyServer + 'static {
impl KeyServerHttpListener {
/// Start KeyServer http listener
pub fn start(listener_address: Option<NodeAddress>, key_server: T) -> Result<Self, Error> {
pub fn start(listener_address: NodeAddress, key_server: Arc<KeyServer>) -> Result<Self, Error> {
let shared_handler = Arc::new(KeyServerSharedHttpHandler {
key_server: key_server,
});
let http_server = listener_address
.map(|listener_address| format!("{}:{}", listener_address.address, listener_address.port))
.map(|listener_address| HttpServer::http(&listener_address).expect("cannot start HttpServer"))
.map(|http_server| http_server.handle(KeyServerHttpHandler {
let listener_address = format!("{}:{}", listener_address.address, listener_address.port);
let http_server = HttpServer::http(&listener_address).expect("cannot start HttpServer");
let http_server = http_server.handle(KeyServerHttpHandler {
handler: shared_handler.clone(),
}).expect("cannot start HttpServer"));
}).expect("cannot start HttpServer");
let listener = KeyServerHttpListener {
http_server: http_server,
handler: shared_handler,
_handler: shared_handler,
};
Ok(listener)
}
}
impl<T> KeyServer for KeyServerHttpListener<T> where T: KeyServer + 'static {}
impl<T> ServerKeyGenerator for KeyServerHttpListener<T> where T: KeyServer + 'static {
fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<Public, Error> {
self.handler.key_server.generate_key(key_id, signature, threshold)
}
}
impl<T> DocumentKeyServer for KeyServerHttpListener<T> where T: KeyServer + 'static {
fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> {
self.handler.key_server.store_document_key(key_id, signature, common_point, encrypted_document_key)
}
fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<EncryptedDocumentKey, Error> {
self.handler.key_server.generate_document_key(key_id, signature, threshold)
}
fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKey, Error> {
self.handler.key_server.restore_document_key(key_id, signature)
}
fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKeyShadow, Error> {
self.handler.key_server.restore_document_key_shadow(key_id, signature)
}
}
impl <T> MessageSigner for KeyServerHttpListener<T> where T: KeyServer + 'static {
fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result<EncryptedMessageSignature, Error> {
self.handler.key_server.sign_message(key_id, signature, message)
}
}
impl<T> Drop for KeyServerHttpListener<T> where T: KeyServer + 'static {
impl Drop for KeyServerHttpListener {
fn drop(&mut self) {
// ignore error as we are dropping anyway
self.http_server.take().map(|mut s| { let _ = s.close(); });
}
}
impl<T> HttpHandler for KeyServerHttpHandler<T> where T: KeyServer + 'static {
impl HttpHandler for KeyServerHttpHandler {
fn handle(&self, req: HttpRequest, mut res: HttpResponse) {
if req.headers.has::<header::Origin>() {
warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method, req.uri);
@@ -310,6 +277,7 @@ fn parse_request(method: &HttpMethod, uri_path: &str) -> Request {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use hyper::method::Method as HttpMethod;
use key_server::tests::DummyKeyServer;
use types::all::NodeAddress;
@@ -317,12 +285,12 @@ mod tests {
#[test]
fn http_listener_successfully_drops() {
let key_server = DummyKeyServer;
let key_server = Arc::new(DummyKeyServer);
let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 };
let listener = KeyServerHttpListener::start(Some(address), key_server).unwrap();
drop(listener);
}
#[test]
fn parse_request_successful() {
// POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key

View File

@@ -0,0 +1,55 @@
pub mod http_listener;
pub mod service_contract_listener;
use std::sync::Arc;
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId,
EncryptedDocumentKey, EncryptedDocumentKeyShadow};
pub struct Listener {
key_server: Arc<KeyServer>,
_http: Option<http_listener::KeyServerHttpListener>,
_contract: Option<Arc<service_contract_listener::ServiceContractListener>>,
}
impl Listener {
pub fn new(key_server: Arc<KeyServer>, http: Option<http_listener::KeyServerHttpListener>, contract: Option<Arc<service_contract_listener::ServiceContractListener>>) -> Self {
Self {
key_server: key_server,
_http: http,
_contract: contract,
}
}
}
impl KeyServer for Listener {}
impl ServerKeyGenerator for Listener {
fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<Public, Error> {
self.key_server.generate_key(key_id, signature, threshold)
}
}
impl DocumentKeyServer for Listener {
fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> {
self.key_server.store_document_key(key_id, signature, common_point, encrypted_document_key)
}
fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<EncryptedDocumentKey, Error> {
self.key_server.generate_document_key(key_id, signature, threshold)
}
fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKey, Error> {
self.key_server.restore_document_key(key_id, signature)
}
fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result<EncryptedDocumentKeyShadow, Error> {
self.key_server.restore_document_key_shadow(key_id, signature)
}
}
impl MessageSigner for Listener {
fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result<EncryptedMessageSignature, Error> {
self.key_server.sign_message(key_id, signature, message)
}
}

View File

@@ -0,0 +1,138 @@
// 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::sync::{Arc, Weak};
use parking_lot::Mutex;
use ethcore::filter::Filter;
use ethcore::client::{Client, BlockChainClient, BlockId, ChainNotify};
use native_contracts::SecretStoreService;
use ethkey::{Random, Generator, sign};
use bytes::Bytes;
use hash::keccak;
use bigint::hash::H256;
use util::Address;
use {NodeKeyPair, KeyServer};
/// Name of the SecretStore contract in the registry.
const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service";
/// Key server has been added to the set.
const SERVER_KEY_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRequested(bytes32)";
lazy_static! {
static ref SERVER_KEY_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_REQUESTED_EVENT_NAME);
}
/// SecretStore <-> Authority connector. Duties:
/// 1. Listen for new requests on SecretStore contract
/// 2. Redirects requests for key server
/// 3. Publishes response on SecretStore contract
pub struct ServiceContractListener {
/// Cached on-chain contract.
contract: Mutex<CachedContract>,
}
/// Cached on-chain Key Server set contract.
struct CachedContract {
/// Blockchain client.
client: Weak<Client>,
/// Contract.
contract: SecretStoreService,
/// Contract address.
contract_addr: Option<Address>,
/// Key server reference.
key_server: Arc<KeyServer>,
/// This node key pair.
self_key_pair: Arc<NodeKeyPair>,
}
impl ServiceContractListener {
pub fn new(client: &Arc<Client>, key_server: Arc<KeyServer>, self_key_pair: Arc<NodeKeyPair>) -> Arc<ServiceContractListener> {
let contract = Arc::new(ServiceContractListener {
contract: Mutex::new(CachedContract::new(client, key_server, self_key_pair)),
});
client.add_notify(contract.clone());
contract
}
}
impl ChainNotify for ServiceContractListener {
fn new_blocks(&self, _imported: Vec<H256>, _invalid: Vec<H256>, enacted: Vec<H256>, _retracted: Vec<H256>, _sealed: Vec<H256>, _proposed: Vec<Bytes>, _duration: u64) {
if !enacted.is_empty() {
self.contract.lock().update(enacted)
}
}
}
impl CachedContract {
pub fn new(client: &Arc<Client>, key_server: Arc<KeyServer>, self_key_pair: Arc<NodeKeyPair>) -> Self {
CachedContract {
client: Arc::downgrade(client),
contract: SecretStoreService::new(Default::default()), // we aren't going to call contract => could use default address
contract_addr: client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned()),
key_server: key_server,
self_key_pair: self_key_pair,
}
}
pub fn update(&mut self, enacted: Vec<H256>) {
if let Some(client) = self.client.upgrade() {
// update contract address
self.contract_addr = client.registry_address(SERVICE_CONTRACT_REGISTRY_NAME.to_owned());
// check for new key requests.
// NOTE: If contract is changed, or unregistered && there are several enacted blocks
// in single update call, some requests in old contract can be abandoned (we get contract_address from latest block)
// && check for requests in this contract for every enacted block.
// The opposite is also true (we can process requests of contract, before it actually becames a SS contract).
if let Some(contract_addr) = self.contract_addr.as_ref() {
// TODO: in case of reorgs we might process requests for free (maybe wait for several confirmations???) && publish keys without request
// TODO: in case of reorgs we might publish keys to forked branch (re-submit transaction???)
for block in enacted {
let request_logs = client.logs(Filter {
from_block: BlockId::Hash(block.clone()),
to_block: BlockId::Hash(block),
address: Some(vec![contract_addr.clone()]),
topics: vec![
Some(vec![*SERVER_KEY_REQUESTED_EVENT_NAME_HASH]),
None,
None,
None,
],
limit: None,
});
// TODO: it actually should queue tasks to separate thread
// + separate thread at the beginning should read all requests from contract
// and then start processing logs
for request in request_logs {
// TODO: check if we are selected to process this request
let key_id = request.entry.topics[1];
let key = Random.generate().unwrap();
let signature = sign(key.secret(), &key_id).unwrap();
let server_key = self.key_server.generate_key(&key_id, &signature, 0).unwrap();
println!("=== generated key: {:?}", server_key);
// publish generated key
let server_key_hash = keccak(server_key);
let signed_key = self.self_key_pair.sign(&server_key_hash).unwrap();
let transaction_data = self.contract.encode_server_key_generated_input(key_id, server_key.to_vec(), signed_key.v(), signed_key.r().into(), signed_key.s().into()).unwrap();
client.transact_contract(contract_addr.clone(), transaction_data).unwrap();
}
}
}
}
}
}