SecretStore: Kovan integration initial commit
This commit is contained in:
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
55
secret_store/src/listener/mod.rs
Normal file
55
secret_store/src/listener/mod.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
138
secret_store/src/listener/service_contract_listener.rs
Normal file
138
secret_store/src/listener/service_contract_listener.rs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user