From e16f6fb9d9ee44e1a670d3888ca68d246613c848 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 16 Nov 2017 19:34:23 +0300 Subject: [PATCH] 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 --- Cargo.lock | 1 + rpc/Cargo.toml | 1 + rpc/src/lib.rs | 1 + rpc/src/v1/helpers/secretstore.rs | 17 +++ rpc/src/v1/impls/secretstore.rs | 12 +- rpc/src/v1/tests/mocked/secretstore.rs | 34 +++++- rpc/src/v1/traits/secretstore.rs | 6 + secret_store/src/http_listener.rs | 113 ++++++++++++++---- secret_store/src/key_server.rs | 31 ++++- .../servers_set_change_session.rs | 15 ++- .../client_sessions/decryption_session.rs | 23 ++-- .../client_sessions/generation_session.rs | 2 +- .../client_sessions/signing_session.rs | 21 ++-- .../src/key_server_cluster/cluster.rs | 21 +++- .../key_server_cluster/cluster_sessions.rs | 9 +- .../src/key_server_cluster/message.rs | 4 +- secret_store/src/traits.rs | 13 +- 17 files changed, 268 insertions(+), 56 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1a370af35..4f7b0d510 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 9fd178dd8..beb42b8d2 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -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" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 3d782d419..113ac19ff 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -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; diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index 39709e78e..4834611d8 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -14,12 +14,15 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +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, encrypted_document: Bytes) -> Result { 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) -> 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 { // key is a previously distributely generated Public if key.len() != 64 { diff --git a/rpc/src/v1/impls/secretstore.rs b/rpc/src/v1/impls/secretstore.rs index 6ddb4a7a0..c34da62d4 100644 --- a/rpc/src/v1/impls/secretstore.rs +++ b/rpc/src/v1/impls/secretstore.rs @@ -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) -> Result { + 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)) + } } diff --git a/rpc/src/v1/tests/mocked/secretstore.rs b/rpc/src/v1/tests/mocked/secretstore.rs index ed278ced8..b0b01cb65 100644 --- a/rpc/src/v1/tests/mocked/secretstore.rs +++ b/rpc/src/v1/tests/mocked/secretstore.rs @@ -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, @@ -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()); +} diff --git a/rpc/src/v1/traits/secretstore.rs b/rpc/src/v1/traits/secretstore.rs index e625cf331..f63bdce92 100644 --- a/rpc/src/v1/traits/secretstore.rs +++ b/rpc/src/v1/traits/secretstore.rs @@ -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) -> Result; + + /// 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) -> Result; } } diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 883389365..b6e48f1c5 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +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 { http_server: Option, @@ -60,6 +63,8 @@ enum Request { GetDocumentKeyShadow(ServerKeyId, RequestSignature), /// Sign message. SignMessage(ServerKeyId, RequestSignature, MessageHash), + /// Change servers set. + ChangeServersSet(RequestSignature, RequestSignature, BTreeSet), } /// Cloneable http handler @@ -96,6 +101,12 @@ impl KeyServerHttpListener where T: KeyServer + 'static { impl KeyServer for KeyServerHttpListener where T: KeyServer + 'static {} +impl AdminSessionsServer for KeyServerHttpListener where T: KeyServer + 'static { + fn change_servers_set(&self, old_set_signature: RequestSignature, new_set_signature: RequestSignature, new_servers_set: BTreeSet) -> Result<(), Error> { + self.handler.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set) + } +} + impl ServerKeyGenerator for KeyServerHttpListener where T: KeyServer + 'static { fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { self.handler.key_server.generate_key(key_id, signature, threshold) @@ -134,17 +145,24 @@ impl Drop for KeyServerHttpListener where T: KeyServer + 'static { } impl HttpHandler for KeyServerHttpHandler 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::() { 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 HttpHandler for KeyServerHttpHandler 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, 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 = 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); } } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index ac6c45489..0d23b99c8 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +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) -> 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 { // 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) -> Result<(), Error> { + unimplemented!() + } + } + impl ServerKeyGenerator for DummyKeyServer { fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { 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 + } } diff --git a/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs b/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs index feb23c9ae..223caef21 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs @@ -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::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(); diff --git a/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs b/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs index e907f6c13..087fc9245 100644 --- a/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/decryption_session.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +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(), diff --git a/secret_store/src/key_server_cluster/client_sessions/generation_session.rs b/secret_store/src/key_server_cluster/client_sessions/generation_session.rs index b6926751b..5c21d5786 100644 --- a/secret_store/src/key_server_cluster/client_sessions/generation_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/generation_session.rs @@ -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 diff --git a/secret_store/src/key_server_cluster/client_sessions/signing_session.rs b/secret_store/src/key_server_cluster/client_sessions/signing_session.rs index 6ca47580e..2f33160fa 100644 --- a/secret_store/src/key_server_cluster/client_sessions/signing_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/signing_session.rs @@ -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) { diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 23df97cb3..0a975c275 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -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(); diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index ce98bd878..254e3ecc6 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -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 { diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 43d27d1fd..357786725 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -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), } } } diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 7ee4c5cc1..0982f40e3 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -14,10 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +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; } +/// 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) -> Result<(), Error>; +} /// Key server. -pub trait KeyServer: DocumentKeyServer + MessageSigner + Send + Sync { +pub trait KeyServer: AdminSessionsServer + DocumentKeyServer + MessageSigner + Send + Sync { }