SecretStore: servers set change session api (#6925)

* SecretStore: first key versions flush

* SecretStore: key versions in encryption session

* SecretStore: flush key versions negotiation session

* SecretStore: connected key version negotiation session to cluster

* SecretStore: cluster sessions container refactoring

* SecretStore: flush

* SecretStore: flush key versions

* SecretStore: flush

* SecretStore: delegation proto

* SecretStore: decryption_session_is_delegated_when_node_does_not_have_key_share

* SecretStore: fixed version in decryption session

* SecretStore: signing_session_is_delegated_when_node_does_not_have_key_share

* SecretStore: started restoring admin sessions

* SecretStore: restoring admin sessions

* SecretStore: removed obsolete ShareRemove && ShareMove sessions

* SecretStore: ShareAdd math tests only require old_t+1 nodes

* SecretStore: ShareAdd revamp using new math backend

* SecretStore: do not include isolated nodes into consensus_group

* SecretStore: ServersSetChange + ShareAdd revamp

* removed debug printlns

* SecretStore: key version negotiation tests

* SecretStore: removed debug/merge artifacts

* SecretStore: fixed master node selection

* SecretStore: cleanup + tests + fixes

* SecretStore: uncommented tests

* SecretStore: cleaning up

* SecretStore: cleaning up + tests

* SecretStore: cleaning up

* SecretStore: cleaning up && tests

* SecretStore: fixing TODOs

* SecretStore: fixing TODOs + cleanup

* SecretStore: fixing TODOs

* SecretStore: nodes_add_to_the_node_with_obsolete_version

* SecretStore: nodes_add_fails_when_not_enough_share_owners_are_connected

* SecretStore: tests

* SecretStore: signing && delegation tests

* SecretStore: signing && decryption tests when some nodes are isolated

* SecretStore: sessions_are_removed_when_initialization_fails

* SecretStore: ceaning up

* SecretStore: removed obsolete comments

* SecretStore: signing_session_completes_if_node_does_not_have_a_share

* SecretStore: initial ServersSetChange API

* SecretStore: added secretstore_signServersSet RPC

* SecretStore: ChangeServersSet parse tests

* SecretStore: fixes after manual ServersSetChange tests

* lost file

* fixed network ports overlap in tests

* lost files
This commit is contained in:
Svyatoslav Nikolsky 2017-11-16 19:34:23 +03:00 committed by Arkadiy Paronyan
parent 605cd5cd9f
commit e16f6fb9d9
17 changed files with 268 additions and 56 deletions

1
Cargo.lock generated
View File

@ -2145,6 +2145,7 @@ dependencies = [
"serde_json 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"stats 0.1.0",
"time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"tokio-timer 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"transient-hashmap 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"vm 0.1.0",

View File

@ -24,6 +24,7 @@ serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
time = "0.1"
tiny-keccak = "1.3"
tokio-timer = "0.1"
transient-hashmap = "0.4"
itertools = "0.5"

View File

@ -37,6 +37,7 @@ extern crate semver;
extern crate serde;
extern crate serde_json;
extern crate time;
extern crate tiny_keccak;
extern crate tokio_timer;
extern crate transient_hashmap;

View File

@ -14,12 +14,15 @@
// 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::collections::BTreeSet;
use rand::{Rng, OsRng};
use ethkey::{Public, Secret, math};
use crypto;
use bytes::Bytes;
use jsonrpc_core::Error;
use v1::helpers::errors;
use v1::types::{H256, H512};
use tiny_keccak::Keccak;
/// Initialization vector length.
const INIT_VEC_LEN: usize = 16;
@ -61,11 +64,25 @@ pub fn decrypt_document(key: Bytes, mut encrypted_document: Bytes) -> Result<Byt
Ok(document)
}
/// Decrypt document given secret shadow.
pub fn decrypt_document_with_shadow(decrypted_secret: Public, common_point: Public, shadows: Vec<Secret>, encrypted_document: Bytes) -> Result<Bytes, Error> {
let key = decrypt_with_shadow_coefficients(decrypted_secret, common_point, shadows)?;
decrypt_document(key.to_vec(), encrypted_document)
}
/// Calculate Keccak(ordered servers set)
pub fn ordered_servers_keccak(servers_set: BTreeSet<H512>) -> H256 {
let mut servers_set_keccak = Keccak::new_keccak256();
for server in servers_set {
servers_set_keccak.update(&server.0);
}
let mut servers_set_keccak_value = [0u8; 32];
servers_set_keccak.finalize(&mut servers_set_keccak_value);
servers_set_keccak_value.into()
}
fn into_document_key(key: Bytes) -> Result<Bytes, Error> {
// key is a previously distributely generated Public
if key.len() != 64 {

View File

@ -16,6 +16,7 @@
//! SecretStore-specific rpc implementation.
use std::collections::BTreeSet;
use std::sync::Arc;
use crypto::DEFAULT_MAC;
@ -25,7 +26,7 @@ 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};
use v1::helpers::secretstore::{encrypt_document, decrypt_document, decrypt_document_with_shadow, ordered_servers_keccak};
use v1::traits::SecretStore;
use v1::types::{H160, H512, Bytes};
@ -82,4 +83,13 @@ impl SecretStore for SecretStoreClient {
decrypt_document_with_shadow(decrypted_secret.into(), common_point.into(), shadows, data.0)
.map(Into::into)
}
fn sign_servers_set(&self, address: H160, password: String, servers_set: BTreeSet<H512>) -> Result<Bytes> {
let servers_set_keccak_value = ordered_servers_keccak(servers_set);
let store = self.account_provider()?;
store
.sign(address.into(), Some(password), servers_set_keccak_value.into())
.map(|s| Bytes::new((*s).to_vec()))
.map_err(|e| errors::account("Could not sign servers set.", e))
}
}

View File

@ -17,12 +17,14 @@
use std::sync::Arc;
use ethcore::account_provider::AccountProvider;
use ethkey::{KeyPair, Signature, verify_public};
use serde_json;
use jsonrpc_core::{IoHandler, Success};
use v1::metadata::Metadata;
use v1::SecretStoreClient;
use v1::traits::secretstore::SecretStore;
use v1::helpers::secretstore::ordered_servers_keccak;
struct Dependencies {
pub accounts: Arc<AccountProvider>,
@ -51,7 +53,7 @@ fn rpc_secretstore_encrypt_and_decrypt() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account && unlock it
// insert new account
let secret = "c1f1cfe279a5c350d13795bce162941967340c8a228e6ba175489afc564a5bef".parse().unwrap();
deps.accounts.insert_account(secret, "password").unwrap();
@ -81,7 +83,7 @@ fn rpc_secretstore_shadow_decrypt() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account && unlock it
// insert new account
let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4".parse().unwrap();
deps.accounts.insert_account(secret, "password").unwrap();
@ -96,3 +98,31 @@ fn rpc_secretstore_shadow_decrypt() {
let decryption_response = io.handle_request_sync(&decryption_request).unwrap();
assert_eq!(decryption_response, r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":1}"#);
}
#[test]
fn rpc_secretstore_sign_servers_set() {
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 signing request
let signing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_signServersSet", "params":[
"0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password",
["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]
], "id": 1}"#;
let signing_response = io.handle_request_sync(&signing_request).unwrap();
let signing_response = signing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, "");
let signing_response = signing_response.replace(r#"","id":1}"#, "");
let signature: Signature = signing_response.parse().unwrap();
let servers_set_keccak = ordered_servers_keccak(vec![
"843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(),
"07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap()
].into_iter().collect());
assert!(verify_public(key_pair.public(), &signature, &servers_set_keccak.into()).unwrap());
}

View File

@ -16,6 +16,7 @@
//! SecretStore-specific rpc interface.
use std::collections::BTreeSet;
use jsonrpc_core::Result;
use v1::types::{H160, H512, Bytes};
@ -37,5 +38,10 @@ build_rpc_trait! {
/// Arguments: `account`, `password`, `decrypted_secret`, `common_point`, `decrypt_shadows`, `data`.
#[rpc(name = "secretstore_shadowDecrypt")]
fn shadow_decrypt(&self, H160, String, H512, H512, Vec<Bytes>, Bytes) -> Result<Bytes>;
/// Sign servers set for use in ServersSetChange session.
/// Arguments: `account`, `password`, `servers_set`.
#[rpc(name = "secretstore_signServersSet")]
fn sign_servers_set(&self, H160, String, BTreeSet<H512>) -> Result<Bytes>;
}
}

View File

@ -14,6 +14,8 @@
// 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::collections::BTreeSet;
use std::io::Read;
use std::sync::Arc;
use hyper::header;
use hyper::uri::RequestUri;
@ -25,10 +27,10 @@ use serde::Serialize;
use serde_json;
use url::percent_encoding::percent_decode;
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
use traits::{ServerKeyGenerator, AdminSessionsServer, DocumentKeyServer, MessageSigner, KeyServer};
use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic};
use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddress, RequestSignature, ServerKeyId,
EncryptedDocumentKey, EncryptedDocumentKeyShadow};
EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId};
/// Key server http-requests listener. Available requests:
/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold}
@ -37,6 +39,7 @@ use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddr
/// To get document key: GET /{server_key_id}/{signature}
/// To get document key shadow: GET /shadow/{server_key_id}/{signature}
/// To sign message with server key: GET /{server_key_id}/{signature}/{message_hash}
/// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids
pub struct KeyServerHttpListener<T: KeyServer + 'static> {
http_server: Option<HttpListening>,
@ -60,6 +63,8 @@ enum Request {
GetDocumentKeyShadow(ServerKeyId, RequestSignature),
/// Sign message.
SignMessage(ServerKeyId, RequestSignature, MessageHash),
/// Change servers set.
ChangeServersSet(RequestSignature, RequestSignature, BTreeSet<NodeId>),
}
/// Cloneable http handler
@ -96,6 +101,12 @@ impl<T> KeyServerHttpListener<T> where T: KeyServer + 'static {
impl<T> KeyServer for KeyServerHttpListener<T> where T: KeyServer + 'static {}
impl<T> AdminSessionsServer for KeyServerHttpListener<T> where T: KeyServer + 'static {
fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet<NodeId>) -> Result<(), Error> {
self.handler.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set)
}
}
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)
@ -134,17 +145,24 @@ impl<T> Drop for KeyServerHttpListener<T> where T: KeyServer + 'static {
}
impl<T> HttpHandler for KeyServerHttpHandler<T> where T: KeyServer + 'static {
fn handle(&self, req: HttpRequest, mut res: HttpResponse) {
fn handle(&self, mut req: HttpRequest, mut res: HttpResponse) {
if req.headers.has::<header::Origin>() {
warn!(target: "secretstore", "Ignoring {}-request {} with Origin header", req.method, req.uri);
*res.status_mut() = HttpStatusCode::NotFound;
return;
}
let mut req_body = Default::default();
if let Err(error) = req.read_to_string(&mut req_body) {
warn!(target: "secretstore", "Error {} reading body of {}-request {}", error, req.method, req.uri);
*res.status_mut() = HttpStatusCode::BadRequest;
return;
}
let req_method = req.method.clone();
let req_uri = req.uri.clone();
match &req_uri {
&RequestUri::AbsolutePath(ref path) => match parse_request(&req_method, &path) {
&RequestUri::AbsolutePath(ref path) => match parse_request(&req_method, &path, &req_body) {
Request::GenerateServerKey(document, signature, threshold) => {
return_server_public_key(req, res, self.handler.key_server.generate_key(&document, &signature, threshold)
.map_err(|err| {
@ -187,6 +205,13 @@ impl<T> HttpHandler for KeyServerHttpHandler<T> where T: KeyServer + 'static {
err
}));
},
Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => {
return_empty(req, res, self.handler.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set)
.map_err(|err| {
warn!(target: "secretstore", "ChangeServersSet request {} has failed with: {}", req_uri, err);
err
}));
},
Request::Invalid => {
warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri);
*res.status_mut() = HttpStatusCode::BadRequest;
@ -261,7 +286,7 @@ fn return_error(mut res: HttpResponse, err: Error) {
}
}
fn parse_request(method: &HttpMethod, uri_path: &str) -> Request {
fn parse_request(method: &HttpMethod, uri_path: &str, body: &str) -> Request {
let uri_path = match percent_decode(uri_path.as_bytes()).decode_utf8() {
Ok(path) => path,
Err(_) => return Request::Invalid,
@ -272,6 +297,10 @@ fn parse_request(method: &HttpMethod, uri_path: &str) -> Request {
return Request::Invalid;
}
if path[0] == "admin" {
return parse_admin_request(method, path, body);
}
let (is_shadow_request, args_offset) = if &path[0] == "shadow" { (true, 1) } else { (false, 0) };
let args_count = path.len() - args_offset;
if args_count < 2 || path[args_offset].is_empty() || path[args_offset + 1].is_empty() {
@ -308,9 +337,35 @@ fn parse_request(method: &HttpMethod, uri_path: &str) -> Request {
}
}
fn parse_admin_request(method: &HttpMethod, path: Vec<String>, body: &str) -> Request {
let args_count = path.len();
if *method != HttpMethod::Post || args_count != 4 || path[1] != "servers_set_change" {
return Request::Invalid;
}
let old_set_signature = match path[2].parse() {
Ok(signature) => signature,
_ => return Request::Invalid,
};
let new_set_signature = match path[3].parse() {
Ok(signature) => signature,
_ => return Request::Invalid,
};
let new_servers_set: BTreeSet<SerializablePublic> = match serde_json::from_str(body) {
Ok(new_servers_set) => new_servers_set,
_ => return Request::Invalid,
};
Request::ChangeServersSet(old_set_signature, new_set_signature,
new_servers_set.into_iter().map(Into::into).collect())
}
#[cfg(test)]
mod tests {
use hyper::method::Method as HttpMethod;
use ethkey::Public;
use key_server::tests::DummyKeyServer;
use types::all::NodeAddress;
use super::{parse_request, Request, KeyServerHttpListener};
@ -326,48 +381,66 @@ mod tests {
#[test]
fn parse_request_successful() {
// POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key
assert_eq!(parse_request(&HttpMethod::Post, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2"),
assert_eq!(parse_request(&HttpMethod::Post, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
Request::GenerateServerKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
2));
// POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} => store encrypted document key
assert_eq!(parse_request(&HttpMethod::Post, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb"),
assert_eq!(parse_request(&HttpMethod::Post, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()),
Request::StoreDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
"b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap(),
"1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".parse().unwrap()));
// POST /{server_key_id}/{signature}/{threshold} => generate server && document key
assert_eq!(parse_request(&HttpMethod::Post, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2"),
assert_eq!(parse_request(&HttpMethod::Post, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()),
Request::GenerateDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
2));
// GET /{server_key_id}/{signature} => get document key
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"),
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
assert_eq!(parse_request(&HttpMethod::Get, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"),
assert_eq!(parse_request(&HttpMethod::Get, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
// GET /shadow/{server_key_id}/{signature} => get document key shadow
assert_eq!(parse_request(&HttpMethod::Get, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"),
assert_eq!(parse_request(&HttpMethod::Get, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()),
Request::GetDocumentKeyShadow("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap()));
// GET /{server_key_id}/{signature}/{message_hash} => sign message with server key
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c"),
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()),
Request::SignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(),
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
"281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()));
// POST /admin/servers_set_change/{old_set_signature}/{new_set_signature} + body
let node1: Public = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap();
let node2: Public = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap();
let nodes = vec![node1, node2].into_iter().collect();
assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01",
&r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#),
Request::ChangeServersSet(
"a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
"b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(),
nodes,
));
}
#[test]
fn parse_request_failed() {
assert_eq!(parse_request(&HttpMethod::Get, ""), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/shadow"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "///2"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/shadow///2"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/a/b"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002"), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/shadow", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "///2", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/shadow///2", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/a/b", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/xxx/yyy",
&r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#),
Request::Invalid);
assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", ""),
Request::Invalid);
}
}

View File

@ -14,6 +14,7 @@
// 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::collections::BTreeSet;
use std::thread;
use std::sync::Arc;
use std::sync::mpsc;
@ -26,9 +27,9 @@ use super::acl_storage::AclStorage;
use super::key_storage::KeyStorage;
use super::key_server_set::KeyServerSet;
use key_server_cluster::{math, ClusterCore};
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair};
use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer, NodeKeyPair};
use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow,
ClusterConfiguration, MessageHash, EncryptedMessageSignature};
ClusterConfiguration, MessageHash, EncryptedMessageSignature, NodeId};
use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration};
/// Secret store key server implementation
@ -60,6 +61,14 @@ impl KeyServerImpl {
impl KeyServer for KeyServerImpl {}
impl AdminSessionsServer for KeyServerImpl {
fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet<NodeId>) -> Result<(), Error> {
let servers_set_change_session = self.data.lock().cluster
.new_servers_set_change_session(None, new_servers_set, old_set_signature, new_set_signature)?;
servers_set_change_session.wait().map_err(Into::into)
}
}
impl ServerKeyGenerator for KeyServerImpl {
fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result<Public, Error> {
// recover requestor' public key from signature
@ -153,7 +162,7 @@ impl KeyServerCore {
allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes,
acl_storage: acl_storage,
key_storage: key_storage,
admin_public: None,
admin_public: config.admin_public.clone(),
};
let (stop, stopped) = futures::oneshot();
@ -191,6 +200,7 @@ impl Drop for KeyServerCore {
#[cfg(test)]
pub mod tests {
use std::collections::BTreeSet;
use std::time;
use std::sync::Arc;
use std::net::SocketAddr;
@ -204,14 +214,20 @@ pub mod tests {
use key_server_cluster::math;
use bigint::hash::H256;
use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId,
EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature};
use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature, NodeId};
use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer};
use super::KeyServerImpl;
pub struct DummyKeyServer;
impl KeyServer for DummyKeyServer {}
impl AdminSessionsServer for DummyKeyServer {
fn change_servers_set(&self, _old_set_signature: RequestSignature, _new_set_signature: RequestSignature, _new_servers_set: BTreeSet<NodeId>) -> Result<(), Error> {
unimplemented!()
}
}
impl ServerKeyGenerator for DummyKeyServer {
fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result<Public, Error> {
unimplemented!()
@ -444,4 +460,9 @@ pub mod tests {
// check signature
assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true));
}
#[test]
fn servers_set_change_session_works_over_network() {
// TODO
}
}

View File

@ -412,7 +412,7 @@ impl SessionImpl {
key_share: key_share,
result_computer: Arc::new(LargestSupportResultComputer {}),
transport: ServersSetChangeKeyVersionNegotiationTransport {
id: key_id,
id: self.core.meta.id.clone(),
nonce: self.core.nonce,
cluster: self.core.cluster.clone(),
},
@ -687,7 +687,7 @@ impl SessionImpl {
/// Create share change session.
fn create_share_change_session(core: &SessionCore, key_id: SessionId, master_node_id: NodeId, session_plan: ShareChangeSessionPlan) -> Result<ShareChangeSession, Error> {
ShareChangeSession::new(ShareChangeSessionParams {
session_id: key_id.clone(),
session_id: core.meta.id.clone(),
nonce: core.nonce,
meta: ShareChangeSessionMeta {
id: key_id,
@ -726,7 +726,7 @@ impl SessionImpl {
key_share: key_share,
result_computer: Arc::new(LargestSupportResultComputer {}), // TODO: optimizations: could use modified Fast version
transport: ServersSetChangeKeyVersionNegotiationTransport {
id: key_id,
id: core.meta.id.clone(),
nonce: core.nonce,
cluster: core.cluster.clone(),
},
@ -855,11 +855,20 @@ impl SessionImpl {
/// Complete servers set change session.
fn complete_session(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> {
debug_assert_eq!(core.meta.self_node_id, core.meta.master_node_id);
// send completion notification
core.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(ServersSetChangeCompleted {
session: core.meta.id.clone().into(),
session_nonce: core.nonce,
})))?;
// if we are on the set of nodes that are being removed from the cluster, let's clear database
if !data.new_nodes_set.as_ref()
.expect("new_nodes_set is filled during initialization; session is completed after initialization; qed")
.contains(&core.meta.self_node_id) {
core.key_storage.clear().map_err(|e| Error::KeyStorage(e.into()))?;
}
data.state = SessionState::Finished;
data.result = Some(Ok(()));
core.completed.notify_all();

View File

@ -14,6 +14,7 @@
// 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::collections::BTreeSet;
use std::sync::Arc;
use parking_lot::{Mutex, Condvar};
use bigint::hash::H256;
@ -243,14 +244,19 @@ impl SessionImpl {
let mut data = self.data.lock();
let non_isolated_nodes = self.core.cluster.nodes();
data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone());
data.version = Some(version.clone());
data.is_shadow_decryption = Some(is_shadow_decryption);
data.consensus_session.initialize(key_version.id_numbers.keys()
let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys()
.filter(|n| non_isolated_nodes.contains(*n))
.cloned()
.chain(::std::iter::once(self.core.meta.self_node_id.clone()))
.collect())?;
.collect();
if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() {
consensus_nodes.remove(&delegation_master);
}
data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone());
data.version = Some(version.clone());
data.is_shadow_decryption = Some(is_shadow_decryption);
data.consensus_session.initialize(consensus_nodes)?;
if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished {
self.core.disseminate_jobs(&mut data.consensus_session, &version, is_shadow_decryption)?;
@ -502,7 +508,10 @@ impl ClusterSession for SessionImpl {
}
fn is_finished(&self) -> bool {
self.data.lock().result.is_some()
let data = self.data.lock();
data.consensus_session.state() == ConsensusSessionState::Failed
|| data.consensus_session.state() == ConsensusSessionState::Finished
|| data.result.is_some()
}
fn on_node_timeout(&self, node: &NodeId) {
@ -1146,7 +1155,7 @@ mod tests {
// now check that:
// 1) 4 of 5 sessions are in Finished state
assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5);
assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 4);
// 2) 1 session has decrypted key value
assert_eq!(sessions[1].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow {
decrypted_secret: SECRET_PLAIN.into(),

View File

@ -1322,7 +1322,7 @@ pub mod tests {
let mut core = Core::new().unwrap();
// prepare cluster objects for each node
let clusters = make_clusters(&core, 6022, num_nodes);
let clusters = make_clusters(&core, 6031, num_nodes);
run_clusters(&clusters);
// establish connections

View File

@ -250,14 +250,19 @@ impl SessionImpl {
let mut data = self.data.lock();
let non_isolated_nodes = self.core.cluster.nodes();
data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone());
data.version = Some(version.clone());
data.message_hash = Some(message_hash);
data.consensus_session.initialize(key_version.id_numbers.keys()
let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys()
.filter(|n| non_isolated_nodes.contains(*n))
.cloned()
.chain(::std::iter::once(self.core.meta.self_node_id.clone()))
.collect())?;
.collect();
if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() {
consensus_nodes.remove(&delegation_master);
}
data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone());
data.version = Some(version.clone());
data.message_hash = Some(message_hash);
data.consensus_session.initialize(consensus_nodes)?;
if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished {
let generation_session = GenerationSession::new(GenerationSessionParams {
@ -355,7 +360,6 @@ impl SessionImpl {
Ok(())
}
/// When consensus-related message is received.
pub fn on_consensus_message(&self, sender: &NodeId, message: &SigningConsensusMessage) -> Result<(), Error> {
debug_assert!(self.core.meta.id == *message.session);
@ -629,7 +633,10 @@ impl ClusterSession for SessionImpl {
}
fn is_finished(&self) -> bool {
self.data.lock().result.is_some()
let data = self.data.lock();
data.consensus_session.state() == ConsensusSessionState::Failed
|| data.consensus_session.state() == ConsensusSessionState::Finished
|| data.result.is_some()
}
fn on_node_timeout(&self, node: &NodeId) {

View File

@ -31,7 +31,7 @@ use bigint::hash::H256;
use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, KeyServerSet, NodeKeyPair};
use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper,
DecryptionSessionWrapper, SigningSessionWrapper, AdminSessionWrapper, KeyNegotiationSessionWrapper, SessionIdWithSubSession,
ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view};
ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view, AdminSessionCreationData};
use key_server_cluster::cluster_sessions_creator::{ClusterSessionCreator, IntoSessionId};
use key_server_cluster::message::{self, Message, ClusterMessage};
use key_server_cluster::generation_session::{Session as GenerationSession};
@ -519,7 +519,7 @@ impl ClusterCore {
let creation_data = SC::creation_data_from_message(&message)?;
let master = if is_initialization_message { sender.clone() } else { data.self_key_pair.public().clone() };
let cluster = create_cluster_view(data, requires_all_connections(&message))?;
sessions.insert(cluster, master, session_id.clone(), Some(message.session_nonce().ok_or(Error::InvalidMessage)?), message.is_exclusive_session_message(), creation_data)
sessions.insert(cluster, master, session_id, Some(message.session_nonce().ok_or(Error::InvalidMessage)?), message.is_exclusive_session_message(), creation_data)
},
}
}
@ -536,7 +536,7 @@ impl ClusterCore {
Ok(session) => session,
Err(error) => {
// this is new session => it is not yet in container
warn!(target: "secretstore_net", "{}: {} session initialization error '{}' when requested for new session from node {}",
warn!(target: "secretstore_net", "{}: {} session read error '{}' when requested for session from node {}",
data.self_key_pair.public(), S::type_name(), error, sender);
if message.is_initialization_message() {
let session_id = message.into_session_id().expect("session_id only fails for cluster messages; only session messages are passed to process_message; qed");
@ -969,7 +969,7 @@ impl ClusterClient for ClusterClientImpl {
};
let cluster = create_cluster_view(&self.data, true)?;
let session = self.data.sessions.admin_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, true, None)?;
let session = self.data.sessions.admin_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, true, Some(AdminSessionCreationData::ServersSetChange))?;
let initialization_result = session.as_servers_set_change().expect("servers set change session is created; qed")
.initialize(new_nodes_set, old_set_signature, new_set_signature);
@ -1294,15 +1294,26 @@ pub mod tests {
assert!(session.joint_public_and_secret().unwrap().is_ok());
// now remove share from node2
assert!((0..3).all(|i| clusters[i].data.sessions.generation_sessions.is_empty()));
clusters[2].data.config.key_storage.remove(&Default::default()).unwrap();
// and try to sign message with generated key
let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap();
let session0 = clusters[0].client().new_signing_session(Default::default(), signature, None, Default::default()).unwrap();
let session = clusters[0].data.sessions.signing_sessions.first().unwrap();
loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished());
loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished() && (0..3).all(|i|
clusters[i].data.sessions.signing_sessions.is_empty()));
session0.wait().unwrap();
// and try to sign message with generated key using node that has no key share
let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap();
let session2 = clusters[2].client().new_signing_session(Default::default(), signature, None, Default::default()).unwrap();
let session = clusters[2].data.sessions.signing_sessions.first().unwrap();
loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished() && (0..3).all(|i|
clusters[i].data.sessions.signing_sessions.is_empty()));
session2.wait().unwrap();
// now remove share from node1
clusters[1].data.config.key_storage.remove(&Default::default()).unwrap();

View File

@ -66,7 +66,7 @@ pub struct SessionIdWithSubSession {
/// Generic cluster session.
pub trait ClusterSession {
/// Session identifier type.
type Id: Ord + Clone;
type Id: ::std::fmt::Debug + Ord + Clone;
/// Session type name.
fn type_name() -> &'static str;
@ -662,6 +662,13 @@ impl AdminSessionWrapper {
cluster: cluster,
})
}
pub fn wait(&self) -> Result<(), Error> {
match *self.session {
AdminSession::ShareAdd(ref session) => session.wait(),
AdminSession::ServersSetChange(ref session) => session.wait(),
}
}
}
impl ShareAddSession for AdminSessionWrapper {

View File

@ -1163,7 +1163,7 @@ impl fmt::Display for ConsensusMessageWithServersSet {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConsensusMessageWithServersSet::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"),
ConsensusMessageWithServersSet::ConfirmConsensusInitialization(_) => write!(f, "ConfirmConsensusInitialization"),
ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed),
}
}
}
@ -1172,7 +1172,7 @@ impl fmt::Display for ConsensusMessageOfShareAdd {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ConsensusMessageOfShareAdd::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"),
ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(_) => write!(f, "ConfirmConsensusInitialization"),
ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed),
}
}
}

View File

@ -14,10 +14,11 @@
// 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::collections::BTreeSet;
use ethkey::{KeyPair, Signature, Error as EthKeyError};
use bigint::hash::H256;
use types::all::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, EncryptedDocumentKey,
EncryptedDocumentKeyShadow};
EncryptedDocumentKeyShadow, NodeId};
/// Node key pair.
pub trait NodeKeyPair: Send + Sync {
@ -81,7 +82,15 @@ pub trait MessageSigner: ServerKeyGenerator {
fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result<EncryptedMessageSignature, Error>;
}
/// Administrative sessions server.
pub trait AdminSessionsServer {
/// Change servers set so that nodes in new_servers_set became owners of shares for all keys.
/// And old nodes (i.e. cluste nodes except new_servers_set) have clear databases.
/// WARNING: newly generated keys will be distributed among all cluster nodes. So this session
/// must be followed with cluster nodes change (either via contract, or config files).
fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet<NodeId>) -> Result<(), Error>;
}
/// Key server.
pub trait KeyServer: DocumentKeyServer + MessageSigner + Send + Sync {
pub trait KeyServer: AdminSessionsServer + DocumentKeyServer + MessageSigner + Send + Sync {
}