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:
parent
605cd5cd9f
commit
e16f6fb9d9
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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>;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user