From bdea714985b4bb0667b4bb123102f2f251d82a7d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 10 May 2017 15:29:51 +0300 Subject: [PATCH 01/45] refactoring traits --- secret_store/src/acl_storage.rs | 14 ++-- secret_store/src/http_listener.rs | 55 ++++++++----- secret_store/src/key_server.rs | 81 ++++++++++++++----- .../key_server_cluster/decryption_session.rs | 18 ++--- secret_store/src/key_server_cluster/mod.rs | 6 +- secret_store/src/key_storage.rs | 30 +++---- secret_store/src/lib.rs | 2 +- secret_store/src/serialization.rs | 2 +- secret_store/src/traits.rs | 64 ++++++++++++--- secret_store/src/types/all.rs | 14 ++-- 10 files changed, 192 insertions(+), 94 deletions(-) diff --git a/secret_store/src/acl_storage.rs b/secret_store/src/acl_storage.rs index fea45c92077..816d100dc0b 100644 --- a/secret_store/src/acl_storage.rs +++ b/secret_store/src/acl_storage.rs @@ -20,14 +20,14 @@ use parking_lot::Mutex; use ethkey::public_to_address; use ethcore::client::{Client, BlockChainClient, BlockId}; use native_contracts::SecretStoreAclStorage; -use types::all::{Error, DocumentAddress, Public}; +use types::all::{Error, ServerKeyId, Public}; const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; /// ACL storage of Secret Store pub trait AclStorage: Send + Sync { /// Check if requestor with `public` key can access document with hash `document` - fn check(&self, public: &Public, document: &DocumentAddress) -> Result; + fn check(&self, public: &Public, document: &ServerKeyId) -> Result; } /// On-chain ACL storage implementation. @@ -48,7 +48,7 @@ impl OnChainAclStorage { } impl AclStorage for OnChainAclStorage { - fn check(&self, public: &Public, document: &DocumentAddress) -> Result { + fn check(&self, public: &Public, document: &ServerKeyId) -> Result { let mut contract = self.contract.lock(); if !contract.is_some() { *contract = self.client.registry_address(ACL_CHECKER_CONTRACT_REGISTRY_NAME.to_owned()) @@ -74,19 +74,19 @@ impl AclStorage for OnChainAclStorage { pub mod tests { use std::collections::{HashMap, HashSet}; use parking_lot::RwLock; - use types::all::{Error, DocumentAddress, Public}; + use types::all::{Error, ServerKeyId, Public}; use super::AclStorage; #[derive(Default, Debug)] /// Dummy ACL storage implementation pub struct DummyAclStorage { - prohibited: RwLock>>, + prohibited: RwLock>>, } impl DummyAclStorage { #[cfg(test)] /// Prohibit given requestor access to given document - pub fn prohibit(&self, public: Public, document: DocumentAddress) { + pub fn prohibit(&self, public: Public, document: ServerKeyId) { self.prohibited.write() .entry(public) .or_insert_with(Default::default) @@ -95,7 +95,7 @@ pub mod tests { } impl AclStorage for DummyAclStorage { - fn check(&self, public: &Public, document: &DocumentAddress) -> Result { + fn check(&self, public: &Public, document: &ServerKeyId) -> Result { Ok(self.prohibited.read() .get(public) .map(|docs| !docs.contains(document)) diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index bc51811de0c..2c63cb8b221 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -24,9 +24,10 @@ use hyper::server::{Server as HttpServer, Request as HttpRequest, Response as Ht use serde_json; use url::percent_encoding::percent_decode; -use traits::KeyServer; -use serialization::{SerializableDocumentEncryptedKeyShadow, SerializableBytes}; -use types::all::{Error, NodeAddress, RequestSignature, DocumentAddress, DocumentEncryptedKey, DocumentEncryptedKeyShadow}; +use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; +use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes}; +use types::all::{Error, Public, MessageData, NodeAddress, RequestSignature, ServerKeyId, + EncryptedDocumentKey, EncryptedDocumentKeyShadow}; /// Key server http-requests listener pub struct KeyServerHttpListener { @@ -40,11 +41,11 @@ enum Request { /// Invalid request Invalid, /// Generate encryption key. - GenerateDocumentKey(DocumentAddress, RequestSignature, usize), + GenerateDocumentKey(ServerKeyId, RequestSignature, usize), /// Request encryption key of given document for given requestor. - GetDocumentKey(DocumentAddress, RequestSignature), + GetDocumentKey(ServerKeyId, RequestSignature), /// Request shadow of encryption key of given document for given requestor. - GetDocumentKeyShadow(DocumentAddress, RequestSignature), + GetDocumentKeyShadow(ServerKeyId, RequestSignature), } /// Cloneable http handler @@ -78,17 +79,35 @@ impl KeyServerHttpListener where T: KeyServer + 'static { } } -impl KeyServer for KeyServerHttpListener where T: KeyServer + 'static { - fn generate_document_key(&self, signature: &RequestSignature, document: &DocumentAddress, threshold: usize) -> Result { - self.handler.key_server.generate_document_key(signature, document, threshold) +impl KeyServer for KeyServerHttpListener where T: KeyServer + 'static {} + +impl ServerKeyGenerator for KeyServerHttpListener where T: KeyServer + 'static { + fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { + unimplemented!() } +} - fn document_key(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result { - self.handler.key_server.document_key(signature, document) +impl DocumentKeyServer for KeyServerHttpListener where T: KeyServer + 'static { + fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _document_key: EncryptedDocumentKey) -> Result<(), Error> { + unimplemented!() } - fn document_key_shadow(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result { - self.handler.key_server.document_key_shadow(signature, document) + fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { + self.handler.key_server.generate_document_key(key_id, signature, threshold) + } + + fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { + self.handler.key_server.restore_document_key(key_id, signature) + } + + fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { + self.handler.key_server.restore_document_key_shadow(key_id, signature) + } +} + +impl MessageSigner for KeyServerHttpListener where T: KeyServer + 'static { + fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageData) -> Result { + unimplemented!() } } @@ -112,27 +131,27 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { match &req_uri { &RequestUri::AbsolutePath(ref path) => match parse_request(&req_method, &path) { Request::GenerateDocumentKey(document, signature, threshold) => { - return_document_key(req, res, self.handler.key_server.generate_document_key(&signature, &document, threshold) + return_document_key(req, res, self.handler.key_server.generate_document_key(&document, &signature, threshold) .map_err(|err| { warn!(target: "secretstore", "GenerateDocumentKey request {} has failed with: {}", req_uri, err); err })); }, Request::GetDocumentKey(document, signature) => { - return_document_key(req, res, self.handler.key_server.document_key(&signature, &document) + return_document_key(req, res, self.handler.key_server.restore_document_key(&document, &signature) .map_err(|err| { warn!(target: "secretstore", "GetDocumentKey request {} has failed with: {}", req_uri, err); err })); }, Request::GetDocumentKeyShadow(document, signature) => { - match self.handler.key_server.document_key_shadow(&signature, &document) + match self.handler.key_server.restore_document_key_shadow(&document, &signature) .map_err(|err| { warn!(target: "secretstore", "GetDocumentKeyShadow request {} has failed with: {}", req_uri, err); err }) { Ok(document_key_shadow) => { - let document_key_shadow = SerializableDocumentEncryptedKeyShadow { + let document_key_shadow = SerializableEncryptedDocumentKeyShadow { decrypted_secret: document_key_shadow.decrypted_secret.into(), common_point: document_key_shadow.common_point.expect("always filled when requesting document_key_shadow; qed").into(), decrypt_shadows: document_key_shadow.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect(), @@ -166,7 +185,7 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { } } -fn return_document_key(req: HttpRequest, mut res: HttpResponse, document_key: Result) { +fn return_document_key(req: HttpRequest, mut res: HttpResponse, document_key: Result) { let document_key = document_key. and_then(|k| serde_json::to_vec(&SerializableBytes(k)).map_err(|e| Error::Serde(e.to_string()))); match document_key { diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index f960c5cc568..036d0cecd11 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -25,8 +25,9 @@ use ethkey; use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; use key_server_cluster::ClusterCore; -use traits::KeyServer; -use types::all::{Error, RequestSignature, DocumentAddress, DocumentEncryptedKey, DocumentEncryptedKeyShadow, ClusterConfiguration}; +use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; +use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, + ClusterConfiguration, MessageData}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration}; /// Secret store key server implementation @@ -56,14 +57,26 @@ impl KeyServerImpl { } } -impl KeyServer for KeyServerImpl { - fn generate_document_key(&self, signature: &RequestSignature, document: &DocumentAddress, threshold: usize) -> Result { +impl KeyServer for KeyServerImpl {} + +impl ServerKeyGenerator for KeyServerImpl { + fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { + unimplemented!() + } +} + +impl DocumentKeyServer for KeyServerImpl { + fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _document_key: EncryptedDocumentKey) -> Result<(), Error> { + unimplemented!() + } + + fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, document) + let public = ethkey::recover(signature, key_id) .map_err(|_| Error::BadSignature)?; // generate document key - let encryption_session = self.data.lock().cluster.new_encryption_session(document.clone(), threshold)?; + let encryption_session = self.data.lock().cluster.new_encryption_session(key_id.clone(), threshold)?; let document_key = encryption_session.wait(None)?; // encrypt document key with requestor public key @@ -72,14 +85,14 @@ impl KeyServer for KeyServerImpl { Ok(document_key) } - fn document_key(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result { + fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { // recover requestor' public key from signature - let public = ethkey::recover(signature, document) + let public = ethkey::recover(signature, key_id) .map_err(|_| Error::BadSignature)?; // decrypt document key - let decryption_session = self.data.lock().cluster.new_decryption_session(document.clone(), signature.clone(), false)?; + let decryption_session = self.data.lock().cluster.new_decryption_session(key_id.clone(), signature.clone(), false)?; let document_key = decryption_session.wait()?.decrypted_secret; // encrypt document key with requestor public key @@ -88,12 +101,18 @@ impl KeyServer for KeyServerImpl { Ok(document_key) } - fn document_key_shadow(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result { - let decryption_session = self.data.lock().cluster.new_decryption_session(document.clone(), signature.clone(), true)?; + fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result { + let decryption_session = self.data.lock().cluster.new_decryption_session(key_id.clone(), signature.clone(), true)?; decryption_session.wait().map_err(Into::into) } } +impl MessageSigner for KeyServerImpl { + fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageData) -> Result { + unimplemented!() + } +} + impl KeyServerCore { pub fn new(config: &ClusterConfiguration, acl_storage: Arc, key_storage: Arc) -> Result { let config = NetClusterConfiguration { @@ -149,21 +168,41 @@ pub mod tests { use ethkey::{self, Random, Generator}; use acl_storage::tests::DummyAclStorage; use key_storage::tests::DummyKeyStorage; - use types::all::{Error, ClusterConfiguration, NodeAddress, RequestSignature, DocumentAddress, DocumentEncryptedKey, DocumentEncryptedKeyShadow}; - use super::{KeyServer, KeyServerImpl}; + use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, + EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageData}; + use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; + use super::KeyServerImpl; pub struct DummyKeyServer; - impl KeyServer for DummyKeyServer { - fn generate_document_key(&self, _signature: &RequestSignature, _document: &DocumentAddress, _threshold: usize) -> Result { + impl KeyServer for DummyKeyServer {} + + impl ServerKeyGenerator for DummyKeyServer { + fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { + unimplemented!() + } + } + + impl DocumentKeyServer for DummyKeyServer { + fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _document_key: EncryptedDocumentKey) -> Result<(), Error> { + unimplemented!() + } + + fn generate_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { unimplemented!() } - fn document_key(&self, _signature: &RequestSignature, _document: &DocumentAddress) -> Result { + fn restore_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature) -> Result { unimplemented!() } - fn document_key_shadow(&self, _signature: &RequestSignature, _document: &DocumentAddress) -> Result { + fn restore_document_key_shadow(&self, _key_id: &ServerKeyId, _signature: &RequestSignature) -> Result { + unimplemented!() + } + } + + impl MessageSigner for DummyKeyServer { + fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageData) -> Result { unimplemented!() } } @@ -228,12 +267,12 @@ pub mod tests { let document = Random.generate().unwrap().secret().clone(); let secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&secret, &document).unwrap(); - let generated_key = key_servers[0].generate_document_key(&signature, &document, threshold).unwrap(); + let generated_key = key_servers[0].generate_document_key(&document, &signature, threshold).unwrap(); let generated_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &generated_key).unwrap(); // now let's try to retrieve key back for key_server in key_servers.iter() { - let retrieved_key = key_server.document_key(&signature, &document).unwrap(); + let retrieved_key = key_server.restore_document_key(&document, &signature).unwrap(); let retrieved_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); } @@ -250,12 +289,12 @@ pub mod tests { let document = Random.generate().unwrap().secret().clone(); let secret = Random.generate().unwrap().secret().clone(); let signature = ethkey::sign(&secret, &document).unwrap(); - let generated_key = key_servers[0].generate_document_key(&signature, &document, *threshold).unwrap(); + let generated_key = key_servers[0].generate_document_key(&document, &signature, *threshold).unwrap(); let generated_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &generated_key).unwrap(); // now let's try to retrieve key back for key_server in key_servers.iter() { - let retrieved_key = key_server.document_key(&signature, &document).unwrap(); + let retrieved_key = key_server.restore_document_key(&document, &signature).unwrap(); let retrieved_key = ethcrypto::ecies::decrypt(&secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); assert_eq!(retrieved_key, generated_key); } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 8f5ecff7de4..55c666956e2 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -21,7 +21,7 @@ use parking_lot::{Mutex, Condvar}; use ethcrypto::ecies::encrypt; use ethcrypto::DEFAULT_MAC; use ethkey::{self, Secret, Public, Signature}; -use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, DocumentEncryptedKeyShadow}; +use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, EncryptedDocumentKeyShadow}; use key_server_cluster::cluster::Cluster; use key_server_cluster::math; use key_server_cluster::message::{Message, DecryptionMessage, InitializeDecryptionSession, ConfirmDecryptionInitialization, @@ -30,7 +30,7 @@ use key_server_cluster::message::{Message, DecryptionMessage, InitializeDecrypti /// Decryption session API. pub trait Session: Send + Sync + 'static { /// Wait until session is completed. Returns distributely restored secret key. - fn wait(&self) -> Result; + fn wait(&self) -> Result; } /// Distributed decryption session. @@ -124,7 +124,7 @@ struct SessionData { /// === Values, filled during final decryption === /// Decrypted secret - decrypted_secret: Option>, + decrypted_secret: Option>, } #[derive(Debug, Clone, PartialEq)] @@ -190,7 +190,7 @@ impl SessionImpl { #[cfg(test)] /// Get decrypted secret - pub fn decrypted_secret(&self) -> Option> { + pub fn decrypted_secret(&self) -> Option> { self.data.lock().decrypted_secret.clone() } @@ -585,7 +585,7 @@ impl SessionImpl { } else { (None, None) }; - data.decrypted_secret = Some(Ok(DocumentEncryptedKeyShadow { + data.decrypted_secret = Some(Ok(EncryptedDocumentKeyShadow { decrypted_secret: decrypted_secret, common_point: common_point, decrypt_shadows: decrypt_shadows, @@ -599,7 +599,7 @@ impl SessionImpl { } impl Session for SessionImpl { - fn wait(&self) -> Result { + fn wait(&self) -> Result { let mut data = self.data.lock(); if !data.decrypted_secret.is_some() { self.completed.wait(&mut data); @@ -700,7 +700,7 @@ mod tests { use std::collections::BTreeMap; use super::super::super::acl_storage::tests::DummyAclStorage; use ethkey::{self, Random, Generator, Public, Secret}; - use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, DocumentEncryptedKeyShadow}; + use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow}; use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::decryption_session::{SessionImpl, SessionParams, SessionState}; use key_server_cluster::message::{self, Message, DecryptionMessage}; @@ -1055,7 +1055,7 @@ mod tests { // 2) 1 session has decrypted key value assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), DocumentEncryptedKeyShadow { + assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { decrypted_secret: SECRET_PLAIN.into(), common_point: None, decrypt_shadows: None, @@ -1141,7 +1141,7 @@ mod tests { assert_eq!(sessions.iter().filter(|s| s.state() == SessionState::Finished).count(), 5); // 2) 1 session has decrypted key value assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), DocumentEncryptedKeyShadow { + assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { decrypted_secret: SECRET_PLAIN.into(), common_point: None, decrypt_shadows: None, diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index bdaa868ee4f..5aa1b0fc0c6 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -18,9 +18,9 @@ use std::fmt; use std::io::Error as IoError; use ethkey; use ethcrypto; -use super::types::all::DocumentAddress; +use super::types::all::ServerKeyId; -pub use super::types::all::{NodeId, DocumentEncryptedKeyShadow}; +pub use super::types::all::{NodeId, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare}; pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic}; @@ -33,7 +33,7 @@ pub use super::key_storage::tests::DummyKeyStorage; #[cfg(test)] pub use super::acl_storage::tests::DummyAclStorage; -pub type SessionId = DocumentAddress; +pub type SessionId = ServerKeyId; #[derive(Clone, Debug, PartialEq)] /// Errors which can occur during encryption/decryption session diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index b324dd90a33..f1d95bdd9e0 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -19,7 +19,7 @@ use std::collections::BTreeMap; use serde_json; use ethkey::{Secret, Public}; use util::Database; -use types::all::{Error, ServiceConfiguration, DocumentAddress, NodeId}; +use types::all::{Error, ServiceConfiguration, ServerKeyId, NodeId}; use serialization::{SerializablePublic, SerializableSecret}; #[derive(Debug, Clone, PartialEq)] @@ -40,11 +40,11 @@ pub struct DocumentKeyShare { /// Document encryption keys storage pub trait KeyStorage: Send + Sync { /// Insert document encryption key - fn insert(&self, document: DocumentAddress, key: DocumentKeyShare) -> Result<(), Error>; + fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; /// Get document encryption key - fn get(&self, document: &DocumentAddress) -> Result; + fn get(&self, document: &ServerKeyId) -> Result; /// Check if storage contains document encryption key - fn contains(&self, document: &DocumentAddress) -> bool; + fn contains(&self, document: &ServerKeyId) -> bool; } /// Persistent document encryption keys storage @@ -81,7 +81,7 @@ impl PersistentKeyStorage { } impl KeyStorage for PersistentKeyStorage { - fn insert(&self, document: DocumentAddress, key: DocumentKeyShare) -> Result<(), Error> { + fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { let key: SerializableDocumentKeyShare = key.into(); let key = serde_json::to_vec(&key).map_err(|e| Error::Database(e.to_string()))?; let mut batch = self.db.transaction(); @@ -89,7 +89,7 @@ impl KeyStorage for PersistentKeyStorage { self.db.write(batch).map_err(Error::Database) } - fn get(&self, document: &DocumentAddress) -> Result { + fn get(&self, document: &ServerKeyId) -> Result { self.db.get(None, document) .map_err(Error::Database)? .ok_or(Error::DocumentNotFound) @@ -98,7 +98,7 @@ impl KeyStorage for PersistentKeyStorage { .map(Into::into) } - fn contains(&self, document: &DocumentAddress) -> bool { + fn contains(&self, document: &ServerKeyId) -> bool { self.db.get(None, document) .map(|k| k.is_some()) .unwrap_or(false) @@ -135,26 +135,26 @@ pub mod tests { use parking_lot::RwLock; use devtools::RandomTempPath; use ethkey::{Random, Generator}; - use super::super::types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, DocumentAddress}; + use super::super::types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare}; #[derive(Default)] /// In-memory document encryption keys storage pub struct DummyKeyStorage { - keys: RwLock>, + keys: RwLock>, } impl KeyStorage for DummyKeyStorage { - fn insert(&self, document: DocumentAddress, key: DocumentKeyShare) -> Result<(), Error> { + fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { self.keys.write().insert(document, key); Ok(()) } - fn get(&self, document: &DocumentAddress) -> Result { + fn get(&self, document: &ServerKeyId) -> Result { self.keys.read().get(document).cloned().ok_or(Error::DocumentNotFound) } - fn contains(&self, document: &DocumentAddress) -> bool { + fn contains(&self, document: &ServerKeyId) -> bool { self.keys.read().contains_key(document) } } @@ -180,7 +180,7 @@ pub mod tests { }, }; - let key1 = DocumentAddress::from(1); + let key1 = ServerKeyId::from(1); let value1 = DocumentKeyShare { threshold: 100, id_numbers: vec![ @@ -190,7 +190,7 @@ pub mod tests { common_point: Random.generate().unwrap().public().clone(), encrypted_point: Random.generate().unwrap().public().clone(), }; - let key2 = DocumentAddress::from(2); + let key2 = ServerKeyId::from(2); let value2 = DocumentKeyShare { threshold: 200, id_numbers: vec![ @@ -200,7 +200,7 @@ pub mod tests { common_point: Random.generate().unwrap().public().clone(), encrypted_point: Random.generate().unwrap().public().clone(), }; - let key3 = DocumentAddress::from(3); + let key3 = ServerKeyId::from(3); let key_storage = PersistentKeyStorage::new(&config).unwrap(); key_storage.insert(key1.clone(), value1.clone()).unwrap(); diff --git a/secret_store/src/lib.rs b/secret_store/src/lib.rs index 209a32cc2a2..fb044028377 100644 --- a/secret_store/src/lib.rs +++ b/secret_store/src/lib.rs @@ -60,7 +60,7 @@ mod serialization; use std::sync::Arc; use ethcore::client::Client; -pub use types::all::{DocumentAddress, DocumentKey, DocumentEncryptedKey, RequestSignature, Public, +pub use types::all::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; pub use traits::{KeyServer}; diff --git a/secret_store/src/serialization.rs b/secret_store/src/serialization.rs index c88e9edf7a9..4c047be954e 100644 --- a/secret_store/src/serialization.rs +++ b/secret_store/src/serialization.rs @@ -25,7 +25,7 @@ use util::{H256, Bytes}; #[derive(Clone, Debug, Serialize, Deserialize)] /// Serializable shadow decryption result. -pub struct SerializableDocumentEncryptedKeyShadow { +pub struct SerializableEncryptedDocumentKeyShadow { /// Decrypted secret point. It is partially decrypted if shadow decrpytion was requested. pub decrypted_secret: SerializablePublic, /// Shared common point. diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 86d41be878f..ee70b107462 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -14,21 +14,61 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use types::all::{Error, RequestSignature, DocumentAddress, DocumentEncryptedKey, DocumentEncryptedKeyShadow}; +use types::all::{Error, Public, ServerKeyId, MessageData, RequestSignature, EncryptedDocumentKey, + EncryptedDocumentKeyShadow}; -#[ipc(client_ident="RemoteKeyServer")] -/// Secret store key server -pub trait KeyServer: Send + Sync { - /// Generate encryption key for given document. - fn generate_document_key(&self, signature: &RequestSignature, document: &DocumentAddress, threshold: usize) -> Result; - /// Request encryption key of given document for given requestor - fn document_key(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result; - /// Request encryption key of given document for given requestor. - /// This method does not reveal document_key to any KeyServer, but it requires additional actions on client. - /// To calculate decrypted key on client: +/// Server key (SK) generator. +pub trait ServerKeyGenerator { + /// Generate new SK. + /// `key_id` is the caller-provided identifier of generated SK. + /// `signature` is `key_id`, signed with caller public key. + /// `threshold + 1` is the minimal number of nodes, required to restore private key. + /// Result is a public portion of SK. + fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result; +} + +/// Document key (DK) server. +pub trait DocumentKeyServer: ServerKeyGenerator { + /// Store externally generated DK. + /// `key_id` is identifier of previously generated SK. + /// `signature` is key_id, signed with caller public key. Caller must be the same as in the `generate_key` call. + /// `document_key` is an externally-generated DK, encrypted with SK with `key_id` identifier. + fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, document_key: EncryptedDocumentKey) -> Result<(), Error>; + /// Generate and store both SK and DK. This is a shortcut for consequent calls of `generate_key` and `store_document_key`. + /// The only difference is that DK is generated by DocumentKeyServer (which might be considered unsafe). + /// `key_id` is the caller-provided identifier of generated SK. + /// `signature` is `key_id`, signed with caller public key. + /// `threshold + 1` is the minimal number of nodes, required to restore private key. + /// Result is a DK, encrypted with caller public key. + fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result; + /// Restore previously stored DK. + /// DK is decrypted on the key server (which might be considered unsafe), and then encrypted with caller public key. + /// `key_id` is identifier of previously generated SK. + /// `signature` is key_id, signed with caller public key. Caller must be on ACL for this function to succeed. + /// Result is a DK, encrypted with caller public key. + fn restore_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result; + /// Restore previously stored DK. + /// To decrypt DK on client: /// 1) use requestor secret key to decrypt secret coefficients from result.decrypt_shadows /// 2) calculate decrypt_shadows_sum = sum of all secrets from (1) /// 3) calculate decrypt_shadow_point: decrypt_shadows_sum * result.common_point /// 4) calculate decrypted_secret: result.decrypted_secret + decrypt_shadow_point - fn document_key_shadow(&self, signature: &RequestSignature, document: &DocumentAddress) -> Result; + /// Result is a DK shadow. + fn restore_document_key_shadow(&self, key_id: &ServerKeyId, signature: &RequestSignature) -> Result; +} + +/// Message signer. +pub trait MessageSigner: ServerKeyGenerator { + /// Sign message with previously generated SK. + /// `key_id` is the caller-provided identifier of generated SK. + /// `signature` is `key_id`, signed with caller public key. + /// `message` is the message to be signed. + /// Result is a signed message. + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageData) -> Result; +} + + +#[ipc(client_ident="RemoteKeyServer")] +/// Key server. +pub trait KeyServer: DocumentKeyServer + MessageSigner + Send + Sync { } diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index 905841ea761..f0c6456c9fe 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -24,12 +24,12 @@ use key_server_cluster; /// Node id. pub type NodeId = ethkey::Public; -/// Document address type. -pub type DocumentAddress = util::H256; -/// Document key type. -pub type DocumentKey = util::Bytes; -/// Encrypted key type. -pub type DocumentEncryptedKey = util::Bytes; +/// Server key id. When key is used to encrypt document, it could be document contents hash. +pub type ServerKeyId = util::H256; +/// Encrypted document key type. +pub type EncryptedDocumentKey = util::Bytes; +/// Message data. +pub type MessageData = util::Bytes; /// Request signature type. pub type RequestSignature = ethkey::Signature; /// Public key type. @@ -95,7 +95,7 @@ pub struct ClusterConfiguration { #[derive(Clone, Debug, PartialEq)] #[binary] /// Shadow decryption result. -pub struct DocumentEncryptedKeyShadow { +pub struct EncryptedDocumentKeyShadow { /// Decrypted secret point. It is partially decrypted if shadow decrpytion was requested. pub decrypted_secret: ethkey::Public, /// Shared common point. From de3d882e8344311a811aed330f6380eccd28cefb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 11 May 2017 13:19:03 +0300 Subject: [PATCH 02/45] separate generation session --- secret_store/src/http_listener.rs | 8 +- secret_store/src/key_server.rs | 34 +- .../src/key_server_cluster/cluster.rs | 304 +++- .../key_server_cluster/decryption_session.rs | 41 +- .../key_server_cluster/encryption_session.rs | 1125 ++------------- .../key_server_cluster/generation_session.rs | 1221 +++++++++++++++++ .../src/key_server_cluster/io/message.rs | 66 +- .../src/key_server_cluster/message.rs | 97 +- secret_store/src/key_server_cluster/mod.rs | 10 +- secret_store/src/key_storage.rs | 61 +- secret_store/src/traits.rs | 6 +- 11 files changed, 1803 insertions(+), 1170 deletions(-) create mode 100644 secret_store/src/key_server_cluster/generation_session.rs diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 2c63cb8b221..dc96ebe2dc7 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -82,14 +82,14 @@ impl KeyServerHttpListener where T: KeyServer + 'static { impl KeyServer for KeyServerHttpListener where T: KeyServer + 'static {} impl ServerKeyGenerator for KeyServerHttpListener where T: KeyServer + 'static { - fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { - unimplemented!() + fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { + self.handler.key_server.generate_key(key_id, signature, threshold) } } impl DocumentKeyServer for KeyServerHttpListener where T: KeyServer + 'static { - fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _document_key: EncryptedDocumentKey) -> Result<(), Error> { - unimplemented!() + fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> { + self.handler.key_server.store_document_key(key_id, signature, common_point, encrypted_document_key) } fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 036d0cecd11..00421e1864d 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -24,7 +24,7 @@ use ethcrypto; use ethkey; use super::acl_storage::AclStorage; use super::key_storage::KeyStorage; -use key_server_cluster::ClusterCore; +use key_server_cluster::{math, ClusterCore}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, ClusterConfiguration, MessageData}; @@ -60,24 +60,38 @@ impl KeyServerImpl { impl KeyServer for KeyServerImpl {} impl ServerKeyGenerator for KeyServerImpl { - fn generate_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _threshold: usize) -> Result { - unimplemented!() + fn generate_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { + // recover requestor' public key from signature + let public = ethkey::recover(signature, key_id) + .map_err(|_| Error::BadSignature)?; + + // generate server key + let generation_session = self.data.lock().cluster.new_generation_session(key_id.clone(), public, threshold)?; + generation_session.wait(None).map_err(Into::into) } } impl DocumentKeyServer for KeyServerImpl { - fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _document_key: EncryptedDocumentKey) -> Result<(), Error> { - unimplemented!() + fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error> { + // store encrypted key + let encryption_session = self.data.lock().cluster.new_encryption_session(key_id.clone(), signature.clone(), common_point, encrypted_document_key)?; + encryption_session.wait(None).map_err(Into::into) } - + fn generate_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, threshold: usize) -> Result { // recover requestor' public key from signature let public = ethkey::recover(signature, key_id) .map_err(|_| Error::BadSignature)?; - // generate document key - let encryption_session = self.data.lock().cluster.new_encryption_session(key_id.clone(), threshold)?; - let document_key = encryption_session.wait(None)?; + // generate server key + let server_key = self.generate_key(key_id, signature, threshold)?; + + // generate random document key + let document_key = math::generate_random_point()?; + let encrypted_document_key = math::encrypt_secret(&document_key, &server_key)?; + + // store document key in the storage + self.store_document_key(key_id, signature, encrypted_document_key.common_point, encrypted_document_key.encrypted_point)?; // encrypt document key with requestor public key let document_key = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &document_key) @@ -184,7 +198,7 @@ pub mod tests { } impl DocumentKeyServer for DummyKeyServer { - fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _document_key: EncryptedDocumentKey) -> Result<(), Error> { + fn store_document_key(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _common_point: Public, _encrypted_document_key: Public) -> Result<(), Error> { unimplemented!() } diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index bd857bda81c..4ee12d47ab9 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -27,9 +27,11 @@ use parking_lot::{RwLock, Mutex}; use tokio_io::IoFuture; use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; -use ethkey::{Secret, KeyPair, Signature, Random, Generator}; +use ethkey::{Public, Secret, KeyPair, Signature, Random, Generator}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; -use key_server_cluster::message::{self, Message, ClusterMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionState as GenerationSessionState, + SessionParams as GenerationSessionParams, Session as GenerationSession}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, SessionState as DecryptionSessionState, SessionParams as DecryptionSessionParams, Session as DecryptionSession, DecryptionSessionId}; use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionState as EncryptionSessionState, @@ -70,17 +72,19 @@ type BoxedEmptyFuture = BoxFuture<(), ()>; pub trait ClusterClient: Send + Sync { /// Get cluster state. fn cluster_state(&self) -> ClusterState; + /// Start new generation session. + fn new_generation_session(&self, session_id: SessionId, author: Public, threshold: usize) -> Result, Error>; /// Start new encryption session. - fn new_encryption_session(&self, session_id: SessionId, threshold: usize) -> Result, Error>; + fn new_encryption_session(&self, session_id: SessionId, requestor_signature: Signature, common_point: Public, encrypted_point: Public) -> Result, Error>; /// Start new decryption session. fn new_decryption_session(&self, session_id: SessionId, requestor_signature: Signature, is_shadow_decryption: bool) -> Result, Error>; #[cfg(test)] - /// Ask node to make 'faulty' encryption sessions. - fn make_faulty_encryption_sessions(&self); + /// Ask node to make 'faulty' generation sessions. + fn make_faulty_generation_sessions(&self); #[cfg(test)] - /// Get active encryption session with given id. - fn encryption_session(&self, session_id: &SessionId) -> Option>; + /// Get active generation session with given id. + fn generation_session(&self, session_id: &SessionId) -> Option>; #[cfg(test)] /// Try connect to disconnected nodes. fn connect(&self); @@ -176,12 +180,28 @@ pub struct ClusterSessions { pub key_storage: Arc, /// Reference to ACL storage pub acl_storage: Arc, + /// Active generation sessions. + pub generation_sessions: RwLock>, /// Active encryption sessions. pub encryption_sessions: RwLock>, /// Active decryption sessions. pub decryption_sessions: RwLock>, - /// Make faulty encryption sessions. - pub make_faulty_encryption_sessions: AtomicBool, + /// Make faulty generation sessions. + pub make_faulty_generation_sessions: AtomicBool, +} + +/// Generation session and its message queue. +pub struct QueuedGenerationSession { + /// Session master. + pub master: NodeId, + /// Cluster view. + pub cluster_view: Arc, + /// Last received message time. + pub last_message_time: time::Instant, + /// Generation session. + pub session: Arc, + /// Messages queue. + pub queue: VecDeque<(NodeId, GenerationMessage)>, } /// Encryption session and its message queue. @@ -439,26 +459,27 @@ impl ClusterCore { connection.set_last_message_time(time::Instant::now()); trace!(target: "secretstore_net", "{}: received message {} from {}", data.self_key_pair.public(), message, connection.node_id()); match message { + Message::Generation(message) => ClusterCore::process_generation_message(data, connection, message), Message::Encryption(message) => ClusterCore::process_encryption_message(data, connection, message), Message::Decryption(message) => ClusterCore::process_decryption_message(data, connection, message), Message::Cluster(message) => ClusterCore::process_cluster_message(data, connection, message), } } - /// Process single encryption message from the connection. - fn process_encryption_message(data: Arc, connection: Arc, mut message: EncryptionMessage) { + /// Process single generation message from the connection. + fn process_generation_message(data: Arc, connection: Arc, mut message: GenerationMessage) { let session_id = message.session_id().clone(); let mut sender = connection.node_id().clone(); let session = match message { - EncryptionMessage::InitializeSession(_) => { + GenerationMessage::InitializeSession(_) => { let mut connected_nodes = data.connections.connected_nodes(); connected_nodes.insert(data.self_key_pair.public().clone()); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); - data.sessions.new_encryption_session(sender.clone(), session_id.clone(), cluster) + data.sessions.new_generation_session(sender.clone(), session_id.clone(), cluster) }, _ => { - data.sessions.encryption_session(&session_id) + data.sessions.generation_session(&session_id) .ok_or(Error::InvalidSessionId) }, }; @@ -466,20 +487,89 @@ impl ClusterCore { let mut is_queued_message = false; loop { match session.clone().and_then(|session| match message { - EncryptionMessage::InitializeSession(ref message) => + GenerationMessage::InitializeSession(ref message) => session.on_initialize_session(sender.clone(), message), - EncryptionMessage::ConfirmInitialization(ref message) => + GenerationMessage::ConfirmInitialization(ref message) => session.on_confirm_initialization(sender.clone(), message), - EncryptionMessage::CompleteInitialization(ref message) => + GenerationMessage::CompleteInitialization(ref message) => session.on_complete_initialization(sender.clone(), message), - EncryptionMessage::KeysDissemination(ref message) => + GenerationMessage::KeysDissemination(ref message) => session.on_keys_dissemination(sender.clone(), message), - EncryptionMessage::PublicKeyShare(ref message) => + GenerationMessage::PublicKeyShare(ref message) => session.on_public_key_share(sender.clone(), message), - EncryptionMessage::SessionError(ref message) => + GenerationMessage::SessionError(ref message) => session.on_session_error(sender.clone(), message), - EncryptionMessage::SessionCompleted(ref message) => + GenerationMessage::SessionCompleted(ref message) => session.on_session_completed(sender.clone(), message), + }) { + Ok(_) => { + // if session is completed => stop + let session = session.clone().expect("session.method() call finished with success; session exists; qed"); + let session_state = session.state(); + if session_state == GenerationSessionState::Finished { + info!(target: "secretstore_net", "{}: generation session completed", data.self_key_pair.public()); + } + if session_state == GenerationSessionState::Finished || session_state == GenerationSessionState::Failed { + data.sessions.remove_generation_session(&session_id); + break; + } + + // try to dequeue message + match data.sessions.dequeue_generation_message(&session_id) { + Some((msg_sender, msg)) => { + is_queued_message = true; + sender = msg_sender; + message = msg; + }, + None => break, + } + }, + Err(Error::TooEarlyForRequest) => { + data.sessions.enqueue_generation_message(&session_id, sender, message, is_queued_message); + break; + }, + Err(err) => { + warn!(target: "secretstore_net", "{}: generation session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); + data.sessions.respond_with_generation_error(&session_id, message::SessionError { + session: session_id.clone().into(), + error: format!("{:?}", err), + }); + if err != Error::InvalidSessionId { + data.sessions.remove_generation_session(&session_id); + } + break; + }, + } + } + } + + /// Process single encryption message from the connection. + fn process_encryption_message(data: Arc, connection: Arc, mut message: EncryptionMessage) { + let session_id = message.session_id().clone(); + let mut sender = connection.node_id().clone(); + let session = match message { + EncryptionMessage::InitializeEncryptionSession(_) => { + let mut connected_nodes = data.connections.connected_nodes(); + connected_nodes.insert(data.self_key_pair.public().clone()); + + let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); + data.sessions.new_encryption_session(sender.clone(), session_id.clone(), cluster) + }, + _ => { + data.sessions.encryption_session(&session_id) + .ok_or(Error::InvalidSessionId) + }, + }; + + let mut is_queued_message = false; + loop { + match session.clone().and_then(|session| match message { + EncryptionMessage::InitializeEncryptionSession(ref message) => + session.on_initialize_session(sender.clone(), message), + EncryptionMessage::ConfirmEncryptionInitialization(ref message) => + session.on_confirm_initialization(sender.clone(), message), + EncryptionMessage::EncryptionSessionError(ref message) => + session.on_session_error(sender.clone(), message), }) { Ok(_) => { // if session is completed => stop @@ -509,7 +599,7 @@ impl ClusterCore { }, Err(err) => { warn!(target: "secretstore_net", "{}: encryption session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); - data.sessions.respond_with_encryption_error(&session_id, message::SessionError { + data.sessions.respond_with_encryption_error(&session_id, message::EncryptionSessionError { session: session_id.clone().into(), error: format!("{:?}", err), }); @@ -687,16 +777,17 @@ impl ClusterSessions { nodes: config.nodes.keys().cloned().collect(), acl_storage: config.acl_storage.clone(), key_storage: config.key_storage.clone(), + generation_sessions: RwLock::new(BTreeMap::new()), encryption_sessions: RwLock::new(BTreeMap::new()), decryption_sessions: RwLock::new(BTreeMap::new()), - make_faulty_encryption_sessions: AtomicBool::new(false), + make_faulty_generation_sessions: AtomicBool::new(false), } } - pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - let mut encryption_sessions = self.encryption_sessions.write(); + pub fn new_generation_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { + let mut generation_sessions = self.generation_sessions.write(); // check that there's no active encryption session with the same id - if encryption_sessions.contains_key(&session_id) { + if generation_sessions.contains_key(&session_id) { return Err(Error::DuplicateSessionId); } // check that there's no finished encryption session with the same id @@ -710,22 +801,85 @@ impl ClusterSessions { return Err(Error::NodeDisconnected); } - let session = Arc::new(EncryptionSessionImpl::new(EncryptionSessionParams { + let session = Arc::new(GenerationSessionImpl::new(GenerationSessionParams { id: session_id.clone(), self_node_id: self.self_node_id.clone(), key_storage: self.key_storage.clone(), cluster: cluster.clone(), })); - let encryption_session = QueuedEncryptionSession { + let generation_session = QueuedGenerationSession { master: master, cluster_view: cluster, last_message_time: time::Instant::now(), session: session.clone(), queue: VecDeque::new() }; - if self.make_faulty_encryption_sessions.load(Ordering::Relaxed) { - encryption_session.session.simulate_faulty_behaviour(); + if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { + generation_session.session.simulate_faulty_behaviour(); + } + generation_sessions.insert(session_id, generation_session); + Ok(session) + } + + pub fn remove_generation_session(&self, session_id: &SessionId) { + self.generation_sessions.write().remove(session_id); + } + + pub fn generation_session(&self, session_id: &SessionId) -> Option> { + self.generation_sessions.read().get(session_id).map(|s| s.session.clone()) + } + + pub fn enqueue_generation_message(&self, session_id: &SessionId, sender: NodeId, message: GenerationMessage, is_queued_message: bool) { + self.generation_sessions.write().get_mut(session_id) + .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } + else { session.queue.push_back((sender, message)) }); + } + + pub fn dequeue_generation_message(&self, session_id: &SessionId) -> Option<(NodeId, GenerationMessage)> { + self.generation_sessions.write().get_mut(session_id) + .and_then(|session| session.queue.pop_front()) + } + + pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { + self.generation_sessions.read().get(session_id) + .map(|s| { + // error in generation session is considered fatal + // => broadcast error + + // do not bother processing send error, as we already processing error + let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); + }); + } + + pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { + let mut encryption_sessions = self.encryption_sessions.write(); + if encryption_sessions.contains_key(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // some of nodes, which were generating the key may be down + // => do not use these in encryption session + let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); + for disconnected_node in disconnected_nodes { + encrypted_data.id_numbers.remove(&disconnected_node); } + + let session = Arc::new(EncryptionSessionImpl::new(EncryptionSessionParams { + id: session_id.clone(), + self_node_id: self.self_node_id.clone(), + encrypted_data: encrypted_data, + key_storage: self.key_storage.clone(), + cluster: cluster.clone(), + })?); + let encryption_session = QueuedEncryptionSession { + master: master, + cluster_view: cluster, + last_message_time: time::Instant::now(), + session: session.clone(), + queue: VecDeque::new() + }; encryption_sessions.insert(session_id, encryption_session); Ok(session) } @@ -749,20 +903,20 @@ impl ClusterSessions { .and_then(|session| session.queue.pop_front()) } - pub fn respond_with_encryption_error(&self, session_id: &SessionId, error: message::SessionError) { + pub fn respond_with_encryption_error(&self, session_id: &SessionId, error: message::EncryptionSessionError) { self.encryption_sessions.read().get(session_id) .map(|s| { // error in encryption session is considered fatal // => broadcast error // do not bother processing send error, as we already processing error - let _ = s.cluster_view.broadcast(Message::Encryption(EncryptionMessage::SessionError(error))); + let _ = s.cluster_view.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(error))); }); } #[cfg(test)] - pub fn make_faulty_encryption_sessions(&self) { - self.make_faulty_encryption_sessions.store(true, Ordering::Relaxed); + pub fn make_faulty_generation_sessions(&self) { + self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); } pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { @@ -841,6 +995,19 @@ impl ClusterSessions { } fn stop_stalled_sessions(&self) { + { + let sessions = self.generation_sessions.write(); + for sid in sessions.keys().collect::>() { + let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); + if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { + session.session.on_session_timeout(); + if session.session.state() == GenerationSessionState::Finished + || session.session.state() == GenerationSessionState::Failed { + self.remove_generation_session(&sid); + } + } + } + } { let sessions = self.encryption_sessions.write(); for sid in sessions.keys().collect::>() { @@ -870,6 +1037,13 @@ impl ClusterSessions { } pub fn on_connection_timeout(&self, node_id: &NodeId) { + for (sid, session) in self.generation_sessions.read().iter() { + session.session.on_node_timeout(node_id); + if session.session.state() == GenerationSessionState::Finished + || session.session.state() == GenerationSessionState::Failed { + self.remove_generation_session(sid); + } + } for (sid, session) in self.encryption_sessions.read().iter() { session.session.on_node_timeout(node_id); if session.session.state() == EncryptionSessionState::Finished @@ -1006,13 +1180,23 @@ impl ClusterClient for ClusterClientImpl { self.data.connections.cluster_state() } - fn new_encryption_session(&self, session_id: SessionId, threshold: usize) -> Result, Error> { + fn new_generation_session(&self, session_id: SessionId, author: Public, threshold: usize) -> Result, Error> { + let mut connected_nodes = self.data.connections.connected_nodes(); + connected_nodes.insert(self.data.self_key_pair.public().clone()); + + let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); + let session = self.data.sessions.new_generation_session(self.data.self_key_pair.public().clone(), session_id, cluster)?; + session.initialize(author, threshold, connected_nodes)?; + Ok(session) + } + + fn new_encryption_session(&self, session_id: SessionId, requestor_signature: Signature, common_point: Public, encrypted_point: Public) -> Result, Error> { let mut connected_nodes = self.data.connections.connected_nodes(); connected_nodes.insert(self.data.self_key_pair.public().clone()); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let session = self.data.sessions.new_encryption_session(self.data.self_key_pair.public().clone(), session_id, cluster)?; - session.initialize(threshold, connected_nodes)?; + session.initialize(requestor_signature, common_point, encrypted_point)?; Ok(session) } @@ -1033,13 +1217,13 @@ impl ClusterClient for ClusterClientImpl { } #[cfg(test)] - fn make_faulty_encryption_sessions(&self) { - self.data.sessions.make_faulty_encryption_sessions(); + fn make_faulty_generation_sessions(&self) { + self.data.sessions.make_faulty_generation_sessions(); } #[cfg(test)] - fn encryption_session(&self, session_id: &SessionId) -> Option> { - self.data.sessions.encryption_session(session_id) + fn generation_session(&self, session_id: &SessionId) -> Option> { + self.data.sessions.generation_session(session_id) } } @@ -1055,11 +1239,11 @@ pub mod tests { use std::collections::VecDeque; use parking_lot::Mutex; use tokio_core::reactor::Core; - use ethkey::{Random, Generator}; + use ethkey::{Random, Generator, Public}; use key_server_cluster::{NodeId, SessionId, Error, DummyAclStorage, DummyKeyStorage}; use key_server_cluster::message::Message; use key_server_cluster::cluster::{Cluster, ClusterCore, ClusterConfiguration}; - use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionState as EncryptionSessionState}; + use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; #[derive(Debug)] pub struct DummyCluster { @@ -1169,11 +1353,11 @@ pub mod tests { } #[test] - fn cluster_wont_start_encryption_session_if_not_fully_connected() { + fn cluster_wont_start_generation_session_if_not_fully_connected() { let core = Core::new().unwrap(); let clusters = make_clusters(&core, 6013, 3); clusters[0].run().unwrap(); - match clusters[0].client().new_encryption_session(SessionId::default(), 1) { + match clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1) { Err(Error::NodeDisconnected) => (), Err(e) => panic!("unexpected error {:?}", e), _ => panic!("unexpected success"), @@ -1181,50 +1365,50 @@ pub mod tests { } #[test] - fn error_in_encryption_session_broadcasted_to_all_other_nodes() { + fn error_in_generation_session_broadcasted_to_all_other_nodes() { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6016, 3); run_clusters(&clusters); loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); - // ask one of nodes to produce faulty encryption sessions - clusters[1].client().make_faulty_encryption_sessions(); + // ask one of nodes to produce faulty generation sessions + clusters[1].client().make_faulty_generation_sessions(); - // start && wait for encryption session to fail - let session = clusters[0].client().new_encryption_session(SessionId::default(), 1).unwrap(); + // start && wait for generation session to fail + let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_key().is_some()); assert!(session.joint_public_key().unwrap().is_err()); // check that faulty session is either removed from all nodes, or nonexistent (already removed) - assert!(clusters[0].client().encryption_session(&SessionId::default()).is_none()); + assert!(clusters[0].client().generation_session(&SessionId::default()).is_none()); for i in 1..3 { - if let Some(session) = clusters[i].client().encryption_session(&SessionId::default()) { + if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_key().is_some()); assert!(session.joint_public_key().unwrap().is_err()); - assert!(clusters[i].client().encryption_session(&SessionId::default()).is_none()); + assert!(clusters[i].client().generation_session(&SessionId::default()).is_none()); } } } #[test] - fn encryption_session_is_removed_when_succeeded() { + fn generation_session_is_removed_when_succeeded() { let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6019, 3); run_clusters(&clusters); loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); - // start && wait for encryption session to complete - let session = clusters[0].client().new_encryption_session(SessionId::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.state() == EncryptionSessionState::Finished); + // start && wait for generation session to complete + let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); + loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished); assert!(session.joint_public_key().unwrap().is_ok()); // check that session is either removed from all nodes, or nonexistent (already removed) - assert!(clusters[0].client().encryption_session(&SessionId::default()).is_none()); + assert!(clusters[0].client().generation_session(&SessionId::default()).is_none()); for i in 1..3 { - if let Some(session) = clusters[i].client().encryption_session(&SessionId::default()) { - loop_until(&mut core, time::Duration::from_millis(300), || session.state() == EncryptionSessionState::Finished); + if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { + loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished); assert!(session.joint_public_key().unwrap().is_err()); - assert!(clusters[i].client().encryption_session(&SessionId::default()).is_none()); + assert!(clusters[i].client().generation_session(&SessionId::default()).is_none()); } } } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 55c666956e2..61e9c655f3c 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -573,11 +573,13 @@ impl SessionImpl { fn do_decryption(access_key: Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // decrypt the secret using shadow points let joint_shadow_point = math::compute_joint_shadow_point(data.shadow_points.values().map(|s| &s.shadow_point))?; - let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, &access_key, &encrypted_data.encrypted_point, &joint_shadow_point)?; + let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); + let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); + let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, &access_key, encrypted_point, &joint_shadow_point)?; let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; decryption follows initialization; qed"); let (common_point, decrypt_shadows) = if is_shadow_decryption { ( - Some(math::make_common_shadow_point(encrypted_data.threshold, encrypted_data.common_point.clone())?), + Some(math::make_common_shadow_point(encrypted_data.threshold, common_point.clone())?), Some(data.shadow_points.values() .map(|s| s.decrypt_shadow.as_ref().expect("decrypt_shadow is filled during partial decryption; decryption follows partial decryption; qed").clone()) .collect()) @@ -639,15 +641,19 @@ impl Ord for DecryptionSessionId { fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { - use key_server_cluster::encryption_session::{check_cluster_nodes, check_threshold}; + use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; + + // check that common_point and encrypted_point are already set + if encrypted_data.common_point.is_none() || encrypted_data.encrypted_point.is_none() { + return Err(Error::NotStartedSessionId); + } let nodes = encrypted_data.id_numbers.keys().cloned().collect(); check_cluster_nodes(self_node_id, &nodes)?; - check_threshold(encrypted_data.threshold, &nodes)?; - - Ok(()) + check_threshold(encrypted_data.threshold, &nodes) } + fn process_initialization_response(encrypted_data: &DocumentKeyShare, data: &mut SessionData, node: &NodeId, check_result: bool) -> Result<(), Error> { if !data.requested_nodes.remove(node) { return Err(Error::InvalidMessage); @@ -684,7 +690,8 @@ fn do_partial_decryption(node: &NodeId, requestor_public: &Public, is_shadow_dec .map(|id| &encrypted_data.id_numbers[id]); let node_shadow = math::compute_node_shadow(node_id_number, node_secret_share, other_id_numbers)?; let decrypt_shadow = if is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; - let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(access_key, &encrypted_data.common_point, &node_shadow, decrypt_shadow)?; + let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); + let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(access_key, common_point, &node_shadow, decrypt_shadow)?; Ok(PartialDecryptionResult { shadow_point: shadow_point, decrypt_shadow: match decrypt_shadow { @@ -734,11 +741,12 @@ mod tests { let common_point: Public = "6962be696e1bcbba8e64cc7fddf140f854835354b5804f3bb95ae5a2799130371b589a131bd39699ac7174ccb35fc4342dab05331202209582fc8f3a40916ab0".into(); let encrypted_point: Public = "b07031982bde9890e12eff154765f03c56c3ab646ad47431db5dd2d742a9297679c4c65b998557f8008469afd0c43d40b6c5f6c6a1c7354875da4115237ed87a".into(); let encrypted_datas: Vec<_> = (0..5).map(|i| DocumentKeyShare { + author: Public::default(), threshold: 3, id_numbers: id_numbers.clone().into_iter().collect(), secret_share: secret_shares[i].clone(), - common_point: common_point.clone(), - encrypted_point: encrypted_point.clone(), + common_point: Some(common_point.clone()), + encrypted_point: Some(encrypted_point.clone()), }).collect(); let acl_storages: Vec<_> = (0..5).map(|_| Arc::new(DummyAclStorage::default())).collect(); let clusters: Vec<_> = (0..5).map(|i| { @@ -792,11 +800,12 @@ mod tests { access_key: Random.generate().unwrap().secret().clone(), self_node_id: self_node_id.clone(), encrypted_data: DocumentKeyShare { + author: Public::default(), threshold: 0, id_numbers: nodes, secret_share: Random.generate().unwrap().secret().clone(), - common_point: Random.generate().unwrap().public().clone(), - encrypted_point: Random.generate().unwrap().public().clone(), + common_point: Some(Random.generate().unwrap().public().clone()), + encrypted_point: Some(Random.generate().unwrap().public().clone()), }, acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), @@ -817,11 +826,12 @@ mod tests { access_key: Random.generate().unwrap().secret().clone(), self_node_id: self_node_id.clone(), encrypted_data: DocumentKeyShare { + author: Public::default(), threshold: 0, id_numbers: nodes, secret_share: Random.generate().unwrap().secret().clone(), - common_point: Random.generate().unwrap().public().clone(), - encrypted_point: Random.generate().unwrap().public().clone(), + common_point: Some(Random.generate().unwrap().public().clone()), + encrypted_point: Some(Random.generate().unwrap().public().clone()), }, acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), @@ -842,11 +852,12 @@ mod tests { access_key: Random.generate().unwrap().secret().clone(), self_node_id: self_node_id.clone(), encrypted_data: DocumentKeyShare { + author: Public::default(), threshold: 2, id_numbers: nodes, secret_share: Random.generate().unwrap().secret().clone(), - common_point: Random.generate().unwrap().public().clone(), - encrypted_point: Random.generate().unwrap().public().clone(), + common_point: Some(Random.generate().unwrap().public().clone()), + encrypted_point: Some(Random.generate().unwrap().public().clone()), }, acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), diff --git a/secret_store/src/key_server_cluster/encryption_session.rs b/secret_store/src/key_server_cluster/encryption_session.rs index 0268bf596ee..22165374322 100644 --- a/secret_store/src/key_server_cluster/encryption_session.rs +++ b/secret_store/src/key_server_cluster/encryption_session.rs @@ -14,44 +14,40 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{BTreeSet, BTreeMap, VecDeque}; +use std::collections::BTreeMap; use std::fmt::{Debug, Formatter, Error as FmtError}; use std::time; use std::sync::Arc; use parking_lot::{Condvar, Mutex}; -use ethkey::{Public, Secret}; +use ethkey::{self, Public, Signature}; use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare}; -use key_server_cluster::math; use key_server_cluster::cluster::Cluster; -use key_server_cluster::message::{Message, EncryptionMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, - KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; +use key_server_cluster::message::{Message, EncryptionMessage, InitializeEncryptionSession, + ConfirmEncryptionInitialization, EncryptionSessionError}; /// Encryption session API. pub trait Session: Send + Sync + 'static { /// Get encryption session state. fn state(&self) -> SessionState; /// Wait until session is completed. Returns distributely generated secret key. - fn wait(&self, timeout: Option) -> Result; - - #[cfg(test)] - /// Get joint public key (if it is known). - fn joint_public_key(&self) -> Option>; + fn wait(&self, timeout: Option) -> Result<(), Error>; } /// Encryption (distributed key generation) session. /// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: /// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf /// Brief overview: -/// 1) initialization: master node (which has received request for generating joint public + secret) initializes the session on all other nodes -/// 2) key dissemination (KD): all nodes are generating secret + public values and send these to appropriate nodes -/// 3) key verification (KV): all nodes are checking values, received for other nodes -/// 4) key generation phase (KG): nodes are exchanging with information, enough to generate joint public key -/// 5) encryption phase: master node generates secret key, encrypts it using joint public && broadcasts encryption result +/// 1) initialization: master node (which has received request for storing the secret) initializes the session on all other nodes +/// 2) master node sends common_point + encrypted_point to all other nodes +/// 3) common_point + encrypted_point are saved on all nodes +/// 4) in case of error, previous values are restored pub struct SessionImpl { /// Unique session id. id: SessionId, /// Public identifier of this node. self_node_id: NodeId, + /// Encrypted data. + encrypted_data: DocumentKeyShare, /// Key storage. key_storage: Arc, /// Cluster which allows this node to send messages to other nodes in the cluster. @@ -68,6 +64,8 @@ pub struct SessionParams { pub id: SessionId, /// Id of node, on which this session is running. pub self_node_id: Public, + /// Encrypted data (result of running generation_session::SessionImpl). + pub encrypted_data: DocumentKeyShare, /// Key storage. pub key_storage: Arc, /// Cluster @@ -79,73 +77,18 @@ pub struct SessionParams { struct SessionData { /// Current state of the session. state: SessionState, - /// Simulate faulty behaviour? - simulate_faulty_behaviour: bool, - - // === Values, filled when session initialization just starts === - /// Reference to the node, which has started this session. - master: Option, - - // === Values, filled when session initialization is completed === - /// Threshold value for this DKG. Only `threshold + 1` will be able to collectively recreate joint secret, - /// and thus - decrypt message, encrypted with joint public. - threshold: Option, - /// Random point, jointly generated by every node in the cluster. - derived_point: Option, /// Nodes-specific data. nodes: BTreeMap, - - // === Values, filled during KD phase === - /// Value of polynom1[0], generated by this node. - secret_coeff: Option, - - // === Values, filled during KG phase === - /// Secret share, which this node holds. Persistent + private. - secret_share: Option, - - /// === Values, filled when DKG session is completed successfully === - /// Jointly generated public key, which can be used to encrypt secret. Public. - joint_public: Option>, - /// Secret point. - secret_point: Option>, + /// Encryption session result. + result: Option>, } #[derive(Debug, Clone)] /// Mutable node-specific data. struct NodeData { - /// Random unique scalar. Persistent. - pub id_number: Secret, - - // === Values, filled during KD phase === - /// Secret value1, which has been sent to this node. - pub secret1_sent: Option, - /// Secret value2, which has been sent to this node. - pub secret2_sent: Option, - /// Secret value1, which has been received from this node. - pub secret1: Option, - /// Secret value2, which has been received from this node. - pub secret2: Option, - /// Public values, which have been received from this node. - pub publics: Option>, - - // === Values, filled during KG phase === - /// Public share, which has been received from this node. - pub public_share: Option, - - // === Values, filled during encryption phase === - /// Flags marking that node has confirmed session completion (encryption data is stored). - pub completion_confirmed: bool, -} - -#[derive(Debug, Clone, PartialEq)] -/// Schedule for visiting other nodes of cluster. -pub struct EveryOtherNodeVisitor { - /// Already visited nodes. - visited: BTreeSet, - /// Not yet visited nodes. - unvisited: VecDeque, - /// Nodes, which are currently visited. - in_progress: BTreeSet, + // === Values, filled during initialization phase === + /// Flags marking that node has confirmed session initialization. + pub initialization_confirmed: bool, } #[derive(Debug, Clone, PartialEq)] @@ -154,53 +97,34 @@ pub enum SessionState { // === Initialization states === /// Every node starts in this state. WaitingForInitialization, - /// Master node asks every other node to confirm initialization. - /// Derived point is generated by all nodes in the cluster. - WaitingForInitializationConfirm(EveryOtherNodeVisitor), - /// Slave nodes are in this state until initialization completion is reported by master node. - WaitingForInitializationComplete, - - // === KD phase states === - /// Node is waiting for generated keys from every other node. - WaitingForKeysDissemination, - - // === KG phase states === - /// Node is waiting for joint public key share to be received from every other node. - WaitingForPublicKeyShare, - - // === Encryption phase states === - /// Node is waiting for session completion/session completion confirmation. - WaitingForEncryptionConfirmation, + /// Master node waits for every other node to confirm initialization. + WaitingForInitializationConfirm, // === Final states of the session === - /// Joint public key generation is completed. + /// Encryption data is saved. Finished, - /// Joint public key generation is failed. + /// Failed to save encryption data. Failed, } impl SessionImpl { /// Create new encryption session. - pub fn new(params: SessionParams) -> Self { - SessionImpl { + pub fn new(params: SessionParams) -> Result { + check_encrypted_data(¶ms.self_node_id, ¶ms.encrypted_data)?; + + Ok(SessionImpl { id: params.id, self_node_id: params.self_node_id, + encrypted_data: params.encrypted_data, key_storage: params.key_storage, cluster: params.cluster, completed: Condvar::new(), data: Mutex::new(SessionData { state: SessionState::WaitingForInitialization, - simulate_faulty_behaviour: false, - master: None, - threshold: None, - derived_point: None, nodes: BTreeMap::new(), - secret_coeff: None, - secret_share: None, - joint_public: None, - secret_point: None, + result: None, }), - } + }) } /// Get this node Id. @@ -208,22 +132,8 @@ impl SessionImpl { &self.self_node_id } - #[cfg(test)] - /// Get derived point. - pub fn derived_point(&self) -> Option { - self.data.lock().derived_point.clone() - } - - /// Simulate faulty encryption session behaviour. - pub fn simulate_faulty_behaviour(&self) { - self.data.lock().simulate_faulty_behaviour = true; - } - /// Start new session initialization. This must be called on master node. - pub fn initialize(&self, threshold: usize, nodes: BTreeSet) -> Result<(), Error> { - check_cluster_nodes(self.node(), &nodes)?; - check_threshold(threshold, &nodes)?; - + pub fn initialize(&self, requestor_signature: Signature, common_point: Public, encrypted_point: Public) -> Result<(), Error> { let mut data = self.data.lock(); // check state @@ -231,39 +141,48 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } - // update state - data.master = Some(self.node().clone()); - data.threshold = Some(threshold); - for node_id in &nodes { - // generate node identification parameter - let node_id_number = math::generate_random_scalar()?; - data.nodes.insert(node_id.clone(), NodeData::with_id_number(node_id_number)); + // check that the requester is the author of the encrypted data + let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; + if self.encrypted_data.author != requestor_public { + return Err(Error::AccessDenied); } - let mut visit_policy = EveryOtherNodeVisitor::new(self.node(), data.nodes.keys().cloned()); - let derived_point = math::generate_random_point()?; - match visit_policy.next_node() { - Some(next_node) => { - data.state = SessionState::WaitingForInitializationConfirm(visit_policy); + // update state + data.state = SessionState::WaitingForInitializationConfirm; + for node_id in self.encrypted_data.id_numbers.keys() { + data.nodes.insert(node_id.clone(), NodeData { + initialization_confirmed: node_id == self.node(), + }); + } + + // TODO: there could be situation when some nodes have failed to store encrypted data + // => potential problems during restore. some confirmation step is needed? + // save encryption data + let mut encrypted_data = self.encrypted_data.clone(); + encrypted_data.common_point = Some(common_point.clone()); + encrypted_data.encrypted_point = Some(encrypted_point.clone()); + self.key_storage.update(self.id.clone(), encrypted_data) + .map_err(|e| Error::KeyStorage(e.into()))?; - // start initialization - self.cluster.send(&next_node, Message::Encryption(EncryptionMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - derived_point: derived_point.into(), - }))) - }, - None => { - drop(data); - self.complete_initialization(derived_point)?; - self.disseminate_keys()?; - self.verify_keys()?; - self.complete_encryption() - } + // start initialization + if self.encrypted_data.id_numbers.len() > 1 { + self.cluster.broadcast(Message::Encryption(EncryptionMessage::InitializeEncryptionSession(InitializeEncryptionSession { + session: self.id.clone().into(), + requestor_signature: requestor_signature.into(), + common_point: common_point.into(), + encrypted_point: encrypted_point.into(), + }))) + } else { + data.state = SessionState::Finished; + data.result = Some(Ok(())); + self.completed.notify_all(); + + Ok(()) } } /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeSession) -> Result<(), Error> { + pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeEncryptionSession) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); @@ -274,241 +193,60 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } - // update derived point with random scalar - let mut derived_point = message.derived_point.clone().into(); - math::update_random_point(&mut derived_point)?; + // check that the requester is the author of the encrypted data + let requestor_public = ethkey::recover(&message.requestor_signature.clone().into(), &self.id)?; + if self.encrypted_data.author != requestor_public { + return Err(Error::AccessDenied); + } - // send confirmation back to master node - self.cluster.send(&sender, Message::Encryption(EncryptionMessage::ConfirmInitialization(ConfirmInitialization { - session: self.id.clone().into(), - derived_point: derived_point.into(), - })))?; + // save encryption data + let mut encrypted_data = self.encrypted_data.clone(); + encrypted_data.common_point = Some(message.common_point.clone().into()); + encrypted_data.encrypted_point = Some(message.encrypted_point.clone().into()); + self.key_storage.update(self.id.clone(), encrypted_data) + .map_err(|e| Error::KeyStorage(e.into()))?; // update state - data.master = Some(sender); - data.state = SessionState::WaitingForInitializationComplete; + data.state = SessionState::Finished; - Ok(()) + // send confirmation back to master node + self.cluster.send(&sender, Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(ConfirmEncryptionInitialization { + session: self.id.clone().into(), + }))) } /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { + pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmEncryptionInitialization) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); let mut data = self.data.lock(); debug_assert!(data.nodes.contains_key(&sender)); - // check state && select new node to be initialized - let next_receiver = match data.state { - SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { - if !visit_policy.mark_visited(&sender) { - return Err(Error::InvalidStateForRequest); - } - - visit_policy.next_node() - }, - _ => return Err(Error::InvalidStateForRequest), - }; - - // proceed message - if let Some(next_receiver) = next_receiver { - return self.cluster.send(&next_receiver, Message::Encryption(EncryptionMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - derived_point: message.derived_point.clone().into(), - }))); - } - - // now it is time for keys dissemination (KD) phase - drop(data); - self.complete_initialization(message.derived_point.clone().into())?; - self.disseminate_keys() - } - - /// When session initialization completion message is received. - pub fn on_complete_initialization(&self, sender: NodeId, message: &CompleteInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - // check message - let nodes_ids = message.nodes.keys().cloned().map(Into::into).collect(); - check_cluster_nodes(self.node(), &nodes_ids)?; - check_threshold(message.threshold, &nodes_ids)?; - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitializationComplete { - return Err(Error::InvalidStateForRequest); - } - if data.master != Some(sender) { - return Err(Error::InvalidMessage); - } - - // remember passed data - data.threshold = Some(message.threshold); - data.derived_point = Some(message.derived_point.clone().into()); - data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); - - // now it is time for keys dissemination (KD) phase - drop(data); - self.disseminate_keys() - } - - /// When keys dissemination message is received. - pub fn on_keys_dissemination(&self, sender: NodeId, message: &KeysDissemination) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // simulate failure, if required - if data.simulate_faulty_behaviour { - return Err(Error::Io("simulated error".into())); - } - - // check state - if data.state != SessionState::WaitingForKeysDissemination { - match data.state { - SessionState::WaitingForInitializationComplete => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - debug_assert!(data.nodes.contains_key(&sender)); - - // check message - let threshold = data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"); - if message.publics.len() != threshold + 1 { - return Err(Error::InvalidMessage); - } - - // update node data - { - let node_data = data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; - if node_data.secret1.is_some() || node_data.secret2.is_some() || node_data.publics.is_some() { - return Err(Error::InvalidStateForRequest); - } - - node_data.secret1 = Some(message.secret1.clone().into()); - node_data.secret2 = Some(message.secret2.clone().into()); - node_data.publics = Some(message.publics.iter().cloned().map(Into::into).collect()); - } - - // check if we have received keys from every other node - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && (node_data.publics.is_none() || node_data.secret1.is_none() || node_data.secret2.is_none())) { - return Ok(()) - } - - drop(data); - self.verify_keys() - } - - /// When public key share is received. - pub fn on_public_key_share(&self, sender: NodeId, message: &PublicKeyShare) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForPublicKeyShare { - match data.state { - SessionState::WaitingForInitializationComplete | - SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - - // update node data with received public share - { - let node_data = &mut data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; - if node_data.public_share.is_some() { - return Err(Error::InvalidMessage); - } - - node_data.public_share = Some(message.public_share.clone().into()); - } - - // if there's also nodes, which has not sent us their public shares - do nothing - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && node_data.public_share.is_none()) { + // check if all nodes have confirmed initialization + data.nodes.get_mut(&sender) + .expect("message is received from cluster; nodes contains all cluster nodes; qed") + .initialization_confirmed = true; + if !data.nodes.values().all(|n| n.initialization_confirmed) { return Ok(()); } - drop(data); - self.complete_encryption() - } - - /// When session completion message is received. - pub fn on_session_completed(&self, sender: NodeId, message: &SessionCompleted) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check state - if data.state != SessionState::WaitingForEncryptionConfirmation { - match data.state { - SessionState::WaitingForPublicKeyShare => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - - // if we are not masters, save result and respond with confirmation - if data.master.as_ref() != Some(self.node()) { - // check that we have received message from master - if data.master.as_ref() != Some(&sender) { - return Err(Error::InvalidMessage); - } - - // save encrypted data to key storage - let encrypted_data = DocumentKeyShare { - threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), - id_numbers: data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - secret_share: data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), - common_point: message.common_point.clone().into(), - encrypted_point: message.encrypted_point.clone().into(), - }; - self.key_storage.insert(self.id.clone(), encrypted_data.clone()) - .map_err(|e| Error::KeyStorage(e.into()))?; - - // then respond with confirmation - data.state = SessionState::Finished; - return self.cluster.send(&sender, Message::Encryption(EncryptionMessage::SessionCompleted(SessionCompleted { - session: self.id.clone().into(), - common_point: encrypted_data.common_point.clone().into(), - encrypted_point: encrypted_data.encrypted_point.clone().into(), - }))); - } - - // remember that we have received confirmation from sender node - { - let sender_node = data.nodes.get_mut(&sender).expect("node is always qualified by himself; qed"); - if sender_node.completion_confirmed { - return Err(Error::InvalidMessage); - } - - sender_node.completion_confirmed = true; - } - - // check if we have received confirmations from all cluster nodes - if data.nodes.iter().any(|(_, node_data)| !node_data.completion_confirmed) { - return Ok(()) - } - - // we have received enough confirmations => complete session + // update state data.state = SessionState::Finished; + data.result = Some(Ok(())); self.completed.notify_all(); Ok(()) } /// When error has occured on another node. - pub fn on_session_error(&self, sender: NodeId, message: &SessionError) -> Result<(), Error> { + pub fn on_session_error(&self, sender: NodeId, message: &EncryptionSessionError) -> Result<(), Error> { let mut data = self.data.lock(); warn!("{}: encryption session failed with error: {} from {}", self.node(), message.error, sender); data.state = SessionState::Failed; - data.joint_public = Some(Err(Error::Io(message.error.clone()))); - data.secret_point = Some(Err(Error::Io(message.error.clone()))); + data.result = Some(Err(Error::Io(message.error.clone()))); self.completed.notify_all(); Ok(()) @@ -518,13 +256,10 @@ impl SessionImpl { pub fn on_node_timeout(&self, node: &NodeId) { let mut data = self.data.lock(); - // all nodes are required for encryption session - // => fail without check warn!("{}: encryption session failed because {} connection has timeouted", self.node(), node); data.state = SessionState::Failed; - data.joint_public = Some(Err(Error::NodeDisconnected)); - data.secret_point = Some(Err(Error::NodeDisconnected)); + data.result = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } @@ -535,696 +270,46 @@ impl SessionImpl { warn!("{}: encryption session failed with timeout", self.node()); data.state = SessionState::Failed; - data.joint_public = Some(Err(Error::NodeDisconnected)); - data.secret_point = Some(Err(Error::NodeDisconnected)); + data.result = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } - - /// Complete initialization (when all other nodex has responded with confirmation) - fn complete_initialization(&self, mut derived_point: Public) -> Result<(), Error> { - // update point once again to make sure that derived point is not generated by last node - math::update_random_point(&mut derived_point)?; - - // remember derived point - let mut data = self.data.lock(); - data.derived_point = Some(derived_point.clone().into()); - - // broadcast derived point && other session paraeters to every other node - self.cluster.broadcast(Message::Encryption(EncryptionMessage::CompleteInitialization(CompleteInitialization { - session: self.id.clone().into(), - nodes: data.nodes.iter().map(|(id, data)| (id.clone().into(), data.id_number.clone().into())).collect(), - threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), - derived_point: derived_point.into(), - }))) - } - - /// Keys dissemination (KD) phase - fn disseminate_keys(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // pick 2t + 2 random numbers as polynomial coefficients for 2 polynoms - let threshold = data.threshold.expect("threshold is filled on initialization phase; KD phase follows initialization phase; qed"); - let polynom1 = math::generate_random_polynom(threshold)?; - let polynom2 = math::generate_random_polynom(threshold)?; - data.secret_coeff = Some(polynom1[0].clone()); - - // compute t+1 public values - let publics = math::public_values_generation(threshold, - data.derived_point.as_ref().expect("keys dissemination occurs after derived point is agreed; qed"), - &polynom1, - &polynom2)?; - - // compute secret values for every other node - for (node, node_data) in data.nodes.iter_mut() { - let secret1 = math::compute_polynom(&polynom1, &node_data.id_number)?; - let secret2 = math::compute_polynom(&polynom2, &node_data.id_number)?; - - // send a message containing secret1 && secret2 to other node - if node != self.node() { - node_data.secret1_sent = Some(secret1.clone()); - node_data.secret2_sent = Some(secret2.clone()); - - self.cluster.send(&node, Message::Encryption(EncryptionMessage::KeysDissemination(KeysDissemination { - session: self.id.clone().into(), - secret1: secret1.into(), - secret2: secret2.into(), - publics: publics.iter().cloned().map(Into::into).collect(), - })))?; - } else { - node_data.secret1 = Some(secret1); - node_data.secret2 = Some(secret2); - node_data.publics = Some(publics.clone()); - } - } - - // update state - data.state = SessionState::WaitingForKeysDissemination; - - Ok(()) - } - - /// Keys verification (KV) phase - fn verify_keys(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // key verification (KV) phase: check that other nodes have passed correct secrets - let threshold = data.threshold.expect("threshold is filled in initialization phase; KV phase follows initialization phase; qed"); - let derived_point = data.derived_point.clone().expect("derived point generated on initialization phase; KV phase follows initialization phase; qed"); - let number_id = data.nodes[self.node()].id_number.clone(); - for (_ , node_data) in data.nodes.iter_mut().filter(|&(node_id, _)| node_id != self.node()) { - let secret1 = node_data.secret1.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let secret2 = node_data.secret2.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let publics = node_data.publics.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let is_key_verification_ok = math::keys_verification(threshold, &derived_point, &number_id, - secret1, secret2, publics)?; - - if !is_key_verification_ok { - // node has sent us incorrect values. In original ECDKG protocol we should have sent complaint here. - return Err(Error::InvalidMessage); - } - } - - // calculate public share - let self_public_share = { - let self_secret_coeff = data.secret_coeff.as_ref().expect("secret_coeff is generated on KD phase; KG phase follows KD phase; qed"); - math::compute_public_share(self_secret_coeff)? - }; - - // calculate self secret + public shares - let self_secret_share = { - let secret_values_iter = data.nodes.values() - .map(|n| n.secret1.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_secret_share(secret_values_iter)? - }; - - // update state - data.state = SessionState::WaitingForPublicKeyShare; - data.secret_share = Some(self_secret_share); - let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); - self_node.public_share = Some(self_public_share.clone()); - - // broadcast self public key share - self.cluster.broadcast(Message::Encryption(EncryptionMessage::PublicKeyShare(PublicKeyShare { - session: self.id.clone().into(), - public_share: self_public_share.into(), - }))) - } - - /// Complete encryption - fn complete_encryption(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // else - calculate joint public key - let joint_public = { - let public_shares = data.nodes.values().map(|n| n.public_share.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_joint_public(public_shares)? - }; - - // if we are at the slave node - wait for session completion - if data.master.as_ref() != Some(self.node()) { - data.joint_public = Some(Ok(joint_public)); - data.state = SessionState::WaitingForEncryptionConfirmation; - return Ok(()); - } - - // then generate secret point - // then encrypt secret point with joint public key - // TODO: secret is revealed to KeyServer here - let secret_point = math::generate_random_point()?; - let encrypted_secret_point = math::encrypt_secret(&secret_point, &joint_public)?; - - // then save encrypted data to the key storage - let encrypted_data = DocumentKeyShare { - threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), - id_numbers: data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - secret_share: data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), - common_point: encrypted_secret_point.common_point, - encrypted_point: encrypted_secret_point.encrypted_point, - }; - self.key_storage.insert(self.id.clone(), encrypted_data.clone()) - .map_err(|e| Error::KeyStorage(e.into()))?; - - // then distribute encrypted data to every other node - self.cluster.broadcast(Message::Encryption(EncryptionMessage::SessionCompleted(SessionCompleted { - session: self.id.clone().into(), - common_point: encrypted_data.common_point.clone().into(), - encrypted_point: encrypted_data.encrypted_point.clone().into(), - })))?; - - // then wait for confirmation from all other nodes - { - let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); - self_node.completion_confirmed = true; - } - data.joint_public = Some(Ok(joint_public)); - data.secret_point = Some(Ok(secret_point)); - data.state = SessionState::WaitingForEncryptionConfirmation; - - Ok(()) - } } impl Session for SessionImpl { - #[cfg(test)] - fn joint_public_key(&self) -> Option> { - self.data.lock().joint_public.clone() - } - fn state(&self) -> SessionState { self.data.lock().state.clone() } - fn wait(&self, timeout: Option) -> Result { + fn wait(&self, timeout: Option) -> Result<(), Error> { let mut data = self.data.lock(); - if !data.secret_point.is_some() { + if !data.result.is_some() { match timeout { None => self.completed.wait(&mut data), Some(timeout) => { self.completed.wait_for(&mut data, timeout); }, } } - data.secret_point.as_ref() - .expect("checked above or waited for completed; completed is only signaled when secret_point.is_some(); qed") + data.result.as_ref() + .expect("checked above or waited for completed; completed is only signaled when result.is_some(); qed") .clone() } } -impl EveryOtherNodeVisitor { - pub fn new(self_id: &NodeId, nodes: I) -> Self where I: Iterator { - EveryOtherNodeVisitor { - visited: BTreeSet::new(), - unvisited: nodes.filter(|n| n != self_id).collect(), - in_progress: BTreeSet::new(), - } - } - - pub fn next_node(&mut self) -> Option { - let next_node = self.unvisited.pop_front(); - if let Some(ref next_node) = next_node { - self.in_progress.insert(next_node.clone()); - } - next_node - } - - pub fn mark_visited(&mut self, node: &NodeId) -> bool { - if !self.in_progress.remove(node) { - return false; - } - self.visited.insert(node.clone()) - } -} - -impl NodeData { - fn with_id_number(node_id_number: Secret) -> Self { - NodeData { - id_number: node_id_number, - secret1_sent: None, - secret2_sent: None, - secret1: None, - secret2: None, - publics: None, - public_share: None, - completion_confirmed: false, - } - } -} - impl Debug for SessionImpl { fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { write!(f, "Encryption session {} on {}", self.id, self.self_node_id) } } -pub fn check_cluster_nodes(self_node_id: &NodeId, nodes: &BTreeSet) -> Result<(), Error> { - // at least two nodes must be in cluster - if nodes.len() < 1 { - return Err(Error::InvalidNodesCount); - } - // this node must be a part of cluster - if !nodes.contains(self_node_id) { - return Err(Error::InvalidNodesConfiguration); - } - - Ok(()) -} - -pub fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Error> { - // at least threshold + 1 nodes are required to collectively decrypt message - if threshold >= nodes.len() { - return Err(Error::InvalidThreshold); - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::time; - use std::sync::Arc; - use std::collections::{BTreeSet, BTreeMap, VecDeque}; - use tokio_core::reactor::Core; - use ethkey::{Random, Generator}; - use key_server_cluster::{NodeId, SessionId, Error, DummyKeyStorage}; - use key_server_cluster::message::{self, Message, EncryptionMessage}; - use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; - use key_server_cluster::encryption_session::{Session, SessionImpl, SessionState, SessionParams}; - use key_server_cluster::math; - use key_server_cluster::math::tests::do_encryption_and_decryption; - - #[derive(Debug)] - struct Node { - pub cluster: Arc, - pub session: SessionImpl, - } - - #[derive(Debug)] - struct MessageLoop { - pub session_id: SessionId, - pub nodes: BTreeMap, - pub queue: VecDeque<(NodeId, NodeId, Message)>, - } - - impl MessageLoop { - pub fn new(nodes_num: usize) -> Self { - let mut nodes = BTreeMap::new(); - let session_id = SessionId::default(); - for _ in 0..nodes_num { - let key_pair = Random.generate().unwrap(); - let node_id = key_pair.public().clone(); - let cluster = Arc::new(DummyCluster::new(node_id.clone())); - let session = SessionImpl::new(SessionParams { - id: session_id.clone(), - self_node_id: node_id.clone(), - key_storage: Arc::new(DummyKeyStorage::default()), - cluster: cluster.clone(), - }); - nodes.insert(node_id, Node { cluster: cluster, session: session }); - } - - let nodes_ids: Vec<_> = nodes.keys().cloned().collect(); - for node in nodes.values() { - for node_id in &nodes_ids { - node.cluster.add_node(node_id.clone()); - } - } - - MessageLoop { - session_id: session_id, - nodes: nodes, - queue: VecDeque::new(), - } - } - - pub fn master(&self) -> &SessionImpl { - &self.nodes.values().nth(0).unwrap().session - } - - pub fn first_slave(&self) -> &SessionImpl { - &self.nodes.values().nth(1).unwrap().session - } - - pub fn second_slave(&self) -> &SessionImpl { - &self.nodes.values().nth(2).unwrap().session - } - - pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { - self.nodes.values() - .filter_map(|n| n.cluster.take_message().map(|m| (n.session.node().clone(), m.0, m.1))) - .nth(0) - .or_else(|| self.queue.pop_front()) - } - - pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { - match { - match msg.2 { - Message::Encryption(EncryptionMessage::InitializeSession(ref message)) => self.nodes[&msg.1].session.on_initialize_session(msg.0.clone(), &message), - Message::Encryption(EncryptionMessage::ConfirmInitialization(ref message)) => self.nodes[&msg.1].session.on_confirm_initialization(msg.0.clone(), &message), - Message::Encryption(EncryptionMessage::CompleteInitialization(ref message)) => self.nodes[&msg.1].session.on_complete_initialization(msg.0.clone(), &message), - Message::Encryption(EncryptionMessage::KeysDissemination(ref message)) => self.nodes[&msg.1].session.on_keys_dissemination(msg.0.clone(), &message), - Message::Encryption(EncryptionMessage::PublicKeyShare(ref message)) => self.nodes[&msg.1].session.on_public_key_share(msg.0.clone(), &message), - Message::Encryption(EncryptionMessage::SessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), - _ => panic!("unexpected"), - } - } { - Ok(_) => Ok(()), - Err(Error::TooEarlyForRequest) => { - self.queue.push_back(msg); - Ok(()) - }, - Err(err) => Err(err), - } - } - - pub fn take_and_process_message(&mut self) -> Result<(), Error> { - let msg = self.take_message().unwrap(); - self.process_message(msg) - } - } - - fn make_simple_cluster(threshold: usize, num_nodes: usize) -> Result<(SessionId, NodeId, NodeId, MessageLoop), Error> { - let l = MessageLoop::new(num_nodes); - l.master().initialize(threshold, l.nodes.keys().cloned().collect())?; - - let session_id = l.session_id.clone(); - let master_id = l.master().node().clone(); - let slave_id = l.first_slave().node().clone(); - Ok((session_id, master_id, slave_id, l)) - } - - #[test] - fn initializes_in_cluster_of_single_node() { - let l = MessageLoop::new(1); - assert!(l.master().initialize(0, l.nodes.keys().cloned().collect()).is_ok()); - } - - #[test] - fn fails_to_initialize_if_not_a_part_of_cluster() { - let node_id = math::generate_random_point().unwrap(); - let cluster = Arc::new(DummyCluster::new(node_id.clone())); - let session = SessionImpl::new(SessionParams { - id: SessionId::default(), - self_node_id: node_id.clone(), - key_storage: Arc::new(DummyKeyStorage::default()), - cluster: cluster, - }); - let cluster_nodes: BTreeSet<_> = (0..2).map(|_| math::generate_random_point().unwrap()).collect(); - assert_eq!(session.initialize(0, cluster_nodes).unwrap_err(), Error::InvalidNodesConfiguration); - } - - #[test] - fn fails_to_initialize_if_threshold_is_wrong() { - assert_eq!(make_simple_cluster(2, 2).unwrap_err(), Error::InvalidThreshold); - } - - #[test] - fn fails_to_initialize_when_already_initialized() { - let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); - assert_eq!(l.master().initialize(0, l.nodes.keys().cloned().collect()).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_initialization_when_already_initialized() { - let (sid, m, _, mut l) = make_simple_cluster(0, 2).unwrap(); - l.take_and_process_message().unwrap(); - assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { - session: sid.into(), - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn slave_updates_derived_point_on_initialization() { - let (_, _, _, mut l) = make_simple_cluster(0, 2).unwrap(); - let passed_point = match l.take_message().unwrap() { - (f, t, Message::Encryption(EncryptionMessage::InitializeSession(message))) => { - let point = message.derived_point.clone(); - l.process_message((f, t, Message::Encryption(EncryptionMessage::InitializeSession(message)))).unwrap(); - point - }, - _ => panic!("unexpected"), - }; - - match l.take_message().unwrap() { - (_, _, Message::Encryption(EncryptionMessage::ConfirmInitialization(message))) => assert!(passed_point != message.derived_point), - _ => panic!("unexpected"), - } - } - - #[test] - fn fails_to_accept_initialization_confirmation_if_already_accepted_from_the_same_node() { - let (sid, _, s, mut l) = make_simple_cluster(0, 3).unwrap(); - l.take_and_process_message().unwrap(); - l.take_and_process_message().unwrap(); - l.take_and_process_message().unwrap(); - assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization { - session: sid.into(), - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_initialization_confirmation_if_initialization_already_completed() { - let (sid, _, s, mut l) = make_simple_cluster(0, 2).unwrap(); - l.take_and_process_message().unwrap(); - l.take_and_process_message().unwrap(); - assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization { - session: sid.into(), - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn master_updates_derived_point_on_initialization_completion() { - let (_, _, _, mut l) = make_simple_cluster(0, 2).unwrap(); - l.take_and_process_message().unwrap(); - let passed_point = match l.take_message().unwrap() { - (f, t, Message::Encryption(EncryptionMessage::ConfirmInitialization(message))) => { - let point = message.derived_point.clone(); - l.process_message((f, t, Message::Encryption(EncryptionMessage::ConfirmInitialization(message)))).unwrap(); - point - }, - _ => panic!("unexpected"), - }; - - assert!(l.master().derived_point().unwrap() != passed_point.into()); - } - - #[test] - fn fails_to_complete_initialization_if_not_a_part_of_cluster() { - let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(math::generate_random_point().unwrap(), math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { - session: sid.into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - threshold: 0, - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidNodesConfiguration); - } - - #[test] - fn fails_to_complete_initialization_if_threshold_is_wrong() { - let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(s, math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { - session: sid.into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - threshold: 2, - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidThreshold); - } - - #[test] - fn fails_to_complete_initialization_if_not_waiting_for_it() { - let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(s, math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { - session: sid.into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - threshold: 0, - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_complete_initialization_from_non_master_node() { - let (sid, m, s, mut l) = make_simple_cluster(0, 3).unwrap(); - l.take_and_process_message().unwrap(); - l.take_and_process_message().unwrap(); - l.take_and_process_message().unwrap(); - l.take_and_process_message().unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(s, math::generate_random_scalar().unwrap()); - nodes.insert(l.second_slave().node().clone(), math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_complete_initialization(l.second_slave().node().clone(), &message::CompleteInitialization { - session: sid.into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - threshold: 0, - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_accept_keys_dissemination_if_not_waiting_for_it() { - let (sid, _, s, l) = make_simple_cluster(0, 2).unwrap(); - assert_eq!(l.master().on_keys_dissemination(s, &message::KeysDissemination { - session: sid.into(), - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into()], - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_keys_dissemination_if_wrong_number_of_publics_passed() { - let (sid, m, _, mut l) = make_simple_cluster(0, 3).unwrap(); - l.take_and_process_message().unwrap(); // m -> s1: InitializeSession - l.take_and_process_message().unwrap(); // m -> s2: InitializeSession - l.take_and_process_message().unwrap(); // s1 -> m: ConfirmInitialization - l.take_and_process_message().unwrap(); // s2 -> m: ConfirmInitialization - l.take_and_process_message().unwrap(); // m -> s1: CompleteInitialization - l.take_and_process_message().unwrap(); // m -> s2: CompleteInitialization - l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination - assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination { - session: sid.into(), - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into(), math::generate_random_point().unwrap().into()], - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_accept_keys_dissemination_second_time_from_the_same_node() { - let (sid, m, _, mut l) = make_simple_cluster(0, 3).unwrap(); - l.take_and_process_message().unwrap(); // m -> s1: InitializeSession - l.take_and_process_message().unwrap(); // m -> s2: InitializeSession - l.take_and_process_message().unwrap(); // s1 -> m: ConfirmInitialization - l.take_and_process_message().unwrap(); // s2 -> m: ConfirmInitialization - l.take_and_process_message().unwrap(); // m -> s1: CompleteInitialization - l.take_and_process_message().unwrap(); // m -> s2: CompleteInitialization - l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination - assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination { - session: sid.into(), - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into()], - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn should_not_accept_public_key_share_when_is_not_waiting_for_it() { - let (sid, _, s, l) = make_simple_cluster(1, 3).unwrap(); - assert_eq!(l.master().on_public_key_share(s, &message::PublicKeyShare { - session: sid.into(), - public_share: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn should_not_accept_public_key_share_when_receiving_twice() { - let (sid, m, _, mut l) = make_simple_cluster(0, 3).unwrap(); - l.take_and_process_message().unwrap(); // m -> s1: InitializeSession - l.take_and_process_message().unwrap(); // m -> s2: InitializeSession - l.take_and_process_message().unwrap(); // s1 -> m: ConfirmInitialization - l.take_and_process_message().unwrap(); // s2 -> m: ConfirmInitialization - l.take_and_process_message().unwrap(); // m -> s1: CompleteInitialization - l.take_and_process_message().unwrap(); // m -> s2: CompleteInitialization - l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination - l.take_and_process_message().unwrap(); // m -> s2: KeysDissemination - l.take_and_process_message().unwrap(); // s1 -> m: KeysDissemination - l.take_and_process_message().unwrap(); // s1 -> s2: KeysDissemination - l.take_and_process_message().unwrap(); // s2 -> m: KeysDissemination - l.take_and_process_message().unwrap(); // s2 -> s1: KeysDissemination - let (f, t, msg) = match l.take_message() { - Some((f, t, Message::Encryption(EncryptionMessage::PublicKeyShare(msg)))) => (f, t, msg), - _ => panic!("unexpected"), - }; - assert_eq!(&f, l.master().node()); - assert_eq!(&t, l.second_slave().node()); - l.process_message((f, t, Message::Encryption(EncryptionMessage::PublicKeyShare(msg.clone())))).unwrap(); - assert_eq!(l.second_slave().on_public_key_share(m, &message::PublicKeyShare { - session: sid.into(), - public_share: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidMessage); - } +fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { + use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; - - #[test] - fn encryption_fails_on_session_timeout() { - let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); - assert!(l.master().joint_public_key().is_none()); - l.master().on_session_timeout(); - assert!(l.master().joint_public_key().unwrap().unwrap_err() == Error::NodeDisconnected); - } - - #[test] - fn encryption_fails_on_node_timeout() { - let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); - assert!(l.master().joint_public_key().is_none()); - l.master().on_node_timeout(l.first_slave().node()); - assert!(l.master().joint_public_key().unwrap().unwrap_err() == Error::NodeDisconnected); + // check that common_point and encrypted_point are still not set yet + if encrypted_data.common_point.is_some() || encrypted_data.encrypted_point.is_some() { + return Err(Error::CompletedSessionId); } - #[test] - fn complete_enc_dec_session() { - let test_cases = [(0, 5), (2, 5), (3, 5)]; - for &(threshold, num_nodes) in &test_cases { - let mut l = MessageLoop::new(num_nodes); - l.master().initialize(threshold, l.nodes.keys().cloned().collect()).unwrap(); - assert_eq!(l.nodes.len(), num_nodes); - - // let nodes do initialization + keys dissemination - while let Some((from, to, message)) = l.take_message() { - l.process_message((from, to, message)).unwrap(); - } - - // check that all nodes has finished joint public generation - let joint_public_key = l.master().joint_public_key().unwrap().unwrap(); - for node in l.nodes.values() { - let state = node.session.state(); - assert_eq!(state, SessionState::Finished); - assert_eq!(node.session.joint_public_key().as_ref(), Some(&Ok(joint_public_key))); - } - - // now let's encrypt some secret (which is a point on EC) - let document_secret_plain = Random.generate().unwrap().public().clone(); - let all_nodes_id_numbers: Vec<_> = l.master().data.lock().nodes.values().map(|n| n.id_number.clone()).collect(); - let all_nodes_secret_shares: Vec<_> = l.nodes.values().map(|n| n.session.data.lock().secret_share.as_ref().unwrap().clone()).collect(); - let document_secret_decrypted = do_encryption_and_decryption(threshold, &joint_public_key, - &all_nodes_id_numbers, - &all_nodes_secret_shares, - None, - document_secret_plain.clone() - ).0; - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - - #[test] - fn encryption_session_works_over_network() { - //::util::log::init_log(); - - let test_cases = [(1, 3)]; - for &(threshold, num_nodes) in &test_cases { - let mut core = Core::new().unwrap(); - - // prepare cluster objects for each node - let clusters = make_clusters(&core, 6022, num_nodes); - run_clusters(&clusters); - - // establish connections - loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); - - // run session to completion - let session_id = SessionId::default(); - let session = clusters[0].client().new_encryption_session(session_id, threshold).unwrap(); - loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_key().is_some()); - } - } + let nodes = encrypted_data.id_numbers.keys().cloned().collect(); + check_cluster_nodes(self_node_id, &nodes)?; + check_threshold(encrypted_data.threshold, &nodes) } diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs new file mode 100644 index 00000000000..4eceb366233 --- /dev/null +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -0,0 +1,1221 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::collections::{BTreeSet, BTreeMap, VecDeque}; +use std::fmt::{Debug, Formatter, Error as FmtError}; +use std::time; +use std::sync::Arc; +use parking_lot::{Condvar, Mutex}; +use ethkey::{Public, Secret}; +use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare}; +use key_server_cluster::math; +use key_server_cluster::cluster::Cluster; +use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, + KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; + +/// Key generation session API. +pub trait Session: Send + Sync + 'static { + /// Get generation session state. + fn state(&self) -> SessionState; + /// Wait until session is completed. Returns public portion of generated server key. + fn wait(&self, timeout: Option) -> Result; + #[cfg(test)] + /// Get joint public key (if it is known). + fn joint_public_key(&self) -> Option>; +} + +/// Distributed key generation session. +/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: +/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf +/// Brief overview: +/// 1) initialization: master node (which has received request for generating joint public + secret) initializes the session on all other nodes +/// 2) key dissemination (KD): all nodes are generating secret + public values and send these to appropriate nodes +/// 3) key verification (KV): all nodes are checking values, received for other nodes +/// 4) key generation phase (KG): nodes are exchanging with information, enough to generate joint public key +pub struct SessionImpl { + /// Unique session id. + id: SessionId, + /// Public identifier of this node. + self_node_id: NodeId, + /// Key storage. + key_storage: Arc, + /// Cluster which allows this node to send messages to other nodes in the cluster. + cluster: Arc, + /// SessionImpl completion condvar. + completed: Condvar, + /// Mutable session data. + data: Mutex, +} + +/// SessionImpl creation parameters +pub struct SessionParams { + /// SessionImpl identifier. + pub id: SessionId, + /// Id of node, on which this session is running. + pub self_node_id: Public, + /// Key storage. + pub key_storage: Arc, + /// Cluster + pub cluster: Arc, +} + +#[derive(Debug)] +/// Mutable data of distributed key generation session. +struct SessionData { + /// Current state of the session. + state: SessionState, + /// Simulate faulty behaviour? + simulate_faulty_behaviour: bool, + + // === Values, filled when session initialization just starts === + /// Reference to the node, which has started this session. + master: Option, + /// Public key of the creator of the session. + author: Option, + + // === Values, filled when session initialization is completed === + /// Threshold value for this DKG. Only `threshold + 1` will be able to collectively recreate joint secret, + /// and thus - decrypt message, encrypted with joint public. + threshold: Option, + /// Random point, jointly generated by every node in the cluster. + derived_point: Option, + /// Nodes-specific data. + nodes: BTreeMap, + + // === Values, filled during KD phase === + /// Value of polynom1[0], generated by this node. + secret_coeff: Option, + + // === Values, filled during KG phase === + /// Secret share, which this node holds. Persistent + private. + secret_share: Option, + + /// === Values, filled when DKG session is completed successfully === + /// Jointly generated public key, which can be used to encrypt secret. Public. + joint_public: Option>, +} + +#[derive(Debug, Clone)] +/// Mutable node-specific data. +struct NodeData { + /// Random unique scalar. Persistent. + pub id_number: Secret, + + // === Values, filled during KD phase === + /// Secret value1, which has been sent to this node. + pub secret1_sent: Option, + /// Secret value2, which has been sent to this node. + pub secret2_sent: Option, + /// Secret value1, which has been received from this node. + pub secret1: Option, + /// Secret value2, which has been received from this node. + pub secret2: Option, + /// Public values, which have been received from this node. + pub publics: Option>, + + // === Values, filled during KG phase === + /// Public share, which has been received from this node. + pub public_share: Option, + + // === Values, filled during completion phase === + /// Flags marking that node has confirmed session completion (generated key is stored). + pub completion_confirmed: bool, +} + +#[derive(Debug, Clone, PartialEq)] +/// Schedule for visiting other nodes of cluster. +pub struct EveryOtherNodeVisitor { + /// Already visited nodes. + visited: BTreeSet, + /// Not yet visited nodes. + unvisited: VecDeque, + /// Nodes, which are currently visited. + in_progress: BTreeSet, +} + +#[derive(Debug, Clone, PartialEq)] +/// Distributed key generation session state. +pub enum SessionState { + // === Initialization states === + /// Every node starts in this state. + WaitingForInitialization, + /// Master node asks every other node to confirm initialization. + /// Derived point is generated by all nodes in the cluster. + WaitingForInitializationConfirm(EveryOtherNodeVisitor), + /// Slave nodes are in this state until initialization completion is reported by master node. + WaitingForInitializationComplete, + + // === KD phase states === + /// Node is waiting for generated keys from every other node. + WaitingForKeysDissemination, + + // === KG phase states === + /// Node is waiting for joint public key share to be received from every other node. + WaitingForPublicKeyShare, + + // === Generation phase states === + /// Node is waiting for session completion/session completion confirmation. + WaitingForGenerationConfirmation, + + // === Final states of the session === + /// Joint public key generation is completed. + Finished, + /// Joint public key generation is failed. + Failed, +} + +impl SessionImpl { + /// Create new generation session. + pub fn new(params: SessionParams) -> Self { + SessionImpl { + id: params.id, + self_node_id: params.self_node_id, + key_storage: params.key_storage, + cluster: params.cluster, + completed: Condvar::new(), + data: Mutex::new(SessionData { + state: SessionState::WaitingForInitialization, + simulate_faulty_behaviour: false, + master: None, + author: None, + threshold: None, + derived_point: None, + nodes: BTreeMap::new(), + secret_coeff: None, + secret_share: None, + joint_public: None, + }), + } + } + + /// Get this node Id. + pub fn node(&self) -> &NodeId { + &self.self_node_id + } + + #[cfg(test)] + /// Get derived point. + pub fn derived_point(&self) -> Option { + self.data.lock().derived_point.clone() + } + + /// Simulate faulty generation session behaviour. + pub fn simulate_faulty_behaviour(&self) { + self.data.lock().simulate_faulty_behaviour = true; + } + + /// Start new session initialization. This must be called on master node. + pub fn initialize(&self, author: Public, threshold: usize, nodes: BTreeSet) -> Result<(), Error> { + check_cluster_nodes(self.node(), &nodes)?; + check_threshold(threshold, &nodes)?; + + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitialization { + return Err(Error::InvalidStateForRequest); + } + + // update state + data.master = Some(self.node().clone()); + data.author = Some(author.clone()); + data.threshold = Some(threshold); + for node_id in &nodes { + // generate node identification parameter + let node_id_number = math::generate_random_scalar()?; + data.nodes.insert(node_id.clone(), NodeData::with_id_number(node_id_number)); + } + + let mut visit_policy = EveryOtherNodeVisitor::new(self.node(), data.nodes.keys().cloned()); + let derived_point = math::generate_random_point()?; + match visit_policy.next_node() { + Some(next_node) => { + data.state = SessionState::WaitingForInitializationConfirm(visit_policy); + + // start initialization + self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { + session: self.id.clone().into(), + author: author.into(), + derived_point: derived_point.into(), + }))) + }, + None => { + drop(data); + self.complete_initialization(derived_point)?; + self.disseminate_keys()?; + self.verify_keys()?; + self.complete_generation() + } + } + } + + /// When session initialization message is received. + pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeSession) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitialization { + return Err(Error::InvalidStateForRequest); + } + + // update derived point with random scalar + let mut derived_point = message.derived_point.clone().into(); + math::update_random_point(&mut derived_point)?; + + // send confirmation back to master node + self.cluster.send(&sender, Message::Generation(GenerationMessage::ConfirmInitialization(ConfirmInitialization { + session: self.id.clone().into(), + derived_point: derived_point.into(), + })))?; + + // update state + data.master = Some(sender); + data.author = Some(message.author.clone().into()); + data.state = SessionState::WaitingForInitializationComplete; + + Ok(()) + } + + /// When session initialization confirmation message is reeived. + pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + debug_assert!(data.nodes.contains_key(&sender)); + + // check state && select new node to be initialized + let next_receiver = match data.state { + SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { + if !visit_policy.mark_visited(&sender) { + return Err(Error::InvalidStateForRequest); + } + + visit_policy.next_node() + }, + _ => return Err(Error::InvalidStateForRequest), + }; + + // proceed message + if let Some(next_receiver) = next_receiver { + return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { + session: self.id.clone().into(), + author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), + derived_point: message.derived_point.clone().into(), + }))); + } + + // now it is time for keys dissemination (KD) phase + drop(data); + self.complete_initialization(message.derived_point.clone().into())?; + self.disseminate_keys() + } + + /// When session initialization completion message is received. + pub fn on_complete_initialization(&self, sender: NodeId, message: &CompleteInitialization) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(&sender != self.node()); + + // check message + let nodes_ids = message.nodes.keys().cloned().map(Into::into).collect(); + check_cluster_nodes(self.node(), &nodes_ids)?; + check_threshold(message.threshold, &nodes_ids)?; + + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitializationComplete { + return Err(Error::InvalidStateForRequest); + } + if data.master != Some(sender) { + return Err(Error::InvalidMessage); + } + + // remember passed data + data.threshold = Some(message.threshold); + data.derived_point = Some(message.derived_point.clone().into()); + data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); + + // now it is time for keys dissemination (KD) phase + drop(data); + self.disseminate_keys() + } + + /// When keys dissemination message is received. + pub fn on_keys_dissemination(&self, sender: NodeId, message: &KeysDissemination) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + + // simulate failure, if required + if data.simulate_faulty_behaviour { + return Err(Error::Io("simulated error".into())); + } + + // check state + if data.state != SessionState::WaitingForKeysDissemination { + match data.state { + SessionState::WaitingForInitializationComplete => return Err(Error::TooEarlyForRequest), + _ => return Err(Error::InvalidStateForRequest), + } + } + debug_assert!(data.nodes.contains_key(&sender)); + + // check message + let threshold = data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"); + if message.publics.len() != threshold + 1 { + return Err(Error::InvalidMessage); + } + + // update node data + { + let node_data = data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; + if node_data.secret1.is_some() || node_data.secret2.is_some() || node_data.publics.is_some() { + return Err(Error::InvalidStateForRequest); + } + + node_data.secret1 = Some(message.secret1.clone().into()); + node_data.secret2 = Some(message.secret2.clone().into()); + node_data.publics = Some(message.publics.iter().cloned().map(Into::into).collect()); + } + + // check if we have received keys from every other node + if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && (node_data.publics.is_none() || node_data.secret1.is_none() || node_data.secret2.is_none())) { + return Ok(()) + } + + drop(data); + self.verify_keys() + } + + /// When public key share is received. + pub fn on_public_key_share(&self, sender: NodeId, message: &PublicKeyShare) -> Result<(), Error> { + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForPublicKeyShare { + match data.state { + SessionState::WaitingForInitializationComplete | + SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), + _ => return Err(Error::InvalidStateForRequest), + } + } + + // update node data with received public share + { + let node_data = &mut data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; + if node_data.public_share.is_some() { + return Err(Error::InvalidMessage); + } + + node_data.public_share = Some(message.public_share.clone().into()); + } + + // if there's also nodes, which has not sent us their public shares - do nothing + if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && node_data.public_share.is_none()) { + return Ok(()); + } + + drop(data); + self.complete_generation() + } + + /// When session completion message is received. + pub fn on_session_completed(&self, sender: NodeId, message: &SessionCompleted) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + debug_assert!(data.nodes.contains_key(&sender)); + + // check state + if data.state != SessionState::WaitingForGenerationConfirmation { + match data.state { + SessionState::WaitingForPublicKeyShare => return Err(Error::TooEarlyForRequest), + _ => return Err(Error::InvalidStateForRequest), + } + } + + // if we are not masters, save result and respond with confirmation + if data.master.as_ref() != Some(self.node()) { + // check that we have received message from master + if data.master.as_ref() != Some(&sender) { + return Err(Error::InvalidMessage); + } + + // save encrypted data to key storage + let encrypted_data = DocumentKeyShare { + author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), + threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), + id_numbers: data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), + secret_share: data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), + common_point: None, + encrypted_point: None, + }; + self.key_storage.insert(self.id.clone(), encrypted_data.clone()) + .map_err(|e| Error::KeyStorage(e.into()))?; + + // then respond with confirmation + data.state = SessionState::Finished; + return self.cluster.send(&sender, Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { + session: self.id.clone().into(), + }))); + } + + // remember that we have received confirmation from sender node + { + let sender_node = data.nodes.get_mut(&sender).expect("node is always qualified by himself; qed"); + if sender_node.completion_confirmed { + return Err(Error::InvalidMessage); + } + + sender_node.completion_confirmed = true; + } + + // check if we have received confirmations from all cluster nodes + if data.nodes.iter().any(|(_, node_data)| !node_data.completion_confirmed) { + return Ok(()) + } + + // we have received enough confirmations => complete session + data.state = SessionState::Finished; + self.completed.notify_all(); + + Ok(()) + } + + /// When error has occured on another node. + pub fn on_session_error(&self, sender: NodeId, message: &SessionError) -> Result<(), Error> { + let mut data = self.data.lock(); + + warn!("{}: generation session failed with error: {} from {}", self.node(), message.error, sender); + + data.state = SessionState::Failed; + data.joint_public = Some(Err(Error::Io(message.error.clone()))); + self.completed.notify_all(); + + Ok(()) + } + + /// When connection to one of cluster nodes has timeouted. + pub fn on_node_timeout(&self, node: &NodeId) { + let mut data = self.data.lock(); + + // all nodes are required for generation session + // => fail without check + warn!("{}: generation session failed because {} connection has timeouted", self.node(), node); + + data.state = SessionState::Failed; + data.joint_public = Some(Err(Error::NodeDisconnected)); + self.completed.notify_all(); + } + + /// When session timeout has occured. + pub fn on_session_timeout(&self) { + let mut data = self.data.lock(); + + warn!("{}: generation session failed with timeout", self.node()); + + data.state = SessionState::Failed; + data.joint_public = Some(Err(Error::NodeDisconnected)); + self.completed.notify_all(); + } + + /// Complete initialization (when all other nodex has responded with confirmation) + fn complete_initialization(&self, mut derived_point: Public) -> Result<(), Error> { + // update point once again to make sure that derived point is not generated by last node + math::update_random_point(&mut derived_point)?; + + // remember derived point + let mut data = self.data.lock(); + data.derived_point = Some(derived_point.clone().into()); + + // broadcast derived point && other session paraeters to every other node + self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization { + session: self.id.clone().into(), + nodes: data.nodes.iter().map(|(id, data)| (id.clone().into(), data.id_number.clone().into())).collect(), + threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), + derived_point: derived_point.into(), + }))) + } + + /// Keys dissemination (KD) phase + fn disseminate_keys(&self) -> Result<(), Error> { + let mut data = self.data.lock(); + + // pick 2t + 2 random numbers as polynomial coefficients for 2 polynoms + let threshold = data.threshold.expect("threshold is filled on initialization phase; KD phase follows initialization phase; qed"); + let polynom1 = math::generate_random_polynom(threshold)?; + let polynom2 = math::generate_random_polynom(threshold)?; + data.secret_coeff = Some(polynom1[0].clone()); + + // compute t+1 public values + let publics = math::public_values_generation(threshold, + data.derived_point.as_ref().expect("keys dissemination occurs after derived point is agreed; qed"), + &polynom1, + &polynom2)?; + + // compute secret values for every other node + for (node, node_data) in data.nodes.iter_mut() { + let secret1 = math::compute_polynom(&polynom1, &node_data.id_number)?; + let secret2 = math::compute_polynom(&polynom2, &node_data.id_number)?; + + // send a message containing secret1 && secret2 to other node + if node != self.node() { + node_data.secret1_sent = Some(secret1.clone()); + node_data.secret2_sent = Some(secret2.clone()); + + self.cluster.send(&node, Message::Generation(GenerationMessage::KeysDissemination(KeysDissemination { + session: self.id.clone().into(), + secret1: secret1.into(), + secret2: secret2.into(), + publics: publics.iter().cloned().map(Into::into).collect(), + })))?; + } else { + node_data.secret1 = Some(secret1); + node_data.secret2 = Some(secret2); + node_data.publics = Some(publics.clone()); + } + } + + // update state + data.state = SessionState::WaitingForKeysDissemination; + + Ok(()) + } + + /// Keys verification (KV) phase + fn verify_keys(&self) -> Result<(), Error> { + let mut data = self.data.lock(); + + // key verification (KV) phase: check that other nodes have passed correct secrets + let threshold = data.threshold.expect("threshold is filled in initialization phase; KV phase follows initialization phase; qed"); + let derived_point = data.derived_point.clone().expect("derived point generated on initialization phase; KV phase follows initialization phase; qed"); + let number_id = data.nodes[self.node()].id_number.clone(); + for (_ , node_data) in data.nodes.iter_mut().filter(|&(node_id, _)| node_id != self.node()) { + let secret1 = node_data.secret1.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); + let secret2 = node_data.secret2.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); + let publics = node_data.publics.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); + let is_key_verification_ok = math::keys_verification(threshold, &derived_point, &number_id, + secret1, secret2, publics)?; + + if !is_key_verification_ok { + // node has sent us incorrect values. In original ECDKG protocol we should have sent complaint here. + return Err(Error::InvalidMessage); + } + } + + // calculate public share + let self_public_share = { + let self_secret_coeff = data.secret_coeff.as_ref().expect("secret_coeff is generated on KD phase; KG phase follows KD phase; qed"); + math::compute_public_share(self_secret_coeff)? + }; + + // calculate self secret + public shares + let self_secret_share = { + let secret_values_iter = data.nodes.values() + .map(|n| n.secret1.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); + math::compute_secret_share(secret_values_iter)? + }; + + // update state + data.state = SessionState::WaitingForPublicKeyShare; + data.secret_share = Some(self_secret_share); + let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); + self_node.public_share = Some(self_public_share.clone()); + + // broadcast self public key share + self.cluster.broadcast(Message::Generation(GenerationMessage::PublicKeyShare(PublicKeyShare { + session: self.id.clone().into(), + public_share: self_public_share.into(), + }))) + } + + /// Complete generation + fn complete_generation(&self) -> Result<(), Error> { + let mut data = self.data.lock(); + + // else - calculate joint public key + let joint_public = { + let public_shares = data.nodes.values().map(|n| n.public_share.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); + math::compute_joint_public(public_shares)? + }; + + // if we are at the slave node - wait for session completion + if data.master.as_ref() != Some(self.node()) { + data.joint_public = Some(Ok(joint_public)); + data.state = SessionState::WaitingForGenerationConfirmation; + return Ok(()); + } + + // then save encrypted data to the key storage + let encrypted_data = DocumentKeyShare { + author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), + threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), + id_numbers: data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), + secret_share: data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), + common_point: None, + encrypted_point: None, + }; + self.key_storage.insert(self.id.clone(), encrypted_data.clone()) + .map_err(|e| Error::KeyStorage(e.into()))?; + + // then distribute encrypted data to every other node + self.cluster.broadcast(Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { + session: self.id.clone().into(), + })))?; + + // then wait for confirmation from all other nodes + { + let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); + self_node.completion_confirmed = true; + } + data.joint_public = Some(Ok(joint_public)); + data.state = SessionState::WaitingForGenerationConfirmation; + + Ok(()) + } +} + +impl Session for SessionImpl { + fn state(&self) -> SessionState { + self.data.lock().state.clone() + } + + fn wait(&self, timeout: Option) -> Result { + let mut data = self.data.lock(); + if !data.joint_public.is_some() { + match timeout { + None => self.completed.wait(&mut data), + Some(timeout) => { self.completed.wait_for(&mut data, timeout); }, + } + } + + data.joint_public.as_ref() + .expect("checked above or waited for completed; completed is only signaled when joint_public.is_some(); qed") + .clone() + } + + #[cfg(test)] + fn joint_public_key(&self) -> Option> { + self.data.lock().joint_public.clone() + } +} + +impl EveryOtherNodeVisitor { + pub fn new(self_id: &NodeId, nodes: I) -> Self where I: Iterator { + EveryOtherNodeVisitor { + visited: BTreeSet::new(), + unvisited: nodes.filter(|n| n != self_id).collect(), + in_progress: BTreeSet::new(), + } + } + + pub fn next_node(&mut self) -> Option { + let next_node = self.unvisited.pop_front(); + if let Some(ref next_node) = next_node { + self.in_progress.insert(next_node.clone()); + } + next_node + } + + pub fn mark_visited(&mut self, node: &NodeId) -> bool { + if !self.in_progress.remove(node) { + return false; + } + self.visited.insert(node.clone()) + } +} + +impl NodeData { + fn with_id_number(node_id_number: Secret) -> Self { + NodeData { + id_number: node_id_number, + secret1_sent: None, + secret2_sent: None, + secret1: None, + secret2: None, + publics: None, + public_share: None, + completion_confirmed: false, + } + } +} + +impl Debug for SessionImpl { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + write!(f, "Generation session {} on {}", self.id, self.self_node_id) + } +} + +pub fn check_cluster_nodes(self_node_id: &NodeId, nodes: &BTreeSet) -> Result<(), Error> { + // at least two nodes must be in cluster + if nodes.len() < 1 { + return Err(Error::InvalidNodesCount); + } + // this node must be a part of cluster + if !nodes.contains(self_node_id) { + return Err(Error::InvalidNodesConfiguration); + } + + Ok(()) +} + +pub fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Error> { + // at least threshold + 1 nodes are required to collectively decrypt message + if threshold >= nodes.len() { + return Err(Error::InvalidThreshold); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::time; + use std::sync::Arc; + use std::collections::{BTreeSet, BTreeMap, VecDeque}; + use tokio_core::reactor::Core; + use ethkey::{Random, Generator, Public}; + use key_server_cluster::{NodeId, SessionId, Error, DummyKeyStorage}; + use key_server_cluster::message::{self, Message, GenerationMessage}; + use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; + use key_server_cluster::generation_session::{Session, SessionImpl, SessionState, SessionParams}; + use key_server_cluster::math; + use key_server_cluster::math::tests::do_encryption_and_decryption; + + #[derive(Debug)] + struct Node { + pub cluster: Arc, + pub session: SessionImpl, + } + + #[derive(Debug)] + struct MessageLoop { + pub session_id: SessionId, + pub nodes: BTreeMap, + pub queue: VecDeque<(NodeId, NodeId, Message)>, + } + + impl MessageLoop { + pub fn new(nodes_num: usize) -> Self { + let mut nodes = BTreeMap::new(); + let session_id = SessionId::default(); + for _ in 0..nodes_num { + let key_pair = Random.generate().unwrap(); + let node_id = key_pair.public().clone(); + let cluster = Arc::new(DummyCluster::new(node_id.clone())); + let session = SessionImpl::new(SessionParams { + id: session_id.clone(), + self_node_id: node_id.clone(), + key_storage: Arc::new(DummyKeyStorage::default()), + cluster: cluster.clone(), + }); + nodes.insert(node_id, Node { cluster: cluster, session: session }); + } + + let nodes_ids: Vec<_> = nodes.keys().cloned().collect(); + for node in nodes.values() { + for node_id in &nodes_ids { + node.cluster.add_node(node_id.clone()); + } + } + + MessageLoop { + session_id: session_id, + nodes: nodes, + queue: VecDeque::new(), + } + } + + pub fn master(&self) -> &SessionImpl { + &self.nodes.values().nth(0).unwrap().session + } + + pub fn first_slave(&self) -> &SessionImpl { + &self.nodes.values().nth(1).unwrap().session + } + + pub fn second_slave(&self) -> &SessionImpl { + &self.nodes.values().nth(2).unwrap().session + } + + pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { + self.nodes.values() + .filter_map(|n| n.cluster.take_message().map(|m| (n.session.node().clone(), m.0, m.1))) + .nth(0) + .or_else(|| self.queue.pop_front()) + } + + pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { + match { + match msg.2 { + Message::Generation(GenerationMessage::InitializeSession(ref message)) => self.nodes[&msg.1].session.on_initialize_session(msg.0.clone(), &message), + Message::Generation(GenerationMessage::ConfirmInitialization(ref message)) => self.nodes[&msg.1].session.on_confirm_initialization(msg.0.clone(), &message), + Message::Generation(GenerationMessage::CompleteInitialization(ref message)) => self.nodes[&msg.1].session.on_complete_initialization(msg.0.clone(), &message), + Message::Generation(GenerationMessage::KeysDissemination(ref message)) => self.nodes[&msg.1].session.on_keys_dissemination(msg.0.clone(), &message), + Message::Generation(GenerationMessage::PublicKeyShare(ref message)) => self.nodes[&msg.1].session.on_public_key_share(msg.0.clone(), &message), + Message::Generation(GenerationMessage::SessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), + _ => panic!("unexpected"), + } + } { + Ok(_) => Ok(()), + Err(Error::TooEarlyForRequest) => { + self.queue.push_back(msg); + Ok(()) + }, + Err(err) => Err(err), + } + } + + pub fn take_and_process_message(&mut self) -> Result<(), Error> { + let msg = self.take_message().unwrap(); + self.process_message(msg) + } + } + + fn make_simple_cluster(threshold: usize, num_nodes: usize) -> Result<(SessionId, NodeId, NodeId, MessageLoop), Error> { + let l = MessageLoop::new(num_nodes); + l.master().initialize(Public::default(), threshold, l.nodes.keys().cloned().collect())?; + + let session_id = l.session_id.clone(); + let master_id = l.master().node().clone(); + let slave_id = l.first_slave().node().clone(); + Ok((session_id, master_id, slave_id, l)) + } + + #[test] + fn initializes_in_cluster_of_single_node() { + let l = MessageLoop::new(1); + assert!(l.master().initialize(Public::default(), 0, l.nodes.keys().cloned().collect()).is_ok()); + } + + #[test] + fn fails_to_initialize_if_not_a_part_of_cluster() { + let node_id = math::generate_random_point().unwrap(); + let cluster = Arc::new(DummyCluster::new(node_id.clone())); + let session = SessionImpl::new(SessionParams { + id: SessionId::default(), + self_node_id: node_id.clone(), + key_storage: Arc::new(DummyKeyStorage::default()), + cluster: cluster, + }); + let cluster_nodes: BTreeSet<_> = (0..2).map(|_| math::generate_random_point().unwrap()).collect(); + assert_eq!(session.initialize(Public::default(), 0, cluster_nodes).unwrap_err(), Error::InvalidNodesConfiguration); + } + + #[test] + fn fails_to_initialize_if_threshold_is_wrong() { + assert_eq!(make_simple_cluster(2, 2).unwrap_err(), Error::InvalidThreshold); + } + + #[test] + fn fails_to_initialize_when_already_initialized() { + let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); + assert_eq!(l.master().initialize(Public::default(), 0, l.nodes.keys().cloned().collect()).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn fails_to_accept_initialization_when_already_initialized() { + let (sid, m, _, mut l) = make_simple_cluster(0, 2).unwrap(); + l.take_and_process_message().unwrap(); + assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { + session: sid.into(), + author: Public::default().into(), + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn slave_updates_derived_point_on_initialization() { + let (_, _, _, mut l) = make_simple_cluster(0, 2).unwrap(); + let passed_point = match l.take_message().unwrap() { + (f, t, Message::Generation(GenerationMessage::InitializeSession(message))) => { + let point = message.derived_point.clone(); + l.process_message((f, t, Message::Generation(GenerationMessage::InitializeSession(message)))).unwrap(); + point + }, + _ => panic!("unexpected"), + }; + + match l.take_message().unwrap() { + (_, _, Message::Generation(GenerationMessage::ConfirmInitialization(message))) => assert!(passed_point != message.derived_point), + _ => panic!("unexpected"), + } + } + + #[test] + fn fails_to_accept_initialization_confirmation_if_already_accepted_from_the_same_node() { + let (sid, _, s, mut l) = make_simple_cluster(0, 3).unwrap(); + l.take_and_process_message().unwrap(); + l.take_and_process_message().unwrap(); + l.take_and_process_message().unwrap(); + assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization { + session: sid.into(), + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn fails_to_accept_initialization_confirmation_if_initialization_already_completed() { + let (sid, _, s, mut l) = make_simple_cluster(0, 2).unwrap(); + l.take_and_process_message().unwrap(); + l.take_and_process_message().unwrap(); + assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization { + session: sid.into(), + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn master_updates_derived_point_on_initialization_completion() { + let (_, _, _, mut l) = make_simple_cluster(0, 2).unwrap(); + l.take_and_process_message().unwrap(); + let passed_point = match l.take_message().unwrap() { + (f, t, Message::Generation(GenerationMessage::ConfirmInitialization(message))) => { + let point = message.derived_point.clone(); + l.process_message((f, t, Message::Generation(GenerationMessage::ConfirmInitialization(message)))).unwrap(); + point + }, + _ => panic!("unexpected"), + }; + + assert!(l.master().derived_point().unwrap() != passed_point.into()); + } + + #[test] + fn fails_to_complete_initialization_if_not_a_part_of_cluster() { + let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap(); + let mut nodes = BTreeMap::new(); + nodes.insert(m, math::generate_random_scalar().unwrap()); + nodes.insert(math::generate_random_point().unwrap(), math::generate_random_scalar().unwrap()); + assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { + session: sid.into(), + nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + threshold: 0, + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidNodesConfiguration); + } + + #[test] + fn fails_to_complete_initialization_if_threshold_is_wrong() { + let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); + let mut nodes = BTreeMap::new(); + nodes.insert(m, math::generate_random_scalar().unwrap()); + nodes.insert(s, math::generate_random_scalar().unwrap()); + assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { + session: sid.into(), + nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + threshold: 2, + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidThreshold); + } + + #[test] + fn fails_to_complete_initialization_if_not_waiting_for_it() { + let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); + let mut nodes = BTreeMap::new(); + nodes.insert(m, math::generate_random_scalar().unwrap()); + nodes.insert(s, math::generate_random_scalar().unwrap()); + assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { + session: sid.into(), + nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + threshold: 0, + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn fails_to_complete_initialization_from_non_master_node() { + let (sid, m, s, mut l) = make_simple_cluster(0, 3).unwrap(); + l.take_and_process_message().unwrap(); + l.take_and_process_message().unwrap(); + l.take_and_process_message().unwrap(); + l.take_and_process_message().unwrap(); + let mut nodes = BTreeMap::new(); + nodes.insert(m, math::generate_random_scalar().unwrap()); + nodes.insert(s, math::generate_random_scalar().unwrap()); + nodes.insert(l.second_slave().node().clone(), math::generate_random_scalar().unwrap()); + assert_eq!(l.first_slave().on_complete_initialization(l.second_slave().node().clone(), &message::CompleteInitialization { + session: sid.into(), + nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + threshold: 0, + derived_point: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn fails_to_accept_keys_dissemination_if_not_waiting_for_it() { + let (sid, _, s, l) = make_simple_cluster(0, 2).unwrap(); + assert_eq!(l.master().on_keys_dissemination(s, &message::KeysDissemination { + session: sid.into(), + secret1: math::generate_random_scalar().unwrap().into(), + secret2: math::generate_random_scalar().unwrap().into(), + publics: vec![math::generate_random_point().unwrap().into()], + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn fails_to_accept_keys_dissemination_if_wrong_number_of_publics_passed() { + let (sid, m, _, mut l) = make_simple_cluster(0, 3).unwrap(); + l.take_and_process_message().unwrap(); // m -> s1: InitializeSession + l.take_and_process_message().unwrap(); // m -> s2: InitializeSession + l.take_and_process_message().unwrap(); // s1 -> m: ConfirmInitialization + l.take_and_process_message().unwrap(); // s2 -> m: ConfirmInitialization + l.take_and_process_message().unwrap(); // m -> s1: CompleteInitialization + l.take_and_process_message().unwrap(); // m -> s2: CompleteInitialization + l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination + assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination { + session: sid.into(), + secret1: math::generate_random_scalar().unwrap().into(), + secret2: math::generate_random_scalar().unwrap().into(), + publics: vec![math::generate_random_point().unwrap().into(), math::generate_random_point().unwrap().into()], + }).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn fails_to_accept_keys_dissemination_second_time_from_the_same_node() { + let (sid, m, _, mut l) = make_simple_cluster(0, 3).unwrap(); + l.take_and_process_message().unwrap(); // m -> s1: InitializeSession + l.take_and_process_message().unwrap(); // m -> s2: InitializeSession + l.take_and_process_message().unwrap(); // s1 -> m: ConfirmInitialization + l.take_and_process_message().unwrap(); // s2 -> m: ConfirmInitialization + l.take_and_process_message().unwrap(); // m -> s1: CompleteInitialization + l.take_and_process_message().unwrap(); // m -> s2: CompleteInitialization + l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination + assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination { + session: sid.into(), + secret1: math::generate_random_scalar().unwrap().into(), + secret2: math::generate_random_scalar().unwrap().into(), + publics: vec![math::generate_random_point().unwrap().into()], + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn should_not_accept_public_key_share_when_is_not_waiting_for_it() { + let (sid, _, s, l) = make_simple_cluster(1, 3).unwrap(); + assert_eq!(l.master().on_public_key_share(s, &message::PublicKeyShare { + session: sid.into(), + public_share: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn should_not_accept_public_key_share_when_receiving_twice() { + let (sid, m, _, mut l) = make_simple_cluster(0, 3).unwrap(); + l.take_and_process_message().unwrap(); // m -> s1: InitializeSession + l.take_and_process_message().unwrap(); // m -> s2: InitializeSession + l.take_and_process_message().unwrap(); // s1 -> m: ConfirmInitialization + l.take_and_process_message().unwrap(); // s2 -> m: ConfirmInitialization + l.take_and_process_message().unwrap(); // m -> s1: CompleteInitialization + l.take_and_process_message().unwrap(); // m -> s2: CompleteInitialization + l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination + l.take_and_process_message().unwrap(); // m -> s2: KeysDissemination + l.take_and_process_message().unwrap(); // s1 -> m: KeysDissemination + l.take_and_process_message().unwrap(); // s1 -> s2: KeysDissemination + l.take_and_process_message().unwrap(); // s2 -> m: KeysDissemination + l.take_and_process_message().unwrap(); // s2 -> s1: KeysDissemination + let (f, t, msg) = match l.take_message() { + Some((f, t, Message::Generation(GenerationMessage::PublicKeyShare(msg)))) => (f, t, msg), + _ => panic!("unexpected"), + }; + assert_eq!(&f, l.master().node()); + assert_eq!(&t, l.second_slave().node()); + l.process_message((f, t, Message::Generation(GenerationMessage::PublicKeyShare(msg.clone())))).unwrap(); + assert_eq!(l.second_slave().on_public_key_share(m, &message::PublicKeyShare { + session: sid.into(), + public_share: math::generate_random_point().unwrap().into(), + }).unwrap_err(), Error::InvalidMessage); + } + + + #[test] + fn encryption_fails_on_session_timeout() { + let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); + assert!(l.master().joint_public_key().is_none()); + l.master().on_session_timeout(); + assert!(l.master().joint_public_key().unwrap().unwrap_err() == Error::NodeDisconnected); + } + + #[test] + fn encryption_fails_on_node_timeout() { + let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); + assert!(l.master().joint_public_key().is_none()); + l.master().on_node_timeout(l.first_slave().node()); + assert!(l.master().joint_public_key().unwrap().unwrap_err() == Error::NodeDisconnected); + } + + #[test] + fn complete_enc_dec_session() { + let test_cases = [(0, 5), (2, 5), (3, 5)]; + for &(threshold, num_nodes) in &test_cases { + let mut l = MessageLoop::new(num_nodes); + l.master().initialize(Public::default(), threshold, l.nodes.keys().cloned().collect()).unwrap(); + assert_eq!(l.nodes.len(), num_nodes); + + // let nodes do initialization + keys dissemination + while let Some((from, to, message)) = l.take_message() { + l.process_message((from, to, message)).unwrap(); + } + + // check that all nodes has finished joint public generation + let joint_public_key = l.master().joint_public_key().unwrap().unwrap(); + for node in l.nodes.values() { + let state = node.session.state(); + assert_eq!(state, SessionState::Finished); + assert_eq!(node.session.joint_public_key().as_ref(), Some(&Ok(joint_public_key))); + } + + // now let's encrypt some secret (which is a point on EC) + let document_secret_plain = Random.generate().unwrap().public().clone(); + let all_nodes_id_numbers: Vec<_> = l.master().data.lock().nodes.values().map(|n| n.id_number.clone()).collect(); + let all_nodes_secret_shares: Vec<_> = l.nodes.values().map(|n| n.session.data.lock().secret_share.as_ref().unwrap().clone()).collect(); + let document_secret_decrypted = do_encryption_and_decryption(threshold, &joint_public_key, + &all_nodes_id_numbers, + &all_nodes_secret_shares, + None, + document_secret_plain.clone() + ).0; + assert_eq!(document_secret_plain, document_secret_decrypted); + } + } + + #[test] + fn encryption_session_works_over_network() { + //::util::log::init_log(); + + let test_cases = [(1, 3)]; + for &(threshold, num_nodes) in &test_cases { + let mut core = Core::new().unwrap(); + + // prepare cluster objects for each node + let clusters = make_clusters(&core, 6022, num_nodes); + run_clusters(&clusters); + + // establish connections + loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + + // run session to completion + let session_id = SessionId::default(); + let session = clusters[0].client().new_generation_session(session_id, Public::default(), threshold).unwrap(); + loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_key().is_some()); + } + } +} diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index 95d3a54cb30..b318e1c9584 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -25,7 +25,7 @@ use ethkey::{Public, Secret, KeyPair}; use ethkey::math::curve_order; use util::{H256, U256}; use key_server_cluster::Error; -use key_server_cluster::message::{Message, ClusterMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage}; /// Size of serialized header. pub const MESSAGE_HEADER_SIZE: usize = 4; @@ -67,20 +67,24 @@ pub fn serialize_message(message: Message) -> Result { Message::Cluster(ClusterMessage::KeepAlive(payload)) => (3, serde_json::to_vec(&payload)), Message::Cluster(ClusterMessage::KeepAliveResponse(payload)) => (4, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::InitializeSession(payload)) => (50, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::ConfirmInitialization(payload)) => (51, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::CompleteInitialization(payload)) => (52, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::KeysDissemination(payload)) => (53, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::PublicKeyShare(payload)) => (54, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::SessionError(payload)) => (55, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::SessionCompleted(payload)) => (56, serde_json::to_vec(&payload)), - - Message::Decryption(DecryptionMessage::InitializeDecryptionSession(payload)) => (100, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(payload)) => (101, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::RequestPartialDecryption(payload)) => (102, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (103, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (104, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (105, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::InitializeSession(payload)) => (50, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::ConfirmInitialization(payload)) => (51, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::CompleteInitialization(payload)) => (52, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::KeysDissemination(payload)) => (53, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::PublicKeyShare(payload)) => (54, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::SessionError(payload)) => (55, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::SessionCompleted(payload)) => (56, serde_json::to_vec(&payload)), + + Message::Encryption(EncryptionMessage::InitializeEncryptionSession(payload)) => (100, serde_json::to_vec(&payload)), + Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(payload)) => (101, serde_json::to_vec(&payload)), + Message::Encryption(EncryptionMessage::EncryptionSessionError(payload)) => (102, serde_json::to_vec(&payload)), + + Message::Decryption(DecryptionMessage::InitializeDecryptionSession(payload)) => (150, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(payload)) => (151, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::RequestPartialDecryption(payload)) => (152, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (153, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (154, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (155, serde_json::to_vec(&payload)), }; let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; @@ -99,20 +103,24 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::Cluster(ClusterMessage::KeepAlive(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 4 => Message::Cluster(ClusterMessage::KeepAliveResponse(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 50 => Message::Encryption(EncryptionMessage::InitializeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 51 => Message::Encryption(EncryptionMessage::ConfirmInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 52 => Message::Encryption(EncryptionMessage::CompleteInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 53 => Message::Encryption(EncryptionMessage::KeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 54 => Message::Encryption(EncryptionMessage::PublicKeyShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 55 => Message::Encryption(EncryptionMessage::SessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 56 => Message::Encryption(EncryptionMessage::SessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 100 => Message::Decryption(DecryptionMessage::InitializeDecryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 101 => Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 102 => Message::Decryption(DecryptionMessage::RequestPartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 103 => Message::Decryption(DecryptionMessage::PartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 104 => Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 105 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 50 => Message::Generation(GenerationMessage::InitializeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 51 => Message::Generation(GenerationMessage::ConfirmInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 52 => Message::Generation(GenerationMessage::CompleteInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 53 => Message::Generation(GenerationMessage::KeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 54 => Message::Generation(GenerationMessage::PublicKeyShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 55 => Message::Generation(GenerationMessage::SessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 56 => Message::Generation(GenerationMessage::SessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + + 100 => Message::Encryption(EncryptionMessage::InitializeEncryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 101 => Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 102 => Message::Encryption(EncryptionMessage::EncryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + + 150 => Message::Decryption(DecryptionMessage::InitializeDecryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 151 => Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 152 => Message::Decryption(DecryptionMessage::RequestPartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 153 => Message::Decryption(DecryptionMessage::PartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 154 => Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 155 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), _ => return Err(Error::Serde(format!("unknown message type {}", header.kind))), }) diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index af3a113fe8b..f59edc1e51e 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -28,6 +28,8 @@ pub type MessageNodeId = SerializablePublic; pub enum Message { /// Cluster message. Cluster(ClusterMessage), + /// Key generation message. + Generation(GenerationMessage), /// Encryption message. Encryption(EncryptionMessage), /// Decryption message. @@ -48,8 +50,8 @@ pub enum ClusterMessage { } #[derive(Clone, Debug)] -/// All possible messages that can be sent during encryption session. -pub enum EncryptionMessage { +/// All possible messages that can be sent during key generation session. +pub enum GenerationMessage { /// Initialize new DKG session. InitializeSession(InitializeSession), /// Confirm DKG session initialization. @@ -66,6 +68,17 @@ pub enum EncryptionMessage { SessionCompleted(SessionCompleted), } +#[derive(Clone, Debug)] +/// All possible messages that can be sent during encryption session. +pub enum EncryptionMessage { + /// Initialize encryption session. + InitializeEncryptionSession(InitializeEncryptionSession), + /// Confirm/reject encryption session initialization. + ConfirmEncryptionInitialization(ConfirmEncryptionInitialization), + /// When encryption session error has occured. + EncryptionSessionError(EncryptionSessionError), +} + #[derive(Clone, Debug)] /// All possible messages that can be sent during decryption session. pub enum DecryptionMessage { @@ -115,6 +128,8 @@ pub struct KeepAliveResponse { pub struct InitializeSession { /// Session Id. pub session: MessageSessionId, + /// Session author. + pub author: SerializablePublic, /// Derived generation point. Starting from originator, every node must multiply this /// point by random scalar (unknown by other nodes). At the end of initialization /// `point` will be some (k1 * k2 * ... * kn) * G = `point` where `(k1 * k2 * ... * kn)` @@ -181,12 +196,37 @@ pub struct SessionError { pub struct SessionCompleted { /// Session Id. pub session: MessageSessionId, - /// Common (shared) encryption point. +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Node is requested to prepare for saving encrypted data. +pub struct InitializeEncryptionSession { + /// Encryption session Id. + pub session: MessageSessionId, + /// Requestor signature. + pub requestor_signature: SerializableSignature, + /// Common point. pub common_point: SerializablePublic, - /// Encrypted point. + /// Encrypted data. pub encrypted_point: SerializablePublic, } +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Node is responding to encryption initialization request. +pub struct ConfirmEncryptionInitialization { + /// Encryption session Id. + pub session: MessageSessionId, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +/// When encryption session error has occured. +pub struct EncryptionSessionError { + /// Encryption session Id. + pub session: MessageSessionId, + /// Error message. + pub error: String, +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Node is requested to decrypt data, encrypted in given session. pub struct InitializeDecryptionSession { @@ -256,16 +296,26 @@ pub struct DecryptionSessionCompleted { pub sub_session: SerializableSecret, } +impl GenerationMessage { + pub fn session_id(&self) -> &SessionId { + match *self { + GenerationMessage::InitializeSession(ref msg) => &msg.session, + GenerationMessage::ConfirmInitialization(ref msg) => &msg.session, + GenerationMessage::CompleteInitialization(ref msg) => &msg.session, + GenerationMessage::KeysDissemination(ref msg) => &msg.session, + GenerationMessage::PublicKeyShare(ref msg) => &msg.session, + GenerationMessage::SessionError(ref msg) => &msg.session, + GenerationMessage::SessionCompleted(ref msg) => &msg.session, + } + } +} + impl EncryptionMessage { pub fn session_id(&self) -> &SessionId { match *self { - EncryptionMessage::InitializeSession(ref msg) => &msg.session, - EncryptionMessage::ConfirmInitialization(ref msg) => &msg.session, - EncryptionMessage::CompleteInitialization(ref msg) => &msg.session, - EncryptionMessage::KeysDissemination(ref msg) => &msg.session, - EncryptionMessage::PublicKeyShare(ref msg) => &msg.session, - EncryptionMessage::SessionError(ref msg) => &msg.session, - EncryptionMessage::SessionCompleted(ref msg) => &msg.session, + EncryptionMessage::InitializeEncryptionSession(ref msg) => &msg.session, + EncryptionMessage::ConfirmEncryptionInitialization(ref msg) => &msg.session, + EncryptionMessage::EncryptionSessionError(ref msg) => &msg.session, } } } @@ -298,6 +348,7 @@ impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Message::Cluster(ref message) => write!(f, "Cluster.{}", message), + Message::Generation(ref message) => write!(f, "Generation.{}", message), Message::Encryption(ref message) => write!(f, "Encryption.{}", message), Message::Decryption(ref message) => write!(f, "Decryption.{}", message), } @@ -315,16 +366,26 @@ impl fmt::Display for ClusterMessage { } } +impl fmt::Display for GenerationMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + GenerationMessage::InitializeSession(_) => write!(f, "InitializeSession"), + GenerationMessage::ConfirmInitialization(_) => write!(f, "ConfirmInitialization"), + GenerationMessage::CompleteInitialization(_) => write!(f, "CompleteInitialization"), + GenerationMessage::KeysDissemination(_) => write!(f, "KeysDissemination"), + GenerationMessage::PublicKeyShare(_) => write!(f, "PublicKeyShare"), + GenerationMessage::SessionError(ref msg) => write!(f, "SessionError({})", msg.error), + GenerationMessage::SessionCompleted(_) => write!(f, "SessionCompleted"), + } + } +} + impl fmt::Display for EncryptionMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - EncryptionMessage::InitializeSession(_) => write!(f, "InitializeSession"), - EncryptionMessage::ConfirmInitialization(_) => write!(f, "ConfirmInitialization"), - EncryptionMessage::CompleteInitialization(_) => write!(f, "CompleteInitialization"), - EncryptionMessage::KeysDissemination(_) => write!(f, "KeysDissemination"), - EncryptionMessage::PublicKeyShare(_) => write!(f, "PublicKeyShare"), - EncryptionMessage::SessionError(ref msg) => write!(f, "SessionError({})", msg.error), - EncryptionMessage::SessionCompleted(_) => write!(f, "SessionCompleted"), + EncryptionMessage::InitializeEncryptionSession(_) => write!(f, "InitializeEncryptionSession"), + EncryptionMessage::ConfirmEncryptionInitialization(_) => write!(f, "ConfirmEncryptionInitialization"), + EncryptionMessage::EncryptionSessionError(ref msg) => write!(f, "EncryptionSessionError({})", msg.error), } } } diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 5aa1b0fc0c6..56f5d9194ec 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -25,6 +25,7 @@ pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare}; pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic}; pub use self::cluster::{ClusterCore, ClusterConfiguration, ClusterClient}; +pub use self::generation_session::Session as GenerationSession; pub use self::encryption_session::Session as EncryptionSession; pub use self::decryption_session::Session as DecryptionSession; @@ -44,6 +45,10 @@ pub enum Error { InvalidNodeId, /// Session with the given id already exists. DuplicateSessionId, + /// Session with the same id already completed. + CompletedSessionId, + /// Session is not ready to start yet (required data is not ready). + NotStartedSessionId, /// Session with the given id is unknown. InvalidSessionId, /// Invalid number of nodes. @@ -102,6 +107,8 @@ impl fmt::Display for Error { Error::InvalidNodeAddress => write!(f, "invalid node address has been passed"), Error::InvalidNodeId => write!(f, "invalid node id has been passed"), Error::DuplicateSessionId => write!(f, "session with the same id is already registered"), + Error::CompletedSessionId => write!(f, "session with the same id is already completed"), + Error::NotStartedSessionId => write!(f, "not enough data to start session with the given id"), Error::InvalidSessionId => write!(f, "invalid session id has been passed"), Error::InvalidNodesCount => write!(f, "invalid nodes count"), Error::InvalidNodesConfiguration => write!(f, "invalid nodes configuration"), @@ -128,7 +135,8 @@ impl Into for Error { mod cluster; mod decryption_session; mod encryption_session; +mod generation_session; mod io; -mod math; +pub mod math; mod message; mod net; diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index f1d95bdd9e0..f150ad7356b 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -25,6 +25,8 @@ use serialization::{SerializablePublic, SerializableSecret}; #[derive(Debug, Clone, PartialEq)] /// Encrypted key share, stored by key storage on the single key server. pub struct DocumentKeyShare { + /// Author of the entry. + pub author: Public, /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). pub threshold: usize, /// Nodes ids numbers. @@ -32,15 +34,17 @@ pub struct DocumentKeyShare { /// Node secret share. pub secret_share: Secret, /// Common (shared) encryption point. - pub common_point: Public, + pub common_point: Option, /// Encrypted point. - pub encrypted_point: Public, + pub encrypted_point: Option, } /// Document encryption keys storage pub trait KeyStorage: Send + Sync { /// Insert document encryption key fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; + /// Update document encryption key + fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; /// Get document encryption key fn get(&self, document: &ServerKeyId) -> Result; /// Check if storage contains document encryption key @@ -55,6 +59,8 @@ pub struct PersistentKeyStorage { #[derive(Serialize, Deserialize)] /// Encrypted key share, as it is stored by key storage on the single key server. struct SerializableDocumentKeyShare { + /// Authore of the entry. + pub author: SerializablePublic, /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). pub threshold: usize, /// Nodes ids numbers. @@ -89,6 +95,10 @@ impl KeyStorage for PersistentKeyStorage { self.db.write(batch).map_err(Error::Database) } + fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { + self.insert(document, key) + } + fn get(&self, document: &ServerKeyId) -> Result { self.db.get(None, document) .map_err(Error::Database)? @@ -108,11 +118,18 @@ impl KeyStorage for PersistentKeyStorage { impl From for SerializableDocumentKeyShare { fn from(key: DocumentKeyShare) -> Self { SerializableDocumentKeyShare { + author: key.author.into(), threshold: key.threshold, id_numbers: key.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), secret_share: key.secret_share.into(), - common_point: key.common_point.into(), - encrypted_point: key.encrypted_point.into(), + common_point: match key.common_point { + Some(common_point) => common_point.into(), + None => Public::default().into(), + }, + encrypted_point: match key.encrypted_point { + Some(encrypted_point) => encrypted_point.into(), + None => Public::default().into(), + }, } } } @@ -120,11 +137,26 @@ impl From for SerializableDocumentKeyShare { impl From for DocumentKeyShare { fn from(key: SerializableDocumentKeyShare) -> Self { DocumentKeyShare { + author: key.author.into(), threshold: key.threshold, id_numbers: key.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), secret_share: key.secret_share.into(), - common_point: key.common_point.into(), - encrypted_point: key.encrypted_point.into(), + common_point: { + let common_point = key.common_point.into(); + if common_point == Public::default() { + None + } else { + Some(common_point) + } + }, + encrypted_point: { + let encrypted_point = key.encrypted_point.into(); + if encrypted_point == Public::default() { + None + } else { + Some(encrypted_point) + } + }, } } } @@ -134,7 +166,7 @@ pub mod tests { use std::collections::{BTreeMap, HashMap}; use parking_lot::RwLock; use devtools::RandomTempPath; - use ethkey::{Random, Generator}; + use ethkey::{Random, Generator, Public}; use super::super::types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare}; @@ -150,6 +182,11 @@ pub mod tests { Ok(()) } + fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { + self.keys.write().insert(document, key); + Ok(()) + } + fn get(&self, document: &ServerKeyId) -> Result { self.keys.read().get(document).cloned().ok_or(Error::DocumentNotFound) } @@ -182,23 +219,25 @@ pub mod tests { let key1 = ServerKeyId::from(1); let value1 = DocumentKeyShare { + author: Public::default(), threshold: 100, id_numbers: vec![ (Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()) ].into_iter().collect(), secret_share: Random.generate().unwrap().secret().clone(), - common_point: Random.generate().unwrap().public().clone(), - encrypted_point: Random.generate().unwrap().public().clone(), + common_point: Some(Random.generate().unwrap().public().clone()), + encrypted_point: Some(Random.generate().unwrap().public().clone()), }; let key2 = ServerKeyId::from(2); let value2 = DocumentKeyShare { + author: Public::default(), threshold: 200, id_numbers: vec![ (Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()) ].into_iter().collect(), secret_share: Random.generate().unwrap().secret().clone(), - common_point: Random.generate().unwrap().public().clone(), - encrypted_point: Random.generate().unwrap().public().clone(), + common_point: Some(Random.generate().unwrap().public().clone()), + encrypted_point: Some(Random.generate().unwrap().public().clone()), }; let key3 = ServerKeyId::from(3); diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index ee70b107462..27828760f4e 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -32,8 +32,10 @@ pub trait DocumentKeyServer: ServerKeyGenerator { /// Store externally generated DK. /// `key_id` is identifier of previously generated SK. /// `signature` is key_id, signed with caller public key. Caller must be the same as in the `generate_key` call. - /// `document_key` is an externally-generated DK, encrypted with SK with `key_id` identifier. - fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, document_key: EncryptedDocumentKey) -> Result<(), Error>; + /// `common_point` is a result of `k * T` expression, where `T` is generation point and `k` is random scalar in EC field. + /// `encrypted_document_key` is a result of `M + k * y` expression, where `M` is unencrypted document key (point on EC), + /// `k` is the same scalar used in `common_point` calculation and `y` is previously generated public part of SK. + fn store_document_key(&self, key_id: &ServerKeyId, signature: &RequestSignature, common_point: Public, encrypted_document_key: Public) -> Result<(), Error>; /// Generate and store both SK and DK. This is a shortcut for consequent calls of `generate_key` and `store_document_key`. /// The only difference is that DK is generated by DocumentKeyServer (which might be considered unsafe). /// `key_id` is the caller-provided identifier of generated SK. From 68cfc483f3ab73d88e0b5300f55c8154c1b3f2e4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 11 May 2017 17:21:12 +0300 Subject: [PATCH 03/45] generalized ClusterSessions --- .../src/key_server_cluster/cluster.rs | 415 +----------- .../key_server_cluster/cluster_sessions.rs | 615 ++++++++++++++++++ .../key_server_cluster/decryption_session.rs | 128 ++-- .../key_server_cluster/encryption_session.rs | 15 +- .../key_server_cluster/generation_session.rs | 56 +- secret_store/src/key_server_cluster/mod.rs | 1 + 6 files changed, 752 insertions(+), 478 deletions(-) create mode 100644 secret_store/src/key_server_cluster/cluster_sessions.rs diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 4ee12d47ab9..24b83e0fa2a 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -17,8 +17,7 @@ use std::io; use std::time; use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use std::collections::{BTreeMap, BTreeSet}; use std::collections::btree_map::Entry; use std::net::{SocketAddr, IpAddr}; use futures::{finished, failed, Future, Stream, BoxFuture}; @@ -27,15 +26,15 @@ use parking_lot::{RwLock, Mutex}; use tokio_io::IoFuture; use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; -use ethkey::{Public, Secret, KeyPair, Signature, Random, Generator}; +use ethkey::{Public, KeyPair, Signature, Random, Generator}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; +use key_server_cluster::cluster_sessions::ClusterSessions; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionState as GenerationSessionState, - SessionParams as GenerationSessionParams, Session as GenerationSession}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, SessionState as DecryptionSessionState, - SessionParams as DecryptionSessionParams, Session as DecryptionSession, DecryptionSessionId}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionState as EncryptionSessionState, - SessionParams as EncryptionSessionParams, Session as EncryptionSession}; +use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; +#[cfg(test)] +use key_server_cluster::generation_session::SessionImpl as GenerationSessionImpl; +use key_server_cluster::decryption_session::{Session as DecryptionSession, SessionState as DecryptionSessionState, DecryptionSessionId}; +use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionState as EncryptionSessionState}; use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, read_encrypted_message, WriteMessage, write_encrypted_message}; use key_server_cluster::net::{accept_connection as net_accept_connection, connect as net_connect, Connection as NetConnection}; @@ -52,18 +51,6 @@ const KEEP_ALIVE_SEND_INTERVAL: u64 = 30; /// we must treat this node as non-responding && disconnect from it. const KEEP_ALIVE_DISCONNECT_INTERVAL: u64 = 60; -/// When there are no encryption session-related messages for ENCRYPTION_SESSION_TIMEOUT_INTERVAL seconds, -/// we must treat this session as stalled && finish it with an error. -/// This timeout is for cases when node is responding to KeepAlive messages, but intentionally ignores -/// session messages. -const ENCRYPTION_SESSION_TIMEOUT_INTERVAL: u64 = 60; - -/// When there are no decryption session-related messages for DECRYPTION_SESSION_TIMEOUT_INTERVAL seconds, -/// we must treat this session as stalled && finish it with an error. -/// This timeout is for cases when node is responding to KeepAlive messages, but intentionally ignores -/// session messages. -const DECRYPTION_SESSION_TIMEOUT_INTERVAL: u64 = 60; - /// Encryption sesion timeout interval. It works /// Empty future. type BoxedEmptyFuture = BoxFuture<(), ()>; @@ -170,68 +157,6 @@ pub struct ClusterConnections { pub connections: RwLock>>, } -/// Active sessions on this cluster. -pub struct ClusterSessions { - /// Self node id. - pub self_node_id: NodeId, - /// All nodes ids. - pub nodes: BTreeSet, - /// Reference to key storage - pub key_storage: Arc, - /// Reference to ACL storage - pub acl_storage: Arc, - /// Active generation sessions. - pub generation_sessions: RwLock>, - /// Active encryption sessions. - pub encryption_sessions: RwLock>, - /// Active decryption sessions. - pub decryption_sessions: RwLock>, - /// Make faulty generation sessions. - pub make_faulty_generation_sessions: AtomicBool, -} - -/// Generation session and its message queue. -pub struct QueuedGenerationSession { - /// Session master. - pub master: NodeId, - /// Cluster view. - pub cluster_view: Arc, - /// Last received message time. - pub last_message_time: time::Instant, - /// Generation session. - pub session: Arc, - /// Messages queue. - pub queue: VecDeque<(NodeId, GenerationMessage)>, -} - -/// Encryption session and its message queue. -pub struct QueuedEncryptionSession { - /// Session master. - pub master: NodeId, - /// Cluster view. - pub cluster_view: Arc, - /// Last received message time. - pub last_message_time: time::Instant, - /// Encryption session. - pub session: Arc, - /// Messages queue. - pub queue: VecDeque<(NodeId, EncryptionMessage)>, -} - -/// Decryption session and its message queue. -pub struct QueuedDecryptionSession { - /// Session master. - pub master: NodeId, - /// Cluster view. - pub cluster_view: Arc, - /// Last received message time. - pub last_message_time: time::Instant, - /// Decryption session. - pub session: Arc, - /// Messages queue. - pub queue: VecDeque<(NodeId, DecryptionMessage)>, -} - /// Cluster view core. struct ClusterViewCore { /// Cluster reference. @@ -479,7 +404,7 @@ impl ClusterCore { data.sessions.new_generation_session(sender.clone(), session_id.clone(), cluster) }, _ => { - data.sessions.generation_session(&session_id) + data.sessions.generation_sessions.get(&session_id) .ok_or(Error::InvalidSessionId) }, }; @@ -510,12 +435,12 @@ impl ClusterCore { info!(target: "secretstore_net", "{}: generation session completed", data.self_key_pair.public()); } if session_state == GenerationSessionState::Finished || session_state == GenerationSessionState::Failed { - data.sessions.remove_generation_session(&session_id); + data.sessions.generation_sessions.remove(&session_id); break; } // try to dequeue message - match data.sessions.dequeue_generation_message(&session_id) { + match data.sessions.generation_sessions.dequeue_message(&session_id) { Some((msg_sender, msg)) => { is_queued_message = true; sender = msg_sender; @@ -525,7 +450,7 @@ impl ClusterCore { } }, Err(Error::TooEarlyForRequest) => { - data.sessions.enqueue_generation_message(&session_id, sender, message, is_queued_message); + data.sessions.generation_sessions.enqueue_message(&session_id, sender, message, is_queued_message); break; }, Err(err) => { @@ -535,7 +460,7 @@ impl ClusterCore { error: format!("{:?}", err), }); if err != Error::InvalidSessionId { - data.sessions.remove_generation_session(&session_id); + data.sessions.generation_sessions.remove(&session_id); } break; }, @@ -556,7 +481,7 @@ impl ClusterCore { data.sessions.new_encryption_session(sender.clone(), session_id.clone(), cluster) }, _ => { - data.sessions.encryption_session(&session_id) + data.sessions.encryption_sessions.get(&session_id) .ok_or(Error::InvalidSessionId) }, }; @@ -579,12 +504,12 @@ impl ClusterCore { info!(target: "secretstore_net", "{}: encryption session completed", data.self_key_pair.public()); } if session_state == EncryptionSessionState::Finished || session_state == EncryptionSessionState::Failed { - data.sessions.remove_encryption_session(&session_id); + data.sessions.encryption_sessions.remove(&session_id); break; } // try to dequeue message - match data.sessions.dequeue_encryption_message(&session_id) { + match data.sessions.encryption_sessions.dequeue_message(&session_id) { Some((msg_sender, msg)) => { is_queued_message = true; sender = msg_sender; @@ -594,7 +519,7 @@ impl ClusterCore { } }, Err(Error::TooEarlyForRequest) => { - data.sessions.enqueue_encryption_message(&session_id, sender, message, is_queued_message); + data.sessions.encryption_sessions.enqueue_message(&session_id, sender, message, is_queued_message); break; }, Err(err) => { @@ -604,7 +529,7 @@ impl ClusterCore { error: format!("{:?}", err), }); if err != Error::InvalidSessionId { - data.sessions.remove_encryption_session(&session_id); + data.sessions.encryption_sessions.remove(&session_id); } break; }, @@ -616,6 +541,7 @@ impl ClusterCore { fn process_decryption_message(data: Arc, connection: Arc, mut message: DecryptionMessage) { let session_id = message.session_id().clone(); let sub_session_id = message.sub_session_id().clone(); + let decryption_session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); let mut sender = connection.node_id().clone(); let session = match message { DecryptionMessage::InitializeDecryptionSession(_) => { @@ -626,7 +552,7 @@ impl ClusterCore { data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster) }, _ => { - data.sessions.decryption_session(&session_id, &sub_session_id) + data.sessions.decryption_sessions.get(&decryption_session_id) .ok_or(Error::InvalidSessionId) }, }; @@ -655,12 +581,12 @@ impl ClusterCore { info!(target: "secretstore_net", "{}: decryption session completed", data.self_key_pair.public()); } if session_state == DecryptionSessionState::Finished || session_state == DecryptionSessionState::Failed { - data.sessions.remove_decryption_session(&session_id, &sub_session_id); + data.sessions.decryption_sessions.remove(&decryption_session_id); break; } // try to dequeue message - match data.sessions.dequeue_decryption_message(&session_id, &sub_session_id) { + match data.sessions.decryption_sessions.dequeue_message(&decryption_session_id) { Some((msg_sender, msg)) => { is_queued_message = true; sender = msg_sender; @@ -670,7 +596,7 @@ impl ClusterCore { } }, Err(Error::TooEarlyForRequest) => { - data.sessions.enqueue_decryption_message(&session_id, &sub_session_id, sender, message, is_queued_message); + data.sessions.decryption_sessions.enqueue_message(&decryption_session_id, sender, message, is_queued_message); break; }, Err(err) => { @@ -681,7 +607,7 @@ impl ClusterCore { error: format!("{:?}", err), }); if err != Error::InvalidSessionId { - data.sessions.remove_decryption_session(&session_id, &sub_session_id); + data.sessions.decryption_sessions.remove(&decryption_session_id); } break; }, @@ -770,297 +696,6 @@ impl ClusterConnections { } } -impl ClusterSessions { - pub fn new(config: &ClusterConfiguration) -> Self { - ClusterSessions { - self_node_id: config.self_key_pair.public().clone(), - nodes: config.nodes.keys().cloned().collect(), - acl_storage: config.acl_storage.clone(), - key_storage: config.key_storage.clone(), - generation_sessions: RwLock::new(BTreeMap::new()), - encryption_sessions: RwLock::new(BTreeMap::new()), - decryption_sessions: RwLock::new(BTreeMap::new()), - make_faulty_generation_sessions: AtomicBool::new(false), - } - } - - pub fn new_generation_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - let mut generation_sessions = self.generation_sessions.write(); - // check that there's no active encryption session with the same id - if generation_sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - // check that there's no finished encryption session with the same id - if self.key_storage.contains(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // communicating to all other nodes is crucial for encryption session - // => check that we have connections to all cluster nodes - if self.nodes.iter().any(|n| !cluster.is_connected(n)) { - return Err(Error::NodeDisconnected); - } - - let session = Arc::new(GenerationSessionImpl::new(GenerationSessionParams { - id: session_id.clone(), - self_node_id: self.self_node_id.clone(), - key_storage: self.key_storage.clone(), - cluster: cluster.clone(), - })); - let generation_session = QueuedGenerationSession { - master: master, - cluster_view: cluster, - last_message_time: time::Instant::now(), - session: session.clone(), - queue: VecDeque::new() - }; - if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { - generation_session.session.simulate_faulty_behaviour(); - } - generation_sessions.insert(session_id, generation_session); - Ok(session) - } - - pub fn remove_generation_session(&self, session_id: &SessionId) { - self.generation_sessions.write().remove(session_id); - } - - pub fn generation_session(&self, session_id: &SessionId) -> Option> { - self.generation_sessions.read().get(session_id).map(|s| s.session.clone()) - } - - pub fn enqueue_generation_message(&self, session_id: &SessionId, sender: NodeId, message: GenerationMessage, is_queued_message: bool) { - self.generation_sessions.write().get_mut(session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_generation_message(&self, session_id: &SessionId) -> Option<(NodeId, GenerationMessage)> { - self.generation_sessions.write().get_mut(session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { - self.generation_sessions.read().get(session_id) - .map(|s| { - // error in generation session is considered fatal - // => broadcast error - - // do not bother processing send error, as we already processing error - let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); - }); - } - - pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - let mut encryption_sessions = self.encryption_sessions.write(); - if encryption_sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // some of nodes, which were generating the key may be down - // => do not use these in encryption session - let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } - - let session = Arc::new(EncryptionSessionImpl::new(EncryptionSessionParams { - id: session_id.clone(), - self_node_id: self.self_node_id.clone(), - encrypted_data: encrypted_data, - key_storage: self.key_storage.clone(), - cluster: cluster.clone(), - })?); - let encryption_session = QueuedEncryptionSession { - master: master, - cluster_view: cluster, - last_message_time: time::Instant::now(), - session: session.clone(), - queue: VecDeque::new() - }; - encryption_sessions.insert(session_id, encryption_session); - Ok(session) - } - - pub fn remove_encryption_session(&self, session_id: &SessionId) { - self.encryption_sessions.write().remove(session_id); - } - - pub fn encryption_session(&self, session_id: &SessionId) -> Option> { - self.encryption_sessions.read().get(session_id).map(|s| s.session.clone()) - } - - pub fn enqueue_encryption_message(&self, session_id: &SessionId, sender: NodeId, message: EncryptionMessage, is_queued_message: bool) { - self.encryption_sessions.write().get_mut(session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_encryption_message(&self, session_id: &SessionId) -> Option<(NodeId, EncryptionMessage)> { - self.encryption_sessions.write().get_mut(session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn respond_with_encryption_error(&self, session_id: &SessionId, error: message::EncryptionSessionError) { - self.encryption_sessions.read().get(session_id) - .map(|s| { - // error in encryption session is considered fatal - // => broadcast error - - // do not bother processing send error, as we already processing error - let _ = s.cluster_view.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(error))); - }); - } - - #[cfg(test)] - pub fn make_faulty_generation_sessions(&self) { - self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); - } - - pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { - let mut decryption_sessions = self.decryption_sessions.write(); - let session_id = DecryptionSessionId::new(session_id, sub_session_id); - if decryption_sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // some of nodes, which were encrypting secret may be down - // => do not use these in decryption session - let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } - - let session = Arc::new(DecryptionSessionImpl::new(DecryptionSessionParams { - id: session_id.id.clone(), - access_key: session_id.access_key.clone(), - self_node_id: self.self_node_id.clone(), - encrypted_data: encrypted_data, - acl_storage: self.acl_storage.clone(), - cluster: cluster.clone(), - })?); - let decryption_session = QueuedDecryptionSession { - master: master, - cluster_view: cluster, - last_message_time: time::Instant::now(), - session: session.clone(), - queue: VecDeque::new() - }; - decryption_sessions.insert(session_id, decryption_session); - Ok(session) - } - - pub fn remove_decryption_session(&self, session_id: &SessionId, sub_session_id: &Secret) { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.write().remove(&session_id); - } - - pub fn decryption_session(&self, session_id: &SessionId, sub_session_id: &Secret) -> Option> { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.read().get(&session_id).map(|s| s.session.clone()) - } - - pub fn enqueue_decryption_message(&self, session_id: &SessionId, sub_session_id: &Secret, sender: NodeId, message: DecryptionMessage, is_queued_message: bool) { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.write().get_mut(&session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_decryption_message(&self, session_id: &SessionId, sub_session_id: &Secret) -> Option<(NodeId, DecryptionMessage)> { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.write().get_mut(&session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn respond_with_decryption_error(&self, session_id: &SessionId, sub_session_id: &Secret, to: &NodeId, error: message::DecryptionSessionError) { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.read().get(&session_id) - .map(|s| { - // error in decryption session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - - // do not bother processing send error, as we already processing error - if &s.master == s.session.node() { - let _ = s.cluster_view.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); - } else { - let _ = s.cluster_view.send(to, Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); - } - }); - } - - fn stop_stalled_sessions(&self) { - { - let sessions = self.generation_sessions.write(); - for sid in sessions.keys().collect::>() { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { - session.session.on_session_timeout(); - if session.session.state() == GenerationSessionState::Finished - || session.session.state() == GenerationSessionState::Failed { - self.remove_generation_session(&sid); - } - } - } - } - { - let sessions = self.encryption_sessions.write(); - for sid in sessions.keys().collect::>() { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { - session.session.on_session_timeout(); - if session.session.state() == EncryptionSessionState::Finished - || session.session.state() == EncryptionSessionState::Failed { - self.remove_encryption_session(&sid); - } - } - } - } - { - let sessions = self.decryption_sessions.write(); - for sid in sessions.keys().collect::>() { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(DECRYPTION_SESSION_TIMEOUT_INTERVAL) { - session.session.on_session_timeout(); - if session.session.state() == DecryptionSessionState::Finished - || session.session.state() == DecryptionSessionState::Failed { - self.remove_decryption_session(&sid.id, &sid.access_key); - } - } - } - } - } - - pub fn on_connection_timeout(&self, node_id: &NodeId) { - for (sid, session) in self.generation_sessions.read().iter() { - session.session.on_node_timeout(node_id); - if session.session.state() == GenerationSessionState::Finished - || session.session.state() == GenerationSessionState::Failed { - self.remove_generation_session(sid); - } - } - for (sid, session) in self.encryption_sessions.read().iter() { - session.session.on_node_timeout(node_id); - if session.session.state() == EncryptionSessionState::Finished - || session.session.state() == EncryptionSessionState::Failed { - self.remove_encryption_session(sid); - } - } - for (sid, session) in self.decryption_sessions.read().iter() { - session.session.on_node_timeout(node_id); - if session.session.state() == DecryptionSessionState::Finished - || session.session.state() == DecryptionSessionState::Failed { - self.remove_decryption_session(&sid.id, &sid.access_key); - } - } - } -} - impl ClusterData { pub fn new(handle: &Handle, config: ClusterConfiguration, connections: ClusterConnections, sessions: ClusterSessions) -> Arc { Arc::new(ClusterData { @@ -1223,7 +858,7 @@ impl ClusterClient for ClusterClientImpl { #[cfg(test)] fn generation_session(&self, session_id: &SessionId) -> Option> { - self.data.sessions.generation_session(session_id) + self.data.sessions.generation_sessions.get(session_id) } } diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs new file mode 100644 index 00000000000..8fad33dba90 --- /dev/null +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -0,0 +1,615 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::time; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::collections::{VecDeque, BTreeSet, BTreeMap}; +use parking_lot::RwLock; +use ethkey::Secret; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; +use key_server_cluster::cluster::{Cluster, ClusterView, ClusterConfiguration}; +use key_server_cluster::message::{self, Message, GenerationMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionParams as GenerationSessionParams}; +use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, DecryptionSessionId, + SessionParams as DecryptionSessionParams}; +use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams}; + +/// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, +/// we must treat this session as stalled && finish it with an error. +/// This timeout is for cases when node is responding to KeepAlive messages, but intentionally ignores +/// session messages. +const SESSION_TIMEOUT_INTERVAL: u64 = 60; + +/// Generic cluster session. +pub trait ClusterSession { + /// If session is finished (either with succcess or not). + fn is_finished(&self) -> bool; + /// When it takes too much time to complete session. + fn on_session_timeout(&self); + /// When it takes too much time to receive response from the node. + fn on_node_timeout(&self, node_id: &NodeId); +} +/// Active sessions on this cluster. +pub struct ClusterSessions { + /// Key generation sessions. + pub generation_sessions: ClusterSessionsContainer, + /// Encryption sessions. + pub encryption_sessions: ClusterSessionsContainer, + /// Decryption sessions. + pub decryption_sessions: ClusterSessionsContainer, + /// Self node id. + self_node_id: NodeId, + /// All nodes ids. + nodes: BTreeSet, + /// Reference to key storage + key_storage: Arc, + /// Reference to ACL storage + acl_storage: Arc, + /// Make faulty generation sessions. + make_faulty_generation_sessions: AtomicBool, +} + +/// Active sessions container. +pub struct ClusterSessionsContainer { + /// Active sessions. + pub sessions: RwLock>>, +} + +/// Session and its message queue. +pub struct QueuedSession { + /// Session master. + pub master: NodeId, + /// Cluster view. + pub cluster_view: Arc, + /// Last received message time. + pub last_message_time: time::Instant, + /// Generation session. + pub session: Arc, + /// Messages queue. + pub queue: VecDeque<(NodeId, M)>, +} + +impl ClusterSessions { + /// Create new cluster sessions container. + pub fn new(config: &ClusterConfiguration) -> Self { + ClusterSessions { + self_node_id: config.self_key_pair.public().clone(), + nodes: config.nodes.keys().cloned().collect(), + acl_storage: config.acl_storage.clone(), + key_storage: config.key_storage.clone(), + generation_sessions: ClusterSessionsContainer::new(), + encryption_sessions: ClusterSessionsContainer::new(), + decryption_sessions: ClusterSessionsContainer::new(), + make_faulty_generation_sessions: AtomicBool::new(false), + } + } + + #[cfg(test)] + pub fn make_faulty_generation_sessions(&self) { + self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); + } + + /// Create new generation session. + pub fn new_generation_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { + // check that there's no active encryption session with the same id + if self.generation_sessions.contains(&session_id) { // TODO: possible race here and below + return Err(Error::DuplicateSessionId); + } + // check that there's no finished encryption session with the same id + if self.key_storage.contains(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // communicating to all other nodes is crucial for encryption session + // => check that we have connections to all cluster nodes + if self.nodes.iter().any(|n| !cluster.is_connected(n)) { + return Err(Error::NodeDisconnected); + } + + let session = self.generation_sessions.insert(master, session_id, cluster.clone(), GenerationSessionImpl::new(GenerationSessionParams { + id: session_id.clone(), + self_node_id: self.self_node_id.clone(), + key_storage: self.key_storage.clone(), + cluster: cluster, + })); + if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { + session.simulate_faulty_behaviour(); + } + + Ok(session) + } + + /// Send generation session error. + pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { + self.generation_sessions.sessions.read().get(session_id) + .map(|s| { + // error in generation session is considered fatal + // => broadcast error + + // do not bother processing send error, as we already processing error + let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); + }); + } + + /// Create new encryption session. + pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { + if self.encryption_sessions.contains(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // some of nodes, which were generating the key may be down + // => do not use these in encryption session + let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); + for disconnected_node in disconnected_nodes { + encrypted_data.id_numbers.remove(&disconnected_node); + } + + Ok(self.encryption_sessions.insert(master, session_id, cluster.clone(), EncryptionSessionImpl::new(EncryptionSessionParams { + id: session_id.clone(), + self_node_id: self.self_node_id.clone(), + encrypted_data: encrypted_data, + key_storage: self.key_storage.clone(), + cluster: cluster, + })?)) + } + + /// Send encryption session error. + pub fn respond_with_encryption_error(&self, session_id: &SessionId, error: message::EncryptionSessionError) { + self.encryption_sessions.sessions.read().get(session_id) + .map(|s| { + // error in encryption session is considered fatal + // => broadcast error + + // do not bother processing send error, as we already processing error + let _ = s.cluster_view.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(error))); + }); + } + + /// Create new decryption session. + pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { + let session_id = DecryptionSessionId::new(session_id, sub_session_id); + if self.decryption_sessions.contains(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // some of nodes, which were encrypting secret may be down + // => do not use these in decryption session + let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); + for disconnected_node in disconnected_nodes { + encrypted_data.id_numbers.remove(&disconnected_node); + } + + Ok(self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), DecryptionSessionImpl::new(DecryptionSessionParams { + id: session_id.id, + access_key: session_id.access_key, + self_node_id: self.self_node_id.clone(), + encrypted_data: encrypted_data, + acl_storage: self.acl_storage.clone(), + cluster: cluster, + })?)) + } + + /// Send decryption session error. + pub fn respond_with_decryption_error(&self, session_id: &SessionId, sub_session_id: &Secret, to: &NodeId, error: message::DecryptionSessionError) { + let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); + self.decryption_sessions.sessions.read().get(&session_id) + .map(|s| { + // error in decryption session is non-fatal, if occurs on slave node + // => either respond with error + // => or broadcast error + + // do not bother processing send error, as we already processing error + if &s.master == s.session.node() { + let _ = s.cluster_view.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); + } else { + let _ = s.cluster_view.send(to, Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); + } + }); + } + + /// Stop sessions that are stalling. + pub fn stop_stalled_sessions(&self) { + self.generation_sessions.stop_stalled_sessions(); + self.encryption_sessions.stop_stalled_sessions(); + self.decryption_sessions.stop_stalled_sessions(); + } + + /// When connection to node is lost. + pub fn on_connection_timeout(&self, node_id: &NodeId) { + self.generation_sessions.on_connection_timeout(node_id); + self.encryption_sessions.on_connection_timeout(node_id); + self.decryption_sessions.on_connection_timeout(node_id); + } +} + +impl ClusterSessionsContainer where K: Clone + Ord, V: ClusterSession { + pub fn new() -> Self { + ClusterSessionsContainer { + sessions: RwLock::new(BTreeMap::new()), + } + } + + pub fn contains(&self, session_id: &K) -> bool { + self.sessions.read().contains_key(session_id) + } + + pub fn get(&self, session_id: &K) -> Option> { + self.sessions.read().get(session_id).map(|s| s.session.clone()) + } + + pub fn insert(&self, master: NodeId, session_id: K, cluster: Arc, session: V) -> Arc { + let session = Arc::new(session); + let queued_session = QueuedSession { + master: master, + cluster_view: cluster, + last_message_time: time::Instant::now(), + session: session.clone(), + queue: VecDeque::new(), + }; + self.sessions.write().insert(session_id, queued_session); + session + } + + pub fn remove(&self, session_id: &K) { + self.sessions.write().remove(session_id); + } + + pub fn enqueue_message(&self, session_id: &K, sender: NodeId, message: M, is_queued_message: bool) { + self.sessions.write().get_mut(session_id) + .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } + else { session.queue.push_back((sender, message)) }); + } + + pub fn dequeue_message(&self, session_id: &K) -> Option<(NodeId, M)> { + self.sessions.write().get_mut(session_id) + .and_then(|session| session.queue.pop_front()) + } + + pub fn stop_stalled_sessions(&self) { + let mut sessions = self.sessions.write(); + for sid in sessions.keys().cloned().collect::>() { + let remove_session = { + let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); + if time::Instant::now() - session.last_message_time > time::Duration::from_secs(SESSION_TIMEOUT_INTERVAL) { + session.session.on_session_timeout(); + session.session.is_finished() + } else { + false + } + }; + + if remove_session { + sessions.remove(&sid); + } + } + } + + pub fn on_connection_timeout(&self, node_id: &NodeId) { + let mut sessions = self.sessions.write(); + for sid in sessions.keys().cloned().collect::>() { + let remove_session = { + let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); + session.session.on_node_timeout(node_id); + session.session.is_finished() + }; + if remove_session { + sessions.remove(&sid); + } + } + } +} + +/* +impl Clu + + pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { + self.generation_sessions.read().get(session_id) + .map(|s| { + // error in generation session is considered fatal + // => broadcast error + + // do not bother processing send error, as we already processing error + let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); + }); + } + + + + + + + pub fn new_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { + let mut generation_sessions = self.generation_sessions.write(); + // check that there's no active encryption session with the same id + if generation_sessions.contains_key(&session_id) { + return Err(Error::DuplicateSessionId); + } + // check that there's no finished encryption session with the same id + if self.key_storage.contains(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // communicating to all other nodes is crucial for encryption session + // => check that we have connections to all cluster nodes + if self.nodes.iter().any(|n| !cluster.is_connected(n)) { + return Err(Error::NodeDisconnected); + } + + let session = Arc::new(GenerationSessionImpl::new(GenerationSessionParams { + id: session_id.clone(), + self_node_id: self.self_node_id.clone(), + key_storage: self.key_storage.clone(), + cluster: cluster.clone(), + })); + let generation_session = QueuedGenerationSession { + master: master, + cluster_view: cluster, + last_message_time: time::Instant::now(), + session: session.clone(), + queue: VecDeque::new() + }; + if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { + generation_session.session.simulate_faulty_behaviour(); + } + generation_sessions.insert(session_id, generation_session); + Ok(session) + } + + pub fn remove_generation_session(&self, session_id: &SessionId) { + self.generation_sessions.write().remove(session_id); + } + + pub fn generation_session(&self, session_id: &SessionId) -> Option> { + self.generation_sessions.read().get(session_id).map(|s| s.session.clone()) + } + + pub fn enqueue_generation_message(&self, session_id: &SessionId, sender: NodeId, message: GenerationMessage, is_queued_message: bool) { + self.generation_sessions.write().get_mut(session_id) + .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } + else { session.queue.push_back((sender, message)) }); + } + + pub fn dequeue_generation_message(&self, session_id: &SessionId) -> Option<(NodeId, GenerationMessage)> { + self.generation_sessions.write().get_mut(session_id) + .and_then(|session| session.queue.pop_front()) + } + + pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { + self.generation_sessions.read().get(session_id) + .map(|s| { + // error in generation session is considered fatal + // => broadcast error + + // do not bother processing send error, as we already processing error + let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); + }); + } + + pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { + let mut encryption_sessions = self.encryption_sessions.write(); + if encryption_sessions.contains_key(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // some of nodes, which were generating the key may be down + // => do not use these in encryption session + let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); + for disconnected_node in disconnected_nodes { + encrypted_data.id_numbers.remove(&disconnected_node); + } + + let session = Arc::new(EncryptionSessionImpl::new(EncryptionSessionParams { + id: session_id.clone(), + self_node_id: self.self_node_id.clone(), + encrypted_data: encrypted_data, + key_storage: self.key_storage.clone(), + cluster: cluster.clone(), + })?); + let encryption_session = QueuedEncryptionSession { + master: master, + cluster_view: cluster, + last_message_time: time::Instant::now(), + session: session.clone(), + queue: VecDeque::new() + }; + encryption_sessions.insert(session_id, encryption_session); + Ok(session) + } + + pub fn remove_encryption_session(&self, session_id: &SessionId) { + self.encryption_sessions.write().remove(session_id); + } + + pub fn encryption_session(&self, session_id: &SessionId) -> Option> { + self.encryption_sessions.read().get(session_id).map(|s| s.session.clone()) + } + + pub fn enqueue_encryption_message(&self, session_id: &SessionId, sender: NodeId, message: EncryptionMessage, is_queued_message: bool) { + self.encryption_sessions.write().get_mut(session_id) + .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } + else { session.queue.push_back((sender, message)) }); + } + + pub fn dequeue_encryption_message(&self, session_id: &SessionId) -> Option<(NodeId, EncryptionMessage)> { + self.encryption_sessions.write().get_mut(session_id) + .and_then(|session| session.queue.pop_front()) + } + + pub fn respond_with_encryption_error(&self, session_id: &SessionId, error: message::EncryptionSessionError) { + self.encryption_sessions.read().get(session_id) + .map(|s| { + // error in encryption session is considered fatal + // => broadcast error + + // do not bother processing send error, as we already processing error + let _ = s.cluster_view.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(error))); + }); + } + + #[cfg(test)] + pub fn make_faulty_generation_sessions(&self) { + self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); + } + + pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { + let mut decryption_sessions = self.decryption_sessions.write(); + let session_id = DecryptionSessionId::new(session_id, sub_session_id); + if decryption_sessions.contains_key(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // some of nodes, which were encrypting secret may be down + // => do not use these in decryption session + let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); + for disconnected_node in disconnected_nodes { + encrypted_data.id_numbers.remove(&disconnected_node); + } + + let session = Arc::new(DecryptionSessionImpl::new(DecryptionSessionParams { + id: session_id.id.clone(), + access_key: session_id.access_key.clone(), + self_node_id: self.self_node_id.clone(), + encrypted_data: encrypted_data, + acl_storage: self.acl_storage.clone(), + cluster: cluster.clone(), + })?); + let decryption_session = QueuedDecryptionSession { + master: master, + cluster_view: cluster, + last_message_time: time::Instant::now(), + session: session.clone(), + queue: VecDeque::new() + }; + decryption_sessions.insert(session_id, decryption_session); + Ok(session) + } + + pub fn remove_decryption_session(&self, session_id: &SessionId, sub_session_id: &Secret) { + let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); + self.decryption_sessions.write().remove(&session_id); + } + + pub fn decryption_session(&self, session_id: &SessionId, sub_session_id: &Secret) -> Option> { + let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); + self.decryption_sessions.read().get(&session_id).map(|s| s.session.clone()) + } + + pub fn enqueue_decryption_message(&self, session_id: &SessionId, sub_session_id: &Secret, sender: NodeId, message: DecryptionMessage, is_queued_message: bool) { + let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); + self.decryption_sessions.write().get_mut(&session_id) + .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } + else { session.queue.push_back((sender, message)) }); + } + + pub fn dequeue_decryption_message(&self, session_id: &SessionId, sub_session_id: &Secret) -> Option<(NodeId, DecryptionMessage)> { + let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); + self.decryption_sessions.write().get_mut(&session_id) + .and_then(|session| session.queue.pop_front()) + } + + pub fn respond_with_decryption_error(&self, session_id: &SessionId, sub_session_id: &Secret, to: &NodeId, error: message::DecryptionSessionError) { + let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); + self.decryption_sessions.read().get(&session_id) + .map(|s| { + // error in decryption session is non-fatal, if occurs on slave node + // => either respond with error + // => or broadcast error + + // do not bother processing send error, as we already processing error + if &s.master == s.session.node() { + let _ = s.cluster_view.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); + } else { + let _ = s.cluster_view.send(to, Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); + } + }); + } + + fn stop_stalled_sessions(&self) { + { + let sessions = self.generation_sessions.write(); + for sid in sessions.keys().collect::>() { + let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); + if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { + session.session.on_session_timeout(); + if session.session.state() == GenerationSessionState::Finished + || session.session.state() == GenerationSessionState::Failed { + self.remove_generation_session(&sid); + } + } + } + } + { + let sessions = self.encryption_sessions.write(); + for sid in sessions.keys().collect::>() { + let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); + if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { + session.session.on_session_timeout(); + if session.session.state() == EncryptionSessionState::Finished + || session.session.state() == EncryptionSessionState::Failed { + self.remove_encryption_session(&sid); + } + } + } + } + { + let sessions = self.decryption_sessions.write(); + for sid in sessions.keys().collect::>() { + let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); + if time::Instant::now() - session.last_message_time > time::Duration::from_secs(DECRYPTION_SESSION_TIMEOUT_INTERVAL) { + session.session.on_session_timeout(); + if session.session.state() == DecryptionSessionState::Finished + || session.session.state() == DecryptionSessionState::Failed { + self.remove_decryption_session(&sid.id, &sid.access_key); + } + } + } + } + } + + pub fn on_connection_timeout(&self, node_id: &NodeId) { + for (sid, session) in self.generation_sessions.read().iter() { + session.session.on_node_timeout(node_id); + if session.session.state() == GenerationSessionState::Finished + || session.session.state() == GenerationSessionState::Failed { + self.remove_generation_session(sid); + } + } + for (sid, session) in self.encryption_sessions.read().iter() { + session.session.on_node_timeout(node_id); + if session.session.state() == EncryptionSessionState::Finished + || session.session.state() == EncryptionSessionState::Failed { + self.remove_encryption_session(sid); + } + } + for (sid, session) in self.decryption_sessions.read().iter() { + session.session.on_node_timeout(node_id); + if session.session.state() == DecryptionSessionState::Finished + || session.session.state() == DecryptionSessionState::Failed { + self.remove_decryption_session(&sid.id, &sid.access_key); + } + } + } +} +*/ \ No newline at end of file diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 61e9c655f3c..d19cf22d49c 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -23,6 +23,7 @@ use ethcrypto::DEFAULT_MAC; use ethkey::{self, Secret, Public, Signature}; use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, EncryptedDocumentKeyShadow}; use key_server_cluster::cluster::Cluster; +use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::math; use key_server_cluster::message::{Message, DecryptionMessage, InitializeDecryptionSession, ConfirmDecryptionInitialization, RequestPartialDecryption, PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted}; @@ -436,8 +437,71 @@ impl SessionImpl { Ok(()) } - /// When connection to one of cluster nodes has timeouted. - pub fn on_node_timeout(&self, node: &NodeId) { + fn start_waiting_for_partial_decryption(self_node_id: NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + let confirmed_nodes: BTreeSet<_> = data.confirmed_nodes.clone(); + let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.rejected_nodes).cloned().collect(); + + data.shadow_requests.clear(); + data.shadow_points.clear(); + for node in confirmed_nodes.iter().filter(|n| n != &&self_node_id) { + data.shadow_requests.insert(node.clone()); + cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { + session: session_id.clone().into(), + sub_session: access_key.clone().into(), + nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), + })))?; + } + + if data.confirmed_nodes.remove(&self_node_id) { + let decryption_result = { + let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); + let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); + do_partial_decryption(&self_node_id, &requestor, is_shadow_decryption, &data.confirmed_nodes, &access_key, &encrypted_data)? + }; + data.shadow_points.insert(self_node_id.clone(), decryption_result); + } + + Ok(()) + } + + fn do_decryption(access_key: Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + // decrypt the secret using shadow points + let joint_shadow_point = math::compute_joint_shadow_point(data.shadow_points.values().map(|s| &s.shadow_point))?; + let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); + let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); + let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, &access_key, encrypted_point, &joint_shadow_point)?; + let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; decryption follows initialization; qed"); + let (common_point, decrypt_shadows) = if is_shadow_decryption { + ( + Some(math::make_common_shadow_point(encrypted_data.threshold, common_point.clone())?), + Some(data.shadow_points.values() + .map(|s| s.decrypt_shadow.as_ref().expect("decrypt_shadow is filled during partial decryption; decryption follows partial decryption; qed").clone()) + .collect()) + ) + } else { + (None, None) + }; + data.decrypted_secret = Some(Ok(EncryptedDocumentKeyShadow { + decrypted_secret: decrypted_secret, + common_point: common_point, + decrypt_shadows: decrypt_shadows, + })); + + // switch to completed state + data.state = SessionState::Finished; + + Ok(()) + } +} + +impl ClusterSession for SessionImpl { + fn is_finished(&self) -> bool { + let data = self.data.lock(); + data.state == SessionState::Failed + || data.state == SessionState::Finished + } + + fn on_node_timeout(&self, node: &NodeId) { let mut data = self.data.lock(); let is_self_master = data.master.as_ref() == Some(self.node()); @@ -503,8 +567,7 @@ impl SessionImpl { self.completed.notify_all(); } - /// When session timeout has occured. - pub fn on_session_timeout(&self) { + fn on_session_timeout(&self) { let mut data = self.data.lock(); let is_self_master = data.master.as_ref() == Some(self.node()); @@ -542,62 +605,6 @@ impl SessionImpl { data.decrypted_secret = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } - - fn start_waiting_for_partial_decryption(self_node_id: NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - let confirmed_nodes: BTreeSet<_> = data.confirmed_nodes.clone(); - let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.rejected_nodes).cloned().collect(); - - data.shadow_requests.clear(); - data.shadow_points.clear(); - for node in confirmed_nodes.iter().filter(|n| n != &&self_node_id) { - data.shadow_requests.insert(node.clone()); - cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { - session: session_id.clone().into(), - sub_session: access_key.clone().into(), - nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), - })))?; - } - - if data.confirmed_nodes.remove(&self_node_id) { - let decryption_result = { - let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); - let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); - do_partial_decryption(&self_node_id, &requestor, is_shadow_decryption, &data.confirmed_nodes, &access_key, &encrypted_data)? - }; - data.shadow_points.insert(self_node_id.clone(), decryption_result); - } - - Ok(()) - } - - fn do_decryption(access_key: Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - // decrypt the secret using shadow points - let joint_shadow_point = math::compute_joint_shadow_point(data.shadow_points.values().map(|s| &s.shadow_point))?; - let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); - let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); - let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, &access_key, encrypted_point, &joint_shadow_point)?; - let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; decryption follows initialization; qed"); - let (common_point, decrypt_shadows) = if is_shadow_decryption { - ( - Some(math::make_common_shadow_point(encrypted_data.threshold, common_point.clone())?), - Some(data.shadow_points.values() - .map(|s| s.decrypt_shadow.as_ref().expect("decrypt_shadow is filled during partial decryption; decryption follows partial decryption; qed").clone()) - .collect()) - ) - } else { - (None, None) - }; - data.decrypted_secret = Some(Ok(EncryptedDocumentKeyShadow { - decrypted_secret: decrypted_secret, - common_point: common_point, - decrypt_shadows: decrypt_shadows, - })); - - // switch to completed state - data.state = SessionState::Finished; - - Ok(()) - } } impl Session for SessionImpl { @@ -709,6 +716,7 @@ mod tests { use ethkey::{self, Random, Generator, Public, Secret}; use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow}; use key_server_cluster::cluster::tests::DummyCluster; + use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::decryption_session::{SessionImpl, SessionParams, SessionState}; use key_server_cluster::message::{self, Message, DecryptionMessage}; use key_server_cluster::math; diff --git a/secret_store/src/key_server_cluster/encryption_session.rs b/secret_store/src/key_server_cluster/encryption_session.rs index 22165374322..b61594925ff 100644 --- a/secret_store/src/key_server_cluster/encryption_session.rs +++ b/secret_store/src/key_server_cluster/encryption_session.rs @@ -22,6 +22,7 @@ use parking_lot::{Condvar, Mutex}; use ethkey::{self, Public, Signature}; use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare}; use key_server_cluster::cluster::Cluster; +use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::message::{Message, EncryptionMessage, InitializeEncryptionSession, ConfirmEncryptionInitialization, EncryptionSessionError}; @@ -251,9 +252,16 @@ impl SessionImpl { Ok(()) } +} + +impl ClusterSession for SessionImpl { + fn is_finished(&self) -> bool { + let data = self.data.lock(); + data.state == SessionState::Failed + || data.state == SessionState::Finished + } - /// When connection to one of cluster nodes has timeouted. - pub fn on_node_timeout(&self, node: &NodeId) { + fn on_node_timeout(&self, node: &NodeId) { let mut data = self.data.lock(); warn!("{}: encryption session failed because {} connection has timeouted", self.node(), node); @@ -263,8 +271,7 @@ impl SessionImpl { self.completed.notify_all(); } - /// When session timeout has occured. - pub fn on_session_timeout(&self) { + fn on_session_timeout(&self) { let mut data = self.data.lock(); warn!("{}: encryption session failed with timeout", self.node()); diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index 4eceb366233..ac6919cd076 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -23,6 +23,7 @@ use ethkey::{Public, Secret}; use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare}; use key_server_cluster::math; use key_server_cluster::cluster::Cluster; +use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; @@ -514,30 +515,6 @@ impl SessionImpl { Ok(()) } - /// When connection to one of cluster nodes has timeouted. - pub fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - // all nodes are required for generation session - // => fail without check - warn!("{}: generation session failed because {} connection has timeouted", self.node(), node); - - data.state = SessionState::Failed; - data.joint_public = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); - } - - /// When session timeout has occured. - pub fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - warn!("{}: generation session failed with timeout", self.node()); - - data.state = SessionState::Failed; - data.joint_public = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); - } - /// Complete initialization (when all other nodex has responded with confirmation) fn complete_initialization(&self, mut derived_point: Public) -> Result<(), Error> { // update point once again to make sure that derived point is not generated by last node @@ -694,6 +671,36 @@ impl SessionImpl { } } +impl ClusterSession for SessionImpl { + fn is_finished(&self) -> bool { + let data = self.data.lock(); + data.state == SessionState::Failed + || data.state == SessionState::Finished + } + + fn on_node_timeout(&self, node: &NodeId) { + let mut data = self.data.lock(); + + // all nodes are required for generation session + // => fail without check + warn!("{}: generation session failed because {} connection has timeouted", self.node(), node); + + data.state = SessionState::Failed; + data.joint_public = Some(Err(Error::NodeDisconnected)); + self.completed.notify_all(); + } + + fn on_session_timeout(&self) { + let mut data = self.data.lock(); + + warn!("{}: generation session failed with timeout", self.node()); + + data.state = SessionState::Failed; + data.joint_public = Some(Err(Error::NodeDisconnected)); + self.completed.notify_all(); + } +} + impl Session for SessionImpl { fn state(&self) -> SessionState { self.data.lock().state.clone() @@ -797,6 +804,7 @@ mod tests { use key_server_cluster::{NodeId, SessionId, Error, DummyKeyStorage}; use key_server_cluster::message::{self, Message, GenerationMessage}; use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; + use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::generation_session::{Session, SessionImpl, SessionState, SessionParams}; use key_server_cluster::math; use key_server_cluster::math::tests::do_encryption_and_decryption; diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 56f5d9194ec..25a6dbae79a 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -133,6 +133,7 @@ impl Into for Error { } mod cluster; +mod cluster_sessions; mod decryption_session; mod encryption_session; mod generation_session; From 679fcbe073e2fb0c5d405753e075c908445e7351 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 11 May 2017 17:47:27 +0300 Subject: [PATCH 04/45] signing session prototype --- .../src/key_server_cluster/cluster.rs | 82 ++++- .../key_server_cluster/cluster_sessions.rs | 304 +----------------- .../key_server_cluster/decryption_session.rs | 1 - .../src/key_server_cluster/io/message.rs | 2 + .../src/key_server_cluster/message.rs | 24 ++ secret_store/src/key_server_cluster/mod.rs | 1 + .../src/key_server_cluster/signing_session.rs | 142 ++++++++ 7 files changed, 256 insertions(+), 300 deletions(-) create mode 100644 secret_store/src/key_server_cluster/signing_session.rs diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 24b83e0fa2a..33d9cf457d1 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -29,12 +29,13 @@ use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; use key_server_cluster::cluster_sessions::ClusterSessions; -use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; #[cfg(test)] use key_server_cluster::generation_session::SessionImpl as GenerationSessionImpl; use key_server_cluster::decryption_session::{Session as DecryptionSession, SessionState as DecryptionSessionState, DecryptionSessionId}; use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionState as EncryptionSessionState}; +use key_server_cluster::signing_session::{Session as SigningSession, SessionState as SigningSessionState, SigningSessionId}; use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, read_encrypted_message, WriteMessage, write_encrypted_message}; use key_server_cluster::net::{accept_connection as net_accept_connection, connect as net_connect, Connection as NetConnection}; @@ -387,6 +388,7 @@ impl ClusterCore { Message::Generation(message) => ClusterCore::process_generation_message(data, connection, message), Message::Encryption(message) => ClusterCore::process_encryption_message(data, connection, message), Message::Decryption(message) => ClusterCore::process_decryption_message(data, connection, message), + Message::Signing(message) => ClusterCore::process_signing_message(data, connection, message), Message::Cluster(message) => ClusterCore::process_cluster_message(data, connection, message), } } @@ -615,6 +617,84 @@ impl ClusterCore { } } + /// Process singlesigning message from the connection. + fn process_signing_message(data: Arc, connection: Arc, mut message: SigningMessage) { + let session_id = message.session_id().clone(); + let sub_session_id = message.sub_session_id().clone(); + let decryption_session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone()); + let mut sender = connection.node_id().clone(); + let session = match message { +/* DecryptionMessage::InitializeDecryptionSession(_) => { + let mut connected_nodes = data.connections.connected_nodes(); + connected_nodes.insert(data.self_key_pair.public().clone()); + + let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); + data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster) + }, + _ => { + data.sessions.decryption_sessions.get(&decryption_session_id) + .ok_or(Error::InvalidSessionId) + },*/ + }; + + let mut is_queued_message = false; + loop { +/* match session.clone().and_then(|session| match message { + DecryptionMessage::InitializeDecryptionSession(ref message) => + session.on_initialize_session(sender.clone(), message), + DecryptionMessage::ConfirmDecryptionInitialization(ref message) => + session.on_confirm_initialization(sender.clone(), message), + DecryptionMessage::RequestPartialDecryption(ref message) => + session.on_partial_decryption_requested(sender.clone(), message), + DecryptionMessage::PartialDecryption(ref message) => + session.on_partial_decryption(sender.clone(), message), + DecryptionMessage::DecryptionSessionError(ref message) => + session.on_session_error(sender.clone(), message), + DecryptionMessage::DecryptionSessionCompleted(ref message) => + session.on_session_completed(sender.clone(), message), + }) { + Ok(_) => { + // if session is completed => stop + let session = session.clone().expect("session.method() call finished with success; session exists; qed"); + let session_state = session.state(); + if session_state == DecryptionSessionState::Finished { + info!(target: "secretstore_net", "{}: decryption session completed", data.self_key_pair.public()); + } + if session_state == DecryptionSessionState::Finished || session_state == DecryptionSessionState::Failed { + data.sessions.decryption_sessions.remove(&decryption_session_id); + break; + } + + // try to dequeue message + match data.sessions.decryption_sessions.dequeue_message(&decryption_session_id) { + Some((msg_sender, msg)) => { + is_queued_message = true; + sender = msg_sender; + message = msg; + }, + None => break, + } + }, + Err(Error::TooEarlyForRequest) => { + data.sessions.decryption_sessions.enqueue_message(&decryption_session_id, sender, message, is_queued_message); + break; + }, + Err(err) => { + warn!(target: "secretstore_net", "{}: decryption session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); + data.sessions.respond_with_decryption_error(&session_id, &sub_session_id, &sender, message::DecryptionSessionError { + session: session_id.clone().into(), + sub_session: sub_session_id.clone().into(), + error: format!("{:?}", err), + }); + if err != Error::InvalidSessionId { + data.sessions.decryption_sessions.remove(&decryption_session_id); + } + break; + }, + }*/ + } + } + /// Process single cluster message from the connection. fn process_cluster_message(data: Arc, connection: Arc, message: ClusterMessage) { match message { diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 8fad33dba90..1167823ae07 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -22,11 +22,13 @@ use parking_lot::RwLock; use ethkey::Secret; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; use key_server_cluster::cluster::{Cluster, ClusterView, ClusterConfiguration}; -use key_server_cluster::message::{self, Message, GenerationMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::message::{self, Message, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage}; use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionParams as GenerationSessionParams}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, DecryptionSessionId, SessionParams as DecryptionSessionParams}; use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams}; +use key_server_cluster::signing_session::{SessionImpl as SigningSessionImpl, SigningSessionId, + SessionParams as SigningSessionParams}; /// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, /// we must treat this session as stalled && finish it with an error. @@ -51,6 +53,8 @@ pub struct ClusterSessions { pub encryption_sessions: ClusterSessionsContainer, /// Decryption sessions. pub decryption_sessions: ClusterSessionsContainer, + /// Signing sessions. + pub signing_sessions: ClusterSessionsContainer, /// Self node id. self_node_id: NodeId, /// All nodes ids. @@ -94,6 +98,7 @@ impl ClusterSessions { generation_sessions: ClusterSessionsContainer::new(), encryption_sessions: ClusterSessionsContainer::new(), decryption_sessions: ClusterSessionsContainer::new(), + signing_sessions: ClusterSessionsContainer::new(), make_faulty_generation_sessions: AtomicBool::new(false), } } @@ -316,300 +321,3 @@ impl ClusterSessionsContainer where K: Clone + Ord, V: Cluster } } } - -/* -impl Clu - - pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { - self.generation_sessions.read().get(session_id) - .map(|s| { - // error in generation session is considered fatal - // => broadcast error - - // do not bother processing send error, as we already processing error - let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); - }); - } - - - - - - - pub fn new_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - let mut generation_sessions = self.generation_sessions.write(); - // check that there's no active encryption session with the same id - if generation_sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - // check that there's no finished encryption session with the same id - if self.key_storage.contains(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // communicating to all other nodes is crucial for encryption session - // => check that we have connections to all cluster nodes - if self.nodes.iter().any(|n| !cluster.is_connected(n)) { - return Err(Error::NodeDisconnected); - } - - let session = Arc::new(GenerationSessionImpl::new(GenerationSessionParams { - id: session_id.clone(), - self_node_id: self.self_node_id.clone(), - key_storage: self.key_storage.clone(), - cluster: cluster.clone(), - })); - let generation_session = QueuedGenerationSession { - master: master, - cluster_view: cluster, - last_message_time: time::Instant::now(), - session: session.clone(), - queue: VecDeque::new() - }; - if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { - generation_session.session.simulate_faulty_behaviour(); - } - generation_sessions.insert(session_id, generation_session); - Ok(session) - } - - pub fn remove_generation_session(&self, session_id: &SessionId) { - self.generation_sessions.write().remove(session_id); - } - - pub fn generation_session(&self, session_id: &SessionId) -> Option> { - self.generation_sessions.read().get(session_id).map(|s| s.session.clone()) - } - - pub fn enqueue_generation_message(&self, session_id: &SessionId, sender: NodeId, message: GenerationMessage, is_queued_message: bool) { - self.generation_sessions.write().get_mut(session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_generation_message(&self, session_id: &SessionId) -> Option<(NodeId, GenerationMessage)> { - self.generation_sessions.write().get_mut(session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn respond_with_generation_error(&self, session_id: &SessionId, error: message::SessionError) { - self.generation_sessions.read().get(session_id) - .map(|s| { - // error in generation session is considered fatal - // => broadcast error - - // do not bother processing send error, as we already processing error - let _ = s.cluster_view.broadcast(Message::Generation(GenerationMessage::SessionError(error))); - }); - } - - pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - let mut encryption_sessions = self.encryption_sessions.write(); - if encryption_sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // some of nodes, which were generating the key may be down - // => do not use these in encryption session - let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } - - let session = Arc::new(EncryptionSessionImpl::new(EncryptionSessionParams { - id: session_id.clone(), - self_node_id: self.self_node_id.clone(), - encrypted_data: encrypted_data, - key_storage: self.key_storage.clone(), - cluster: cluster.clone(), - })?); - let encryption_session = QueuedEncryptionSession { - master: master, - cluster_view: cluster, - last_message_time: time::Instant::now(), - session: session.clone(), - queue: VecDeque::new() - }; - encryption_sessions.insert(session_id, encryption_session); - Ok(session) - } - - pub fn remove_encryption_session(&self, session_id: &SessionId) { - self.encryption_sessions.write().remove(session_id); - } - - pub fn encryption_session(&self, session_id: &SessionId) -> Option> { - self.encryption_sessions.read().get(session_id).map(|s| s.session.clone()) - } - - pub fn enqueue_encryption_message(&self, session_id: &SessionId, sender: NodeId, message: EncryptionMessage, is_queued_message: bool) { - self.encryption_sessions.write().get_mut(session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_encryption_message(&self, session_id: &SessionId) -> Option<(NodeId, EncryptionMessage)> { - self.encryption_sessions.write().get_mut(session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn respond_with_encryption_error(&self, session_id: &SessionId, error: message::EncryptionSessionError) { - self.encryption_sessions.read().get(session_id) - .map(|s| { - // error in encryption session is considered fatal - // => broadcast error - - // do not bother processing send error, as we already processing error - let _ = s.cluster_view.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(error))); - }); - } - - #[cfg(test)] - pub fn make_faulty_generation_sessions(&self) { - self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); - } - - pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { - let mut decryption_sessions = self.decryption_sessions.write(); - let session_id = DecryptionSessionId::new(session_id, sub_session_id); - if decryption_sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // some of nodes, which were encrypting secret may be down - // => do not use these in decryption session - let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } - - let session = Arc::new(DecryptionSessionImpl::new(DecryptionSessionParams { - id: session_id.id.clone(), - access_key: session_id.access_key.clone(), - self_node_id: self.self_node_id.clone(), - encrypted_data: encrypted_data, - acl_storage: self.acl_storage.clone(), - cluster: cluster.clone(), - })?); - let decryption_session = QueuedDecryptionSession { - master: master, - cluster_view: cluster, - last_message_time: time::Instant::now(), - session: session.clone(), - queue: VecDeque::new() - }; - decryption_sessions.insert(session_id, decryption_session); - Ok(session) - } - - pub fn remove_decryption_session(&self, session_id: &SessionId, sub_session_id: &Secret) { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.write().remove(&session_id); - } - - pub fn decryption_session(&self, session_id: &SessionId, sub_session_id: &Secret) -> Option> { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.read().get(&session_id).map(|s| s.session.clone()) - } - - pub fn enqueue_decryption_message(&self, session_id: &SessionId, sub_session_id: &Secret, sender: NodeId, message: DecryptionMessage, is_queued_message: bool) { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.write().get_mut(&session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_decryption_message(&self, session_id: &SessionId, sub_session_id: &Secret) -> Option<(NodeId, DecryptionMessage)> { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.write().get_mut(&session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn respond_with_decryption_error(&self, session_id: &SessionId, sub_session_id: &Secret, to: &NodeId, error: message::DecryptionSessionError) { - let session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); - self.decryption_sessions.read().get(&session_id) - .map(|s| { - // error in decryption session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - - // do not bother processing send error, as we already processing error - if &s.master == s.session.node() { - let _ = s.cluster_view.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); - } else { - let _ = s.cluster_view.send(to, Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); - } - }); - } - - fn stop_stalled_sessions(&self) { - { - let sessions = self.generation_sessions.write(); - for sid in sessions.keys().collect::>() { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { - session.session.on_session_timeout(); - if session.session.state() == GenerationSessionState::Finished - || session.session.state() == GenerationSessionState::Failed { - self.remove_generation_session(&sid); - } - } - } - } - { - let sessions = self.encryption_sessions.write(); - for sid in sessions.keys().collect::>() { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(ENCRYPTION_SESSION_TIMEOUT_INTERVAL) { - session.session.on_session_timeout(); - if session.session.state() == EncryptionSessionState::Finished - || session.session.state() == EncryptionSessionState::Failed { - self.remove_encryption_session(&sid); - } - } - } - } - { - let sessions = self.decryption_sessions.write(); - for sid in sessions.keys().collect::>() { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if time::Instant::now() - session.last_message_time > time::Duration::from_secs(DECRYPTION_SESSION_TIMEOUT_INTERVAL) { - session.session.on_session_timeout(); - if session.session.state() == DecryptionSessionState::Finished - || session.session.state() == DecryptionSessionState::Failed { - self.remove_decryption_session(&sid.id, &sid.access_key); - } - } - } - } - } - - pub fn on_connection_timeout(&self, node_id: &NodeId) { - for (sid, session) in self.generation_sessions.read().iter() { - session.session.on_node_timeout(node_id); - if session.session.state() == GenerationSessionState::Finished - || session.session.state() == GenerationSessionState::Failed { - self.remove_generation_session(sid); - } - } - for (sid, session) in self.encryption_sessions.read().iter() { - session.session.on_node_timeout(node_id); - if session.session.state() == EncryptionSessionState::Finished - || session.session.state() == EncryptionSessionState::Failed { - self.remove_encryption_session(sid); - } - } - for (sid, session) in self.decryption_sessions.read().iter() { - session.session.on_node_timeout(node_id); - if session.session.state() == DecryptionSessionState::Finished - || session.session.state() == DecryptionSessionState::Failed { - self.remove_decryption_session(&sid.id, &sid.access_key); - } - } - } -} -*/ \ No newline at end of file diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index d19cf22d49c..6edcba9d376 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -646,7 +646,6 @@ impl Ord for DecryptionSessionId { } } - fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index b318e1c9584..e9934ce312f 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -85,6 +85,8 @@ pub fn serialize_message(message: Message) -> Result { Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (153, serde_json::to_vec(&payload)), Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (154, serde_json::to_vec(&payload)), Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (155, serde_json::to_vec(&payload)), + + Message::Signing(_) => unreachable!(), }; let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index f59edc1e51e..1ab9c5e02fa 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -34,6 +34,8 @@ pub enum Message { Encryption(EncryptionMessage), /// Decryption message. Decryption(DecryptionMessage), + /// Signing message. + Signing(SigningMessage), } #[derive(Clone, Debug)] @@ -96,6 +98,11 @@ pub enum DecryptionMessage { DecryptionSessionCompleted(DecryptionSessionCompleted), } +#[derive(Clone, Debug)] +/// All possible messages that can be sent during signing session. +pub enum SigningMessage { +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Introduce node public key. pub struct NodePublicKey { @@ -344,6 +351,16 @@ impl DecryptionMessage { } } +impl SigningMessage { + pub fn session_id(&self) -> &SessionId { + unimplemented!() + } + + pub fn sub_session_id(&self) -> &Secret { + unimplemented!() + } +} + impl fmt::Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -351,6 +368,7 @@ impl fmt::Display for Message { Message::Generation(ref message) => write!(f, "Generation.{}", message), Message::Encryption(ref message) => write!(f, "Encryption.{}", message), Message::Decryption(ref message) => write!(f, "Decryption.{}", message), + Message::Signing(ref message) => write!(f, "Signing.{}", message), } } } @@ -402,3 +420,9 @@ impl fmt::Display for DecryptionMessage { } } } + +impl fmt::Display for SigningMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + unimplemented!() + } +} diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 25a6dbae79a..23c6b63dece 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -140,4 +140,5 @@ mod generation_session; mod io; pub mod math; mod message; +mod signing_session; mod net; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs new file mode 100644 index 00000000000..771c39e1617 --- /dev/null +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -0,0 +1,142 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::cmp::{Ordering, Ord, PartialOrd}; +use std::sync::Arc; +use std::time; +use ethkey::{Public, Secret}; +use util; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; +use key_server_cluster::cluster::{Cluster}; +use key_server_cluster::cluster_sessions::ClusterSession; + +/// Signing session API. +pub trait Session: Send + Sync + 'static { + /// Get generation session state. + fn state(&self) -> SessionState; + /// Wait until session is completed. Returns signed message. + fn wait(&self, timeout: Option) -> Result; +} + +/// Signing session. +pub struct SessionImpl { +} + +/// Signing session Id. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SigningSessionId { + /// Encryption session id. + pub id: SessionId, + /// Signing session access key. + pub access_key: Secret, +} + +/// SessionImpl creation parameters +pub struct SessionParams { + /// SessionImpl identifier. + pub id: SessionId, + /// Id of node, on which this session is running. + pub self_node_id: Public, + /// Key storage. + pub acl_storage: Arc, + /// Cluster + pub cluster: Arc, +} + +#[derive(Debug)] +/// Mutable data of signing session. +struct SessionData { + /// Current state of the session. + state: SessionState, + + // === Values, filled when session initialization just starts === + /// Reference to the node, which has started this session. + master: Option, +} + +#[derive(Debug, Clone)] +/// Mutable node-specific data. +struct NodeData { + /// Random unique scalar. Persistent. + pub id_number: Secret, +} + +#[derive(Debug, Clone, PartialEq)] +/// Distributed key generation session state. +pub enum SessionState { + // === Initialization states === + /// Every node starts in this state. + WaitingForInitialization, + + // === Final states of the session === + /// Signing is completed. + Finished, + /// Signing is failed. + Failed, +} + +impl SessionImpl { +} + +impl ClusterSession for SessionImpl { + fn is_finished(&self) -> bool { + unimplemented!() + } + + fn on_session_timeout(&self) { + unimplemented!() + } + + fn on_node_timeout(&self, node_id: &NodeId) { + unimplemented!() + } +} + +impl Session for SessionImpl { + fn state(&self) -> SessionState { + unimplemented!() + } + + fn wait(&self, timeout: Option) -> Result { + unimplemented!() + } +} + +impl SigningSessionId { + /// Create new decryption session Id. + pub fn new(session_id: SessionId, sub_session_id: Secret) -> Self { + SigningSessionId { + id: session_id, + access_key: sub_session_id, + } + } +} + +impl PartialOrd for SigningSessionId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + + +impl Ord for SigningSessionId { + fn cmp(&self, other: &Self) -> Ordering { + match self.id.cmp(&other.id) { + Ordering::Equal => self.access_key.cmp(&other.access_key), + r @ _ => r, + } + } +} From 56a7458573fdcba65efd347d9b8c8f22d5e7a3fb Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 17 May 2017 16:39:04 +0300 Subject: [PATCH 05/45] full_signature_math_session --- .../key_server_cluster/decryption_session.rs | 2 +- secret_store/src/key_server_cluster/math.rs | 356 ++++++++++++++---- .../src/key_server_cluster/message.rs | 16 +- 3 files changed, 295 insertions(+), 79 deletions(-) diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 6edcba9d376..f4507a210b9 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -694,7 +694,7 @@ fn do_partial_decryption(node: &NodeId, requestor_public: &Public, is_shadow_dec let other_id_numbers = participants.iter() .filter(|id| *id != node) .map(|id| &encrypted_data.id_numbers[id]); - let node_shadow = math::compute_node_shadow(node_id_number, node_secret_share, other_id_numbers)?; + let node_shadow = math::compute_node_shadow(node_secret_share, node_id_number, other_id_numbers)?; let decrypt_shadow = if is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(access_key, common_point, &node_shadow, decrypt_shadow)?; diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index c3bef274f30..1ef95cc4f1b 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -14,7 +14,9 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use ethkey::{Public, Secret, Random, Generator, math}; +use std::iter::once; +use ethkey::{Public, Secret, Random, Generator, Signature, recover, math}; +use util::{U256, H256, Bytes, sha3, Hashable}; use key_server_cluster::Error; #[derive(Debug)] @@ -36,6 +38,48 @@ pub fn generate_random_point() -> Result { Ok(Random.generate()?.public().clone()) } +/// Compute publics sum. +pub fn compute_public_sum<'a, I>(mut publics: I) -> Result where I: Iterator { + let mut sum = publics.next().expect("compute_public_sum is called when there's at least one public; qed").clone(); + while let Some(public) = publics.next() { + math::public_add(&mut sum, &public)?; + } + Ok(sum) +} + +/// Compute secrets sum. +pub fn compute_secret_sum<'a, I>(mut secrets: I) -> Result where I: Iterator { + let mut sum = secrets.next().expect("compute_secret_sum is called when there's at least one secret; qed").clone(); + while let Some(secret) = secrets.next() { + sum.add(secret)?; + } + Ok(sum) +} + +/// Compute secrets 'shadow' multiplication: coeff * multiplication(s[j] / (s[i] - s[j])) for every i != j +pub fn compute_shadow_mul<'a, I>(coeff: &Secret, self_secret: &Secret, mut other_secrets: I) -> Result where I: Iterator { + // when there are no other secrets, only coeff is left + let other_secret = match other_secrets.next() { + Some(other_secret) => other_secret, + None => return Ok(coeff.clone()), + }; + + let mut shadow_mul = self_secret.clone(); + shadow_mul.sub(other_secret)?; + shadow_mul.inv()?; + shadow_mul.mul(other_secret)?; + while let Some(other_secret) = other_secrets.next() { + let mut shadow_mul_element = self_secret.clone(); + shadow_mul_element.sub(other_secret)?; + shadow_mul_element.inv()?; + shadow_mul_element.mul(other_secret)?; + shadow_mul.mul(&shadow_mul_element)?; + } + + shadow_mul.mul(coeff)?; + Ok(shadow_mul) +} + /// Update point by multiplying to random scalar pub fn update_random_point(point: &mut Public) -> Result<(), Error> { Ok(math::public_mul_secret(point, &generate_random_scalar()?)?) @@ -43,12 +87,9 @@ pub fn update_random_point(point: &mut Public) -> Result<(), Error> { /// Generate random polynom of threshold degree pub fn generate_random_polynom(threshold: usize) -> Result, Error> { - let mut polynom: Vec<_> = Vec::with_capacity(threshold + 1); - for _ in 0..threshold + 1 { - polynom.push(generate_random_scalar()?); - } - debug_assert_eq!(polynom.len(), threshold + 1); - Ok(polynom) + (0..threshold + 1) + .map(|_| generate_random_scalar()) + .collect() } /// Compute value of polynom, using `node_number` as argument @@ -126,11 +167,7 @@ pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &S /// Compute secret share. pub fn compute_secret_share<'a, I>(mut secret_values: I) -> Result where I: Iterator { - let mut secret_share = secret_values.next().expect("compute_secret_share is called when cluster has at least one node; qed").clone(); - while let Some(secret_value) = secret_values.next() { - secret_share.add(secret_value)?; - } - Ok(secret_share) + compute_secret_sum(secret_values) } /// Compute public key share. @@ -142,21 +179,13 @@ pub fn compute_public_share(self_secret_value: &Secret) -> Result /// Compute joint public key. pub fn compute_joint_public<'a, I>(mut public_shares: I) -> Result where I: Iterator { - let mut joint_public = public_shares.next().expect("compute_joint_public is called when cluster has at least one node; qed").clone(); - while let Some(public_share) = public_shares.next() { - math::public_add(&mut joint_public, &public_share)?; - } - Ok(joint_public) + compute_public_sum(public_shares) } #[cfg(test)] /// Compute joint secret key. pub fn compute_joint_secret<'a, I>(mut secret_coeffs: I) -> Result where I: Iterator { - let mut joint_secret = secret_coeffs.next().expect("compute_joint_private is called when cluster has at least one node; qed").clone(); - while let Some(secret_coeff) = secret_coeffs.next() { - joint_secret.add(secret_coeff)?; - } - Ok(joint_secret) + compute_secret_sum(secret_coeffs) } /// Encrypt secret with joint public key. @@ -180,26 +209,8 @@ pub fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result(node_number: &Secret, node_secret_share: &Secret, mut other_nodes_numbers: I) -> Result where I: Iterator { - let other_node_number = match other_nodes_numbers.next() { - Some(other_node_number) => other_node_number, - None => return Ok(node_secret_share.clone()), - }; - - let mut shadow = node_number.clone(); - shadow.sub(other_node_number)?; - shadow.inv()?; - shadow.mul(other_node_number)?; - while let Some(other_node_number) = other_nodes_numbers.next() { - let mut shadow_element = node_number.clone(); - shadow_element.sub(other_node_number)?; - shadow_element.inv()?; - shadow_element.mul(other_node_number)?; - shadow.mul(&shadow_element)?; - } - - shadow.mul(&node_secret_share)?; - Ok(shadow) +pub fn compute_node_shadow<'a, I>(node_secret_share: &Secret, node_number: &Secret, mut other_nodes_numbers: I) -> Result where I: Iterator { + compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers) } /// Compute shadow point for the node. @@ -225,20 +236,13 @@ pub fn compute_node_shadow_point(access_key: &Secret, common_point: &Public, nod /// Compute joint shadow point. pub fn compute_joint_shadow_point<'a, I>(mut nodes_shadow_points: I) -> Result where I: Iterator { - let mut joint_shadow_point = nodes_shadow_points.next().expect("compute_joint_shadow_point is called when at least two nodes are required to decrypt secret; qed").clone(); - while let Some(node_shadow_point) = nodes_shadow_points.next() { - math::public_add(&mut joint_shadow_point, &node_shadow_point)?; - } - Ok(joint_shadow_point) + compute_public_sum(nodes_shadow_points) } #[cfg(test)] /// Compute joint shadow point (version for tests). pub fn compute_joint_shadow_point_test<'a, I>(access_key: &Secret, common_point: &Public, mut nodes_shadows: I) -> Result where I: Iterator { - let mut joint_shadow = nodes_shadows.next().expect("compute_joint_shadow_point_test is called when at least two nodes are required to decrypt secret; qed").clone(); - while let Some(node_shadow) = nodes_shadows.next() { - joint_shadow.add(node_shadow)?; - } + let mut joint_shadow = compute_secret_sum(nodes_shadows)?; joint_shadow.mul(access_key)?; let mut joint_shadow_point = common_point.clone(); @@ -277,10 +281,7 @@ pub fn make_common_shadow_point(threshold: usize, mut common_point: Public) -> R #[cfg(test)] /// Decrypt shadow-encrypted secret. pub fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_shadow_point: Public, shadow_coefficients: Vec) -> Result { - let mut shadow_coefficients_sum = shadow_coefficients[0].clone(); - for shadow_coefficient in shadow_coefficients.iter().skip(1) { - shadow_coefficients_sum.add(shadow_coefficient)?; - } + let shadow_coefficients_sum = compute_secret_sum(shadow_coefficients.iter())?; math::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum)?; math::public_add(&mut decrypted_shadow, &common_shadow_point)?; Ok(decrypted_shadow) @@ -298,11 +299,141 @@ pub fn decrypt_with_joint_secret(encrypted_point: &Public, common_point: &Public Ok(decrypted_point) } +/// Combine message hash with public key X coordinate. +pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> Result { + // buffer is just [message_hash | public.x] + let mut buffer = [0; 64]; + buffer[0..32].copy_from_slice(&message_hash[0..32]); + buffer[32..64].copy_from_slice(&public[0..32]); + + // calculate hash of buffer + let hash = (&buffer[..]).sha3(); + + // map hash to EC finite field value + let hash: U256 = hash.into(); + let hash: H256 = (hash % math::curve_order()).into(); + let hash = Secret::from_slice(&*hash)?; + + Ok(hash) +} + +/// Compute signature share. +pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, mut other_nodes_numbers: I) + -> Result where I: Iterator { + let mut sum = one_time_secret_coeff.clone(); + let mut addendum = compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers)?; + addendum.mul(combined_hash)?; + sum.add(&addendum)?; + Ok(sum) +} + +/// Check signature share. +pub fn check_signature_share<'a, I>(combined_hash: &Secret, signature_share: &Secret, public_share: &Public, one_time_public_share: &Public, node_number: &Secret, mut other_nodes_numbers: I) + -> Result where I: Iterator { + Ok(true) + /* TODO: fix with odd-of-N + let mut left = math::generation_point(); + math::public_mul_secret(&mut left, signature_share)?; + + let right_coeff = compute_shadow_mul(hash, node_number, other_nodes_numbers)?; + let mut right_subtrahend = public_share.clone(); + math::public_mul_secret(&mut right_subtrahend, &right_coeff)?; + let mut right = one_time_public_share.clone(); + math::public_sub(&mut right, &right_subtrahend)?; + + Ok(left == right)*/ +} + +/// Compute signature. +pub fn compute_signature<'a, I>(self_share: &'a Secret, other_shares: I) -> Result where I: Iterator { + compute_secret_sum(other_shares.chain(once(self_share))) +} + +#[cfg(test)] +/// Locally compute Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Signing. +pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &Secret) -> Result<(Secret, Secret), Error> { +/* + let mut R = math::generation_point(); + math::public_mul_secret(&mut R, &k).unwrap(); +println!("schnorr_R = {:?}", R); + let h = combine_message_hash_with_public(&m, &R).unwrap(); + let mut s_right = h.clone(); + s_right.mul(&s).unwrap(); + let mut s = k.clone(); + s.sub(&s_right).unwrap(); + (h, s) +*/ + + let mut nonce_public = math::generation_point(); + math::public_mul_secret(&mut nonce_public, &nonce).unwrap(); + + let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; + + let mut sig_subtrahend = combined_hash.clone(); + sig_subtrahend.mul(secret)?; + let mut sig = nonce.clone(); + sig.sub(&sig_subtrahend)?; + + Ok((combined_hash, sig)) +} + +/// Verify signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. +pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &Secret) -> Result { + let mut addendum = math::generation_point(); + math::public_mul_secret(&mut addendum, &signature.1)?; + let mut nonce_public = public.clone(); + math::public_mul_secret(&mut nonce_public, &signature.0)?; + math::public_add(&mut nonce_public, &addendum)?; + + let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; + Ok(combined_hash == signature.0) +} + #[cfg(test)] pub mod tests { use ethkey::KeyPair; use super::*; + #[derive(Clone)] + struct KeyGenerationArtifacts { + id_numbers: Vec, + polynoms1: Vec>, + secrets1: Vec>, + public_shares: Vec, + secret_shares: Vec, + joint_public: Public, + } + + fn run_key_generation(t: usize, n: usize, id_numbers: Option>) -> KeyGenerationArtifacts { + // === PART1: DKG === + + // data, gathered during initialization + let id_numbers: Vec<_> = match id_numbers { + Some(id_numbers) => id_numbers, + None => (0..n).map(|_| generate_random_scalar().unwrap()).collect(), + }; + + // data, generated during keys dissemination + let polynoms1: Vec<_> = (0..n).map(|_| generate_random_polynom(t).unwrap()).collect(); + let secrets1: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms1[i], &id_numbers[j]).unwrap()).collect::>()).collect(); + + // data, generated during keys generation + let public_shares: Vec<_> = (0..n).map(|i| compute_public_share(&polynoms1[i][0]).unwrap()).collect(); + let secret_shares: Vec<_> = (0..n).map(|i| compute_secret_share(secrets1.iter().map(|s| &s[i])).unwrap()).collect(); + + // joint public key, as a result of DKG + let joint_public = compute_joint_public(public_shares.iter()).unwrap(); + + KeyGenerationArtifacts { + id_numbers: id_numbers, + polynoms1: polynoms1, + secrets1: secrets1, + public_shares: public_shares, + secret_shares: secret_shares, + joint_public: joint_public, + } + } + pub fn do_encryption_and_decryption(t: usize, joint_public: &Public, id_numbers: &[Secret], secret_shares: &[Secret], joint_secret: Option<&Secret>, document_secret_plain: Public) -> (Public, Public) { // === PART2: encryption using joint public key === @@ -316,7 +447,7 @@ pub mod tests { // use t + 1 nodes to compute joint shadow point let nodes_shadows: Vec<_> = (0..t + 1).map(|i| - compute_node_shadow(&id_numbers[i], &secret_shares[i], id_numbers.iter() + compute_node_shadow(&secret_shares[i], &id_numbers[i], id_numbers.iter() .enumerate() .filter(|&(j, _)| j != i) .take(t) @@ -349,39 +480,110 @@ pub mod tests { let test_cases = [(0, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5), (4, 5), (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10)]; for &(t, n) in &test_cases { - // === PART1: DKG === - - // data, gathered during initialization - let id_numbers: Vec<_> = (0..n).map(|_| generate_random_scalar().unwrap()).collect(); - - // data, generated during keys dissemination - let polynoms1: Vec<_> = (0..n).map(|_| generate_random_polynom(t).unwrap()).collect(); - let secrets1: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms1[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - - // data, generated during keys generation - let public_shares: Vec<_> = (0..n).map(|i| compute_public_share(&polynoms1[i][0]).unwrap()).collect(); - let secret_shares: Vec<_> = (0..n).map(|i| compute_secret_share(secrets1.iter().map(|s| &s[i])).unwrap()).collect(); - - // joint public key, as a result of DKG - let joint_public = compute_joint_public(public_shares.iter()).unwrap(); + let artifacts = run_key_generation(t, n, None); // compute joint private key [just for test] - let joint_secret = compute_joint_secret(polynoms1.iter().map(|p| &p[0])).unwrap(); + let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); let joint_key_pair = KeyPair::from_secret(joint_secret.clone()).unwrap(); - assert_eq!(&joint_public, joint_key_pair.public()); + assert_eq!(&artifacts.joint_public, joint_key_pair.public()); // check secret shares computation [just for test] - let secret_shares_polynom: Vec<_> = (0..t + 1).map(|k| compute_secret_share(polynoms1.iter().map(|p| &p[k])).unwrap()).collect(); - let secret_shares_calculated_from_polynom: Vec<_> = id_numbers.iter().map(|id_number| compute_polynom(&*secret_shares_polynom, id_number).unwrap()).collect(); - assert_eq!(secret_shares, secret_shares_calculated_from_polynom); + let secret_shares_polynom: Vec<_> = (0..t + 1).map(|k| compute_secret_share(artifacts.polynoms1.iter().map(|p| &p[k])).unwrap()).collect(); + let secret_shares_calculated_from_polynom: Vec<_> = artifacts.id_numbers.iter().map(|id_number| compute_polynom(&*secret_shares_polynom, id_number).unwrap()).collect(); + assert_eq!(artifacts.secret_shares, secret_shares_calculated_from_polynom); // now encrypt and decrypt data let document_secret_plain = generate_random_point().unwrap(); let (document_secret_decrypted, document_secret_decrypted_test) = - do_encryption_and_decryption(t, &joint_public, &id_numbers, &secret_shares, Some(&joint_secret), document_secret_plain.clone()); + do_encryption_and_decryption(t, &artifacts.joint_public, &artifacts.id_numbers, &artifacts.secret_shares, Some(&joint_secret), document_secret_plain.clone()); assert_eq!(document_secret_plain, document_secret_decrypted_test); assert_eq!(document_secret_plain, document_secret_decrypted); } } + + #[test] + fn local_signature_works() { + let key_pair = Random.generate().unwrap(); + let message_hash = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); + let nonce = generate_random_scalar().unwrap(); + let signature = local_compute_signature(&nonce, key_pair.secret(), &message_hash).unwrap(); + assert_eq!(verify_signature(key_pair.public(), &signature, &message_hash), Ok(true)); + } + + #[test] + fn full_signature_math_session() { + let test_cases = [(1, 4)]; + for &(t, n) in &test_cases { + // hash of the message to be signed + let message_hash: Secret = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); + + // === MiDS-S algorithm === + // setup: all nodes share master secret key && every node knows master public key + let artifacts = run_key_generation(t, n, None); + + // in this gap (not related to math): + // master node should ask every other node if it is able to do a signing + // if there are < than t+1 nodes, able to sign => error + // select t+1 nodes for signing session + // all steps below are for this subset of nodes + let n = t + 1; + + // step 1: run DKG to generate one-time secret key (nonce) + let one_time_artifacts = run_key_generation(t, n, Some(artifacts.id_numbers.clone())); + + // step 2: message hash && x coordinate of one-time public value are combined + let combined_hash = combine_message_hash_with_public(&message_hash, &one_time_artifacts.joint_public).unwrap(); + + // step 3: compute signature shares + let partial_signatures: Vec<_> = (0..n) + .map(|i| compute_signature_share( + &combined_hash, + &one_time_artifacts.polynoms1[i][0], + &artifacts.secret_shares[i], + &artifacts.id_numbers[i], + artifacts.id_numbers.iter() + .enumerate() + .filter(|&(j, _)| i != j) + .map(|(_, n)| n) + .take(t) + ).unwrap()) + .collect(); + + // step 4: receive and verify signatures shares from other nodes + let received_signatures: Vec> = (0..n) + .map(|i| (0..n) + .filter(|j| i != *j) + .map(|j| { + let signature_share = partial_signatures[j].clone(); + assert!(check_signature_share(&combined_hash, + &signature_share, + &artifacts.public_shares[j], + &one_time_artifacts.public_shares[j], + &artifacts.id_numbers[j], + artifacts.id_numbers.iter() + .enumerate() + .filter(|&(k, _)| j != k) + .map(|(_, n)| n) + .take(t)).unwrap_or(false)); + signature_share + }) + .collect()) + .collect(); + + // step 5: compute signature + let signatures: Vec<_> = (0..n) + .map(|i| (combined_hash.clone(), compute_signature(&partial_signatures[i], received_signatures[i].iter()).unwrap())) + .collect(); + + // === verify signature === + let master_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); + let nonce = compute_joint_secret(one_time_artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); + let local_signature = local_compute_signature(&nonce, &master_secret, &message_hash).unwrap(); + for signature in &signatures { + assert_eq!(signature, &local_signature); + assert_eq!(verify_signature(&artifacts.joint_public, signature, &message_hash), Ok(true)); + } + } + } } diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 1ab9c5e02fa..1ad6f964a24 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -90,7 +90,7 @@ pub enum DecryptionMessage { ConfirmDecryptionInitialization(ConfirmDecryptionInitialization), /// Request partial decryption from node. RequestPartialDecryption(RequestPartialDecryption), - /// Partial decryption is completed + /// Partial decryption is completed. PartialDecryption(PartialDecryption), /// When decryption session error has occured. DecryptionSessionError(DecryptionSessionError), @@ -101,6 +101,20 @@ pub enum DecryptionMessage { #[derive(Clone, Debug)] /// All possible messages that can be sent during signing session. pub enum SigningMessage { + /*/// Initialize signing session. + InitializeSigningSession(InitializeSigningSession), + /// Confirm/reject signing session initialization. + ConfirmSigningInitialization(ConfirmSigningInitialization), + /// Nonce generation message. + SigningNonceGeneration(SigningNonceGeneration), + /// Request partial signature from node. + RequestPartialSignature(RequestPartialSignature), + /// Partial signature is generated. + PartialDecryption(PartialDecryption), + /// When signature session error has occured. + SignatureSessionError(SignatureSessionError), + /// When signature session is completed. + SignatureSessionCompleted(SignatureSessionCompleted),*/ } #[derive(Clone, Debug, Serialize, Deserialize)] From 296aa7a78a7b6d0fc03fca49ccc11d518f142669 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 19 May 2017 11:39:23 +0300 Subject: [PATCH 06/45] consensus session prototype --- .../src/key_server_cluster/cluster.rs | 1 + .../key_server_cluster/consensus_session.rs | 235 ++++++++++++++++++ .../src/key_server_cluster/io/message.rs | 9 +- secret_store/src/key_server_cluster/math.rs | 12 - .../src/key_server_cluster/message.rs | 37 ++- secret_store/src/key_server_cluster/mod.rs | 1 + 6 files changed, 281 insertions(+), 14 deletions(-) create mode 100644 secret_store/src/key_server_cluster/consensus_session.rs diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 33d9cf457d1..45365e4892d 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -390,6 +390,7 @@ impl ClusterCore { Message::Decryption(message) => ClusterCore::process_decryption_message(data, connection, message), Message::Signing(message) => ClusterCore::process_signing_message(data, connection, message), Message::Cluster(message) => ClusterCore::process_cluster_message(data, connection, message), + Message::Consensus(_) => (), // consensus messages are always wrapped into other messages } } diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs new file mode 100644 index 00000000000..8539621b543 --- /dev/null +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -0,0 +1,235 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::sync::Arc; +use std::collections::BTreeSet; +use parking_lot::Mutex; +use ethkey::{self, Public, Secret, Signature}; +use util; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; +use key_server_cluster::cluster::{Cluster}; +use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; + +#[derive(Default, Debug, Clone)] +/// Consensus data. +pub struct Consensus { + /// Nodes, which have been requested for signing initialization. + requested_nodes: BTreeSet, + /// Nodes, which have responded with reject to initialization request. + rejected_nodes: BTreeSet, + /// Nodes, which have responded with confirm to initialization request. + confirmed_nodes: BTreeSet, +} + +/// Signing session. +pub struct ConsensusSession { + /// Key generation session id. + id: SessionId, + /// Public identifier of this node. + self_node_id: NodeId, + /// Key generation data. + encrypted_data: DocumentKeyShare, + /// ACL storate to check access to the resource. + acl_storage: Arc, + /// Mutable session data. + data: Mutex, +} + +/// SessionImpl creation parameters +pub struct SessionParams { + /// Key generation session id. + pub id: SessionId, + /// Id of node, on which this session is running. + pub self_node_id: Public, + /// Encrypted data (result of running encryption_session::SessionImpl). + pub encrypted_data: DocumentKeyShare, + /// Key storage. + pub acl_storage: Arc, +} + +#[derive(Debug)] +/// Mutable data of signing session. +struct SessionData { + /// Current state of the session. + state: SessionState, + /// Consensus data. + consensus: Consensus, + /// Consensus result. + result: Option>, +} + +#[derive(Debug, Clone, PartialEq)] +/// Distributed key generation session state. +pub enum SessionState { + // === Initialization states === + /// Every node starts in this state. + WaitingForInitialization, + /// Master node waits for other nodes to confirm session initialization. + WaitingForInitializationConfirm, + + // === Final states of the session === + /// Signing is completed. + Finished, + /// Signing is failed. + Failed, +} + +#[derive(Debug, Clone)] +/// Session action. +pub enum SessionAction { + /// Check session status. + CheckStatus, + /// Broadcast consensus message. + BroadcastMessage(ConsensusMessage), + /// Send consensus message. + SendMessage(NodeId, ConsensusMessage), +} + +impl ConsensusSession { + /// Create new signing session. + pub fn new(params: SessionParams) -> Result { + Ok(ConsensusSession { + id: params.id, + self_node_id: params.self_node_id, + encrypted_data: params.encrypted_data, + acl_storage: params.acl_storage, + data: Mutex::new(SessionData { + state: SessionState::WaitingForInitialization, + consensus: Consensus::default(), + result: None, + }) + }) + } + + /// Get this node Id. + pub fn node(&self) -> &NodeId { + &self.self_node_id + } + + /// Get current session state. + pub fn state(&self) -> SessionState { + self.data.lock().state.clone() + } + + /// Initialize consensus session. + pub fn initialize(&self, requestor_signature: Signature) -> Result { + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitialization { + return Err(Error::InvalidStateForRequest); + } + + // recover requestor signature + let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; + + // update state + data.state = SessionState::WaitingForInitializationConfirm; + data.consensus.requested_nodes.extend(self.encrypted_data.id_numbers.keys().cloned()); + + // ..and finally check access on our's own + let is_permitted = self.acl_storage.check(&requestor_public, &self.id).unwrap_or(false); + process_initialization_response(&self.encrypted_data, &mut *data, self.node(), is_permitted)?; + + // check if we have enough nodes to sign message + match data.state { + // not enough nodes => pass initialization message to all other nodes + SessionState::WaitingForInitializationConfirm => + Ok(SessionAction::BroadcastMessage(ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: requestor_signature.clone().into(), + }))), + // else state must be checked + _ => Ok(SessionAction::CheckStatus), + } + } + + /// When session initialization message is received. + pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeConsensusSession) -> Result { + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitialization { + return Err(Error::InvalidStateForRequest); + } + + // recover requestor signature + let requestor_public = ethkey::recover(&message.requestor_signature, &self.id)?; + + // check access + let is_permitted = self.acl_storage.check(&requestor_public, &self.id).unwrap_or(false); + data.state = if is_permitted { SessionState::Finished } else { SessionState::Failed }; + + // respond to sender + Ok(SessionAction::SendMessage(sender, ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: is_permitted, + }))) + } + + /// When session initialization confirmation message is reeived. + pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmConsensusInitialization) -> Result { + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitializationConfirm { + return Err(Error::InvalidStateForRequest); + } + + // update state + process_initialization_response(&self.encrypted_data, &mut *data, &sender, message.is_confirmed)?; + + // check if we have enough nodes for consensus + match data.state { + // we do not yet have enough nodes for consensus + SessionState::WaitingForInitializationConfirm => Ok(SessionAction::CheckStatus), + // else state must be checked + _ => Ok(SessionAction::CheckStatus), + } + } +} + +fn process_initialization_response(encrypted_data: &DocumentKeyShare, data: &mut SessionData, node: &NodeId, is_permitted: bool) -> Result<(), Error> { + if !data.consensus.requested_nodes.remove(node) { + return Err(Error::InvalidMessage); + } + + match is_permitted { + true => { + data.consensus.confirmed_nodes.insert(node.clone()); + + // check if we have enough nodes for consensus? + if data.consensus.confirmed_nodes.len() == encrypted_data.threshold + 1 { + data.result = Some(Ok(())); + data.state = SessionState::Finished; + } + }, + false => { + data.consensus.rejected_nodes.insert(node.clone()); + + // check if we still can receive enough confirmations for consensus? + if encrypted_data.id_numbers.len() - data.consensus.rejected_nodes.len() < encrypted_data.threshold + 1 { + data.result = Some(Err(Error::AccessDenied)); + data.state = SessionState::Failed; + } + }, + } + + Ok(()) +} diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index e9934ce312f..4d8948f48f7 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -25,7 +25,8 @@ use ethkey::{Public, Secret, KeyPair}; use ethkey::math::curve_order; use util::{H256, U256}; use key_server_cluster::Error; -use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage}; +use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, + DecryptionMessage, ConsensusMessage}; /// Size of serialized header. pub const MESSAGE_HEADER_SIZE: usize = 4; @@ -86,6 +87,9 @@ pub fn serialize_message(message: Message) -> Result { Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (154, serde_json::to_vec(&payload)), Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (155, serde_json::to_vec(&payload)), + Message::Consensus(ConsensusMessage::InitializeConsensusSession(payload)) => (200, serde_json::to_vec(&payload)), + Message::Consensus(ConsensusMessage::ConfirmConsensusInitialization(payload)) => (201, serde_json::to_vec(&payload)), + Message::Signing(_) => unreachable!(), }; @@ -124,6 +128,9 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 155 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 200 => Message::Consensus(ConsensusMessage::InitializeConsensusSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 201 => Message::Consensus(ConsensusMessage::ConfirmConsensusInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + _ => return Err(Error::Serde(format!("unknown message type {}", header.kind))), }) } diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index 1ef95cc4f1b..2eaed6fa7a2 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -352,18 +352,6 @@ pub fn compute_signature<'a, I>(self_share: &'a Secret, other_shares: I) -> Resu #[cfg(test)] /// Locally compute Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Signing. pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &Secret) -> Result<(Secret, Secret), Error> { -/* - let mut R = math::generation_point(); - math::public_mul_secret(&mut R, &k).unwrap(); -println!("schnorr_R = {:?}", R); - let h = combine_message_hash_with_public(&m, &R).unwrap(); - let mut s_right = h.clone(); - s_right.mul(&s).unwrap(); - let mut s = k.clone(); - s.sub(&s_right).unwrap(); - (h, s) -*/ - let mut nonce_public = math::generation_point(); math::public_mul_secret(&mut nonce_public, &nonce).unwrap(); diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 1ad6f964a24..e913140737e 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -32,6 +32,8 @@ pub enum Message { Generation(GenerationMessage), /// Encryption message. Encryption(EncryptionMessage), + /// Consensus message. + Consensus(ConsensusMessage), /// Decryption message. Decryption(DecryptionMessage), /// Signing message. @@ -81,6 +83,15 @@ pub enum EncryptionMessage { EncryptionSessionError(EncryptionSessionError), } +#[derive(Clone, Debug)] +/// All possible messages that can be sent during consensus establishing. +pub enum ConsensusMessage { + /// Initialize consensus session. + InitializeConsensusSession(InitializeConsensusSession), + /// Confirm/reject consensus session initialization. + ConfirmConsensusInitialization(ConfirmConsensusInitialization), +} + #[derive(Clone, Debug)] /// All possible messages that can be sent during decryption session. pub enum DecryptionMessage { @@ -101,7 +112,7 @@ pub enum DecryptionMessage { #[derive(Clone, Debug)] /// All possible messages that can be sent during signing session. pub enum SigningMessage { - /*/// Initialize signing session. +/* /// Initialize signing session. InitializeSigningSession(InitializeSigningSession), /// Confirm/reject signing session initialization. ConfirmSigningInitialization(ConfirmSigningInitialization), @@ -248,6 +259,20 @@ pub struct EncryptionSessionError { pub error: String, } +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Node is asked to be part of consensus group. +pub struct InitializeConsensusSession { + /// Requestor signature. + pub requestor_signature: SerializableSignature, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Node is responding to consensus initialization request. +pub struct ConfirmConsensusInitialization { + /// Is node confirmed consensus participation. + pub is_confirmed: bool, +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Node is requested to decrypt data, encrypted in given session. pub struct InitializeDecryptionSession { @@ -381,6 +406,7 @@ impl fmt::Display for Message { Message::Cluster(ref message) => write!(f, "Cluster.{}", message), Message::Generation(ref message) => write!(f, "Generation.{}", message), Message::Encryption(ref message) => write!(f, "Encryption.{}", message), + Message::Consensus(ref message) => write!(f, "Consensus.{}", message), Message::Decryption(ref message) => write!(f, "Decryption.{}", message), Message::Signing(ref message) => write!(f, "Signing.{}", message), } @@ -422,6 +448,15 @@ impl fmt::Display for EncryptionMessage { } } +impl fmt::Display for ConsensusMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConsensusMessage::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), + ConsensusMessage::ConfirmConsensusInitialization(_) => write!(f, "ConfirmConsensusInitialization"), + } + } +} + impl fmt::Display for DecryptionMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 23c6b63dece..d5e0aab4f28 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -134,6 +134,7 @@ impl Into for Error { mod cluster; mod cluster_sessions; +mod consensus_session; mod decryption_session; mod encryption_session; mod generation_session; From eed126ff559286b77c6fd8bba4fbdec9ae38c1df Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 May 2017 08:45:12 +0300 Subject: [PATCH 07/45] continue signing session --- .../src/key_server_cluster/cluster.rs | 1 + .../key_server_cluster/consensus_session.rs | 6 + .../src/key_server_cluster/message.rs | 15 +- .../src/key_server_cluster/signing_session.rs | 181 +++++++++++++++++- 4 files changed, 200 insertions(+), 3 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 45365e4892d..97b6d583278 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -625,6 +625,7 @@ impl ClusterCore { let decryption_session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone()); let mut sender = connection.node_id().clone(); let session = match message { + _ => unimplemented!(), /* DecryptionMessage::InitializeDecryptionSession(_) => { let mut connected_nodes = data.connections.connected_nodes(); connected_nodes.insert(data.self_key_pair.public().clone()); diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index 8539621b543..55e65d6b0dc 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -125,6 +125,12 @@ impl ConsensusSession { self.data.lock().state.clone() } + /// Get result of consensus. + pub fn consensus(&self) -> Option> { + let data = self.data.lock(); + data.result.clone().map(|r| r.map(|_| data.consensus.clone())) + } + /// Initialize consensus session. pub fn initialize(&self, requestor_signature: Signature) -> Result { let mut data = self.data.lock(); diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index e913140737e..3e11a2d49d0 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -83,7 +83,7 @@ pub enum EncryptionMessage { EncryptionSessionError(EncryptionSessionError), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] /// All possible messages that can be sent during consensus establishing. pub enum ConsensusMessage { /// Initialize consensus session. @@ -112,6 +112,8 @@ pub enum DecryptionMessage { #[derive(Clone, Debug)] /// All possible messages that can be sent during signing session. pub enum SigningMessage { + /// Consensus establishing message. + SigningConsensusMessage(SigningConsensusMessage), /* /// Initialize signing session. InitializeSigningSession(InitializeSigningSession), /// Confirm/reject signing session initialization. @@ -273,6 +275,17 @@ pub struct ConfirmConsensusInitialization { pub is_confirmed: bool, } +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Consensus-related signing message. +pub struct SigningConsensusMessage { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Consensus message. + pub message: ConsensusMessage, +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Node is requested to decrypt data, encrypted in given session. pub struct InitializeDecryptionSession { diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 771c39e1617..f54f5d5f163 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -17,11 +17,15 @@ use std::cmp::{Ordering, Ord, PartialOrd}; use std::sync::Arc; use std::time; -use ethkey::{Public, Secret}; +use parking_lot::{Mutex, Condvar}; +use ethkey::{self, Public, Secret, Signature}; use util; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::consensus_session::{ConsensusSession, Consensus, SessionParams as ConsensusSessionParams, + SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; +use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, ConsensusMessage}; /// Signing session API. pub trait Session: Send + Sync + 'static { @@ -33,6 +37,22 @@ pub trait Session: Send + Sync + 'static { /// Signing session. pub struct SessionImpl { + /// Key generation session id. + id: SessionId, + /// Signing session access key. + access_key: Secret, + /// Public identifier of this node. + self_node_id: NodeId, + /// Encrypted data. + encrypted_data: DocumentKeyShare, + /// ACL storate to check access to the resource. + acl_storage: Arc, + /// Cluster which allows this node to send messages to other nodes in the cluster. + cluster: Arc, + /// SessionImpl completion condvar. + completed: Condvar, + /// Mutable session data. + data: Mutex, } /// Signing session Id. @@ -48,15 +68,18 @@ pub struct SigningSessionId { pub struct SessionParams { /// SessionImpl identifier. pub id: SessionId, + /// SessionImpl access key. + pub access_key: Secret, /// Id of node, on which this session is running. pub self_node_id: Public, + /// Encrypted data (result of running encryption_session::SessionImpl). + pub encrypted_data: DocumentKeyShare, /// Key storage. pub acl_storage: Arc, /// Cluster pub cluster: Arc, } -#[derive(Debug)] /// Mutable data of signing session. struct SessionData { /// Current state of the session. @@ -65,6 +88,18 @@ struct SessionData { // === Values, filled when session initialization just starts === /// Reference to the node, which has started this session. master: Option, + /// Public key of requestor. + requestor: Option, + + // === Values, filled when consensus is establishing === + /// Consensus session. + consensus_session: Option, + /// Consensus params. + consensus: Option, + + /// === Values, filled during final decryption === + /// Decrypted secret + signed_message: Option>, } #[derive(Debug, Clone)] @@ -80,6 +115,8 @@ pub enum SessionState { // === Initialization states === /// Every node starts in this state. WaitingForInitialization, + /// Establishing consensus. + EstablishingConsensus, // === Final states of the session === /// Signing is completed. @@ -89,6 +126,138 @@ pub enum SessionState { } impl SessionImpl { + /// Create new decryption session. + pub fn new(params: SessionParams) -> Result { + check_encrypted_data(¶ms.self_node_id, ¶ms.encrypted_data)?; + + Ok(SessionImpl { + id: params.id, + access_key: params.access_key, + self_node_id: params.self_node_id, + encrypted_data: params.encrypted_data, + acl_storage: params.acl_storage, + cluster: params.cluster, + completed: Condvar::new(), + data: Mutex::new(SessionData { + state: SessionState::WaitingForInitialization, + master: None, + requestor: None, + consensus_session: None, + consensus: None, + signed_message: None, + }) + }) + } + + /// Get this node Id. + pub fn node(&self) -> &NodeId { + &self.self_node_id + } + + /// Initialize signing session. + pub fn initialize(&self, requestor_signature: Signature) -> Result<(), Error> { + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForInitialization { + return Err(Error::InvalidStateForRequest); + } + + // recover requestor signature + let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; + + // update state + data.master = Some(self.node().clone()); + //data.requestor = Some(requestor_public.clone()); + data.state = SessionState::EstablishingConsensus; + + // create consensus session + let consensus_session = ConsensusSession::new(ConsensusSessionParams { + id: self.id.clone(), + self_node_id: self.self_node_id.clone(), + encrypted_data: self.encrypted_data.clone(), + acl_storage: self.acl_storage.clone(), + })?; + + // start consensus session + let consensus_action = consensus_session.initialize(requestor_signature)?; + data.consensus_session = Some(consensus_session); + process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; + + // if single node is required to sign message, proceed + if data.state != SessionState::Failed && data.consensus.is_some() { + //data.state = SessionState::Finished; + //SessionImpl::start_waiting_for_partial_signature(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; + //SessionImpl::make_signature(self.access_key.clone(), &self.encrypted_data, &mut *data)?; + //self.completed.notify_all(); + } + + Ok(()) + } + + /// When consensus-related message is received. + pub fn on_consensus_message(&self, sender: NodeId, message: &SigningConsensusMessage) -> Result<(), Error> { + let mut data = self.data.lock(); + + // if we are waiting for initialization + if data.state == SessionState::WaitingForInitialization { + data.master = Some(sender.clone()); + //data.requestor = Some(requestor_public); + data.state = SessionState::EstablishingConsensus; + data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { + id: self.id.clone(), + self_node_id: self.self_node_id.clone(), + encrypted_data: self.encrypted_data.clone(), + acl_storage: self.acl_storage.clone(), + })?); + } + + // check state + if data.state != SessionState::EstablishingConsensus { + return Err(Error::InvalidStateForRequest); + } + + let consensus_action = match message.message { + ConsensusMessage::InitializeConsensusSession(ref message) => + data.consensus_session.as_ref().expect("TODO").on_initialize_session(sender, &message)?, + ConsensusMessage::ConfirmConsensusInitialization(ref message) => + data.consensus_session.as_ref().expect("TODO").on_confirm_initialization(sender, &message)?, + }; + + process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action) + } +} + +fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { + match action { + ConsensusSessionAction::BroadcastMessage(message) => { + cluster.broadcast(Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + }))) + }, + ConsensusSessionAction::SendMessage(to, message) => { + cluster.send(&to, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + }))) + }, + ConsensusSessionAction::CheckStatus => match data.consensus_session.as_ref().expect("TODO").consensus() { + Some(Ok(consensus)) => { + data.consensus = Some(consensus.clone()); + Ok(()) + }, + Some(Err(err)) => { + data.state = SessionState::Failed; + data.signed_message = Some(Err(err)); + completed.notify_all(); + Ok(()) + }, + None => Ok(()), + }, + } } impl ClusterSession for SessionImpl { @@ -140,3 +309,11 @@ impl Ord for SigningSessionId { } } } + +fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { + use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; + + let nodes = encrypted_data.id_numbers.keys().cloned().collect(); + check_cluster_nodes(self_node_id, &nodes)?; + check_threshold(encrypted_data.threshold, &nodes) +} \ No newline at end of file From 4de05debe891efc4c188871d33a16c5324e338e5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 May 2017 10:59:54 +0300 Subject: [PATCH 08/45] continue signing session --- .../key_server_cluster/cluster_sessions.rs | 2 +- .../key_server_cluster/consensus_session.rs | 6 +- .../key_server_cluster/generation_session.rs | 98 +++--- .../src/key_server_cluster/message.rs | 42 ++- secret_store/src/key_server_cluster/mod.rs | 2 +- .../src/key_server_cluster/signing_session.rs | 287 ++++++++++++++++-- secret_store/src/serialization.rs | 3 + 7 files changed, 355 insertions(+), 85 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 1167823ae07..103ce7389f9 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -128,7 +128,7 @@ impl ClusterSessions { let session = self.generation_sessions.insert(master, session_id, cluster.clone(), GenerationSessionImpl::new(GenerationSessionParams { id: session_id.clone(), self_node_id: self.self_node_id.clone(), - key_storage: self.key_storage.clone(), + key_storage: Some(self.key_storage.clone()), cluster: cluster, })); if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index 55e65d6b0dc..acd55c20353 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -28,11 +28,11 @@ use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, /// Consensus data. pub struct Consensus { /// Nodes, which have been requested for signing initialization. - requested_nodes: BTreeSet, + pub requested_nodes: BTreeSet, /// Nodes, which have responded with reject to initialization request. - rejected_nodes: BTreeSet, + pub rejected_nodes: BTreeSet, /// Nodes, which have responded with confirm to initialization request. - confirmed_nodes: BTreeSet, + pub confirmed_nodes: BTreeSet, } /// Signing session. diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index ac6919cd076..4487ca9c78f 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -52,7 +52,7 @@ pub struct SessionImpl { /// Public identifier of this node. self_node_id: NodeId, /// Key storage. - key_storage: Arc, + key_storage: Option>, /// Cluster which allows this node to send messages to other nodes in the cluster. cluster: Arc, /// SessionImpl completion condvar. @@ -68,7 +68,7 @@ pub struct SessionParams { /// Id of node, on which this session is running. pub self_node_id: Public, /// Key storage. - pub key_storage: Arc, + pub key_storage: Option>, /// Cluster pub cluster: Arc, } @@ -105,6 +105,8 @@ struct SessionData { secret_share: Option, /// === Values, filled when DKG session is completed successfully === + /// Key share. + key_share: Option>, /// Jointly generated public key, which can be used to encrypt secret. Public. joint_public: Option>, } @@ -197,6 +199,7 @@ impl SessionImpl { nodes: BTreeMap::new(), secret_coeff: None, secret_share: None, + key_share: None, joint_public: None, }), } @@ -213,6 +216,11 @@ impl SessionImpl { self.data.lock().derived_point.clone() } + /// Get key share. + pub fn key_share(&self) -> Option> { + self.data.lock().key_share.clone() + } + /// Simulate faulty generation session behaviour. pub fn simulate_faulty_behaviour(&self) { self.data.lock().simulate_faulty_behaviour = true; @@ -250,6 +258,8 @@ impl SessionImpl { self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { session: self.id.clone().into(), author: author.into(), + nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), + threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), derived_point: derived_point.into(), }))) }, @@ -268,6 +278,11 @@ impl SessionImpl { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); + // check message + let nodes_ids = message.nodes.keys().cloned().map(Into::into).collect(); + check_threshold(message.threshold, &nodes_ids)?; + check_cluster_nodes(self.node(), &nodes_ids)?; + let mut data = self.data.lock(); // check state @@ -289,6 +304,8 @@ impl SessionImpl { data.master = Some(sender); data.author = Some(message.author.clone().into()); data.state = SessionState::WaitingForInitializationComplete; + data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); + data.threshold = Some(message.threshold); Ok(()) } @@ -318,6 +335,8 @@ impl SessionImpl { return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { session: self.id.clone().into(), author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), + nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), + threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), derived_point: message.derived_point.clone().into(), }))); } @@ -333,11 +352,6 @@ impl SessionImpl { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); - // check message - let nodes_ids = message.nodes.keys().cloned().map(Into::into).collect(); - check_cluster_nodes(self.node(), &nodes_ids)?; - check_threshold(message.threshold, &nodes_ids)?; - let mut data = self.data.lock(); // check state @@ -349,9 +363,7 @@ impl SessionImpl { } // remember passed data - data.threshold = Some(message.threshold); data.derived_point = Some(message.derived_point.clone().into()); - data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); // now it is time for keys dissemination (KD) phase drop(data); @@ -470,8 +482,11 @@ impl SessionImpl { common_point: None, encrypted_point: None, }; - self.key_storage.insert(self.id.clone(), encrypted_data.clone()) - .map_err(|e| Error::KeyStorage(e.into()))?; + + if let Some(ref key_storage) = self.key_storage { + key_storage.insert(self.id.clone(), encrypted_data.clone()) + .map_err(|e| Error::KeyStorage(e.into()))?; + } // then respond with confirmation data.state = SessionState::Finished; @@ -509,6 +524,7 @@ impl SessionImpl { warn!("{}: generation session failed with error: {} from {}", self.node(), message.error, sender); data.state = SessionState::Failed; + data.key_share = Some(Err(Error::Io(message.error.clone()))); data.joint_public = Some(Err(Error::Io(message.error.clone()))); self.completed.notify_all(); @@ -527,8 +543,6 @@ impl SessionImpl { // broadcast derived point && other session paraeters to every other node self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization { session: self.id.clone().into(), - nodes: data.nodes.iter().map(|(id, data)| (id.clone().into(), data.id_number.clone().into())).collect(), - threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), derived_point: derived_point.into(), }))) } @@ -635,14 +649,7 @@ impl SessionImpl { math::compute_joint_public(public_shares)? }; - // if we are at the slave node - wait for session completion - if data.master.as_ref() != Some(self.node()) { - data.joint_public = Some(Ok(joint_public)); - data.state = SessionState::WaitingForGenerationConfirmation; - return Ok(()); - } - - // then save encrypted data to the key storage + // prepare key data let encrypted_data = DocumentKeyShare { author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), @@ -651,8 +658,20 @@ impl SessionImpl { common_point: None, encrypted_point: None, }; - self.key_storage.insert(self.id.clone(), encrypted_data.clone()) - .map_err(|e| Error::KeyStorage(e.into()))?; + + // if we are at the slave node - wait for session completion + if data.master.as_ref() != Some(self.node()) { + data.key_share = Some(Ok(encrypted_data)); + data.joint_public = Some(Ok(joint_public)); + data.state = SessionState::WaitingForGenerationConfirmation; + return Ok(()); + } + + // then save encrypted data to the key storage + if let Some(ref key_storage) = self.key_storage { + key_storage.insert(self.id.clone(), encrypted_data.clone()) + .map_err(|e| Error::KeyStorage(e.into()))?; + } // then distribute encrypted data to every other node self.cluster.broadcast(Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { @@ -664,6 +683,7 @@ impl SessionImpl { let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); self_node.completion_confirmed = true; } + data.key_share = Some(Ok(encrypted_data)); data.joint_public = Some(Ok(joint_public)); data.state = SessionState::WaitingForGenerationConfirmation; @@ -686,6 +706,7 @@ impl ClusterSession for SessionImpl { warn!("{}: generation session failed because {} connection has timeouted", self.node(), node); data.state = SessionState::Failed; + data.key_share = Some(Err(Error::NodeDisconnected)); data.joint_public = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } @@ -696,6 +717,7 @@ impl ClusterSession for SessionImpl { warn!("{}: generation session failed with timeout", self.node()); data.state = SessionState::Failed; + data.key_share = Some(Err(Error::NodeDisconnected)); data.joint_public = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } @@ -833,7 +855,7 @@ mod tests { let session = SessionImpl::new(SessionParams { id: session_id.clone(), self_node_id: node_id.clone(), - key_storage: Arc::new(DummyKeyStorage::default()), + key_storage: Some(Arc::new(DummyKeyStorage::default())), cluster: cluster.clone(), }); nodes.insert(node_id, Node { cluster: cluster, session: session }); @@ -922,7 +944,7 @@ mod tests { let session = SessionImpl::new(SessionParams { id: SessionId::default(), self_node_id: node_id.clone(), - key_storage: Arc::new(DummyKeyStorage::default()), + key_storage: Some(Arc::new(DummyKeyStorage::default())), cluster: cluster, }); let cluster_nodes: BTreeSet<_> = (0..2).map(|_| math::generate_random_point().unwrap()).collect(); @@ -943,12 +965,9 @@ mod tests { #[test] fn fails_to_accept_initialization_when_already_initialized() { let (sid, m, _, mut l) = make_simple_cluster(0, 2).unwrap(); - l.take_and_process_message().unwrap(); - assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { - session: sid.into(), - author: Public::default().into(), - derived_point: math::generate_random_point().unwrap().into(), - }).unwrap_err(), Error::InvalidStateForRequest); + let message = l.take_message().unwrap(); + l.process_message(message.clone()).unwrap(); + assert_eq!(l.process_message(message.clone()).unwrap_err(), Error::InvalidStateForRequest); } #[test] @@ -1014,8 +1033,9 @@ mod tests { let mut nodes = BTreeMap::new(); nodes.insert(m, math::generate_random_scalar().unwrap()); nodes.insert(math::generate_random_point().unwrap(), math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { + assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { session: sid.into(), + author: Public::default().into(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), threshold: 0, derived_point: math::generate_random_point().unwrap().into(), @@ -1028,8 +1048,9 @@ mod tests { let mut nodes = BTreeMap::new(); nodes.insert(m, math::generate_random_scalar().unwrap()); nodes.insert(s, math::generate_random_scalar().unwrap()); - assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { + assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { session: sid.into(), + author: Public::default().into(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), threshold: 2, derived_point: math::generate_random_point().unwrap().into(), @@ -1039,13 +1060,8 @@ mod tests { #[test] fn fails_to_complete_initialization_if_not_waiting_for_it() { let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(s, math::generate_random_scalar().unwrap()); assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { session: sid.into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - threshold: 0, derived_point: math::generate_random_point().unwrap().into(), }).unwrap_err(), Error::InvalidStateForRequest); } @@ -1057,14 +1073,8 @@ mod tests { l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap(); - let mut nodes = BTreeMap::new(); - nodes.insert(m, math::generate_random_scalar().unwrap()); - nodes.insert(s, math::generate_random_scalar().unwrap()); - nodes.insert(l.second_slave().node().clone(), math::generate_random_scalar().unwrap()); assert_eq!(l.first_slave().on_complete_initialization(l.second_slave().node().clone(), &message::CompleteInitialization { session: sid.into(), - nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - threshold: 0, derived_point: math::generate_random_point().unwrap().into(), }).unwrap_err(), Error::InvalidMessage); } diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 3e11a2d49d0..25d1bff8ff0 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -18,7 +18,7 @@ use std::fmt; use std::collections::{BTreeSet, BTreeMap}; use ethkey::Secret; use key_server_cluster::SessionId; -use super::{SerializableH256, SerializablePublic, SerializableSecret, SerializableSignature}; +use super::{SerializableH256, SerializablePublic, SerializableSecret, SerializableSignature, SerializableMessageHash}; pub type MessageSessionId = SerializableH256; pub type MessageNodeId = SerializablePublic; @@ -53,7 +53,7 @@ pub enum ClusterMessage { KeepAliveResponse(KeepAliveResponse), } -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Serialize, Deserialize)] /// All possible messages that can be sent during key generation session. pub enum GenerationMessage { /// Initialize new DKG session. @@ -114,14 +114,16 @@ pub enum DecryptionMessage { pub enum SigningMessage { /// Consensus establishing message. SigningConsensusMessage(SigningConsensusMessage), + /// Session key generation message. + SigningGenerationMessage(SigningGenerationMessage), + /// Request partial signature from node. + RequestPartialSignature(RequestPartialSignature), /* /// Initialize signing session. InitializeSigningSession(InitializeSigningSession), /// Confirm/reject signing session initialization. ConfirmSigningInitialization(ConfirmSigningInitialization), /// Nonce generation message. SigningNonceGeneration(SigningNonceGeneration), - /// Request partial signature from node. - RequestPartialSignature(RequestPartialSignature), /// Partial signature is generated. PartialDecryption(PartialDecryption), /// When signature session error has occured. @@ -164,6 +166,11 @@ pub struct InitializeSession { pub session: MessageSessionId, /// Session author. pub author: SerializablePublic, + /// All session participants along with their identification numbers. + pub nodes: BTreeMap, + /// Decryption threshold. During decryption threshold-of-route.len() nodes must came to + /// consensus to successfully decrypt message. + pub threshold: usize, /// Derived generation point. Starting from originator, every node must multiply this /// point by random scalar (unknown by other nodes). At the end of initialization /// `point` will be some (k1 * k2 * ... * kn) * G = `point` where `(k1 * k2 * ... * kn)` @@ -185,11 +192,6 @@ pub struct ConfirmInitialization { pub struct CompleteInitialization { /// Session Id. pub session: MessageSessionId, - /// All session participants along with their identification numbers. - pub nodes: BTreeMap, - /// Decryption threshold. During decryption threshold-of-route.len() nodes must came to - /// consensus to successfully decrypt message. - pub threshold: usize, /// Derived generation point. pub derived_point: SerializablePublic, } @@ -286,6 +288,28 @@ pub struct SigningConsensusMessage { pub message: ConsensusMessage, } +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Session key generation message. +pub struct SigningGenerationMessage { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Generation message. + pub message: GenerationMessage, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Request partial signature. +pub struct RequestPartialSignature { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Message hash. + pub message_hash: SerializableMessageHash, +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Node is requested to decrypt data, encrypted in given session. pub struct InitializeDecryptionSession { diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index d5e0aab4f28..3059203045b 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -23,7 +23,7 @@ use super::types::all::ServerKeyId; pub use super::types::all::{NodeId, EncryptedDocumentKeyShadow}; pub use super::acl_storage::AclStorage; pub use super::key_storage::{KeyStorage, DocumentKeyShare}; -pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic}; +pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic, SerializableMessageHash}; pub use self::cluster::{ClusterCore, ClusterConfiguration, ClusterClient}; pub use self::generation_session::Session as GenerationSession; pub use self::encryption_session::Session as EncryptionSession; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index f54f5d5f163..41297219e39 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -14,18 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::collections::{BTreeSet, VecDeque}; +use std::mem::swap; use std::cmp::{Ordering, Ord, PartialOrd}; use std::sync::Arc; use std::time; use parking_lot::{Mutex, Condvar}; use ethkey::{self, Public, Secret, Signature}; -use util; +use util::{self, H256}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::consensus_session::{ConsensusSession, Consensus, SessionParams as ConsensusSessionParams, SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; -use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, ConsensusMessage}; +use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, + SessionState as GenerationSessionState}; +use key_server_cluster::math; +use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, + RequestPartialSignature, GenerationMessage, ConsensusMessage}; /// Signing session API. pub trait Session: Send + Sync + 'static { @@ -90,6 +96,8 @@ struct SessionData { master: Option, /// Public key of requestor. requestor: Option, + /// Hash of the message to sign. + message_hash: Option, // === Values, filled when consensus is establishing === /// Consensus session. @@ -97,6 +105,22 @@ struct SessionData { /// Consensus params. consensus: Option, + // === Values, filled when session key is generating === + /// Signing cluster subgroup. + generation_cluster: Option>, + /// Session key generation session. + generation_session: Option, + /// Generated session key. + session_key: Option, + + // === Values, filled when partial signatures are generating === + /// Nodes which have agreed to make partial signatures. + confirmed_nodes: BTreeSet, + /// Active partial requests. + partial_requests: BTreeSet, + /// Partial signatures. + partial_signatures: VecDeque, + /// === Values, filled during final decryption === /// Decrypted secret signed_message: Option>, @@ -118,6 +142,10 @@ pub enum SessionState { /// Establishing consensus. EstablishingConsensus, + // === Intermediate states === + /// Generating one-time key. + SessionKeyGeneration, + // === Final states of the session === /// Signing is completed. Finished, @@ -125,6 +153,16 @@ pub enum SessionState { Failed, } +/// Signing group of cluster nodes. +struct SigningCluster { + /// Original cluster reference. + cluster: Arc, + /// Signing group. + nodes: BTreeSet, + /// Generation session messages. + messages: Mutex>, +} + impl SessionImpl { /// Create new decryption session. pub fn new(params: SessionParams) -> Result { @@ -142,8 +180,15 @@ impl SessionImpl { state: SessionState::WaitingForInitialization, master: None, requestor: None, + message_hash: None, consensus_session: None, consensus: None, + generation_cluster: None, + generation_session: None, + session_key: None, + confirmed_nodes: BTreeSet::new(), + partial_requests: BTreeSet::new(), + partial_signatures: VecDeque::new(), signed_message: None, }) }) @@ -155,7 +200,7 @@ impl SessionImpl { } /// Initialize signing session. - pub fn initialize(&self, requestor_signature: Signature) -> Result<(), Error> { + pub fn initialize(&self, requestor_signature: Signature, message_hash: H256) -> Result<(), Error> { let mut data = self.data.lock(); // check state @@ -167,9 +212,10 @@ impl SessionImpl { let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; // update state + data.state = SessionState::EstablishingConsensus; data.master = Some(self.node().clone()); //data.requestor = Some(requestor_public.clone()); - data.state = SessionState::EstablishingConsensus; + data.message_hash = Some(message_hash); // create consensus session let consensus_session = ConsensusSession::new(ConsensusSessionParams { @@ -182,7 +228,7 @@ impl SessionImpl { // start consensus session let consensus_action = consensus_session.initialize(requestor_signature)?; data.consensus_session = Some(consensus_session); - process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; + SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; // if single node is required to sign message, proceed if data.state != SessionState::Failed && data.consensus.is_some() { @@ -216,37 +262,131 @@ impl SessionImpl { if data.state != SessionState::EstablishingConsensus { return Err(Error::InvalidStateForRequest); } + // TODO: check master node + etc + // process message let consensus_action = match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => data.consensus_session.as_ref().expect("TODO").on_initialize_session(sender, &message)?, ConsensusMessage::ConfirmConsensusInitialization(ref message) => data.consensus_session.as_ref().expect("TODO").on_confirm_initialization(sender, &message)?, }; + SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; - process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action) + // if consensus is reached, start generating session key + if data.state == SessionState::Failed || data.consensus.is_some() { + return Ok(()); + } + SessionImpl::start_generating_session_key(self.self_node_id.clone(), &self.encrypted_data, &self.cluster, &mut *data)?; + SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data) } -} -fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { - match action { - ConsensusSessionAction::BroadcastMessage(message) => { - cluster.broadcast(Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - }))) - }, - ConsensusSessionAction::SendMessage(to, message) => { - cluster.send(&to, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - }))) - }, - ConsensusSessionAction::CheckStatus => match data.consensus_session.as_ref().expect("TODO").consensus() { - Some(Ok(consensus)) => { - data.consensus = Some(consensus.clone()); + /// When session key related message is received. + pub fn on_generation_message(&self, sender: NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::EstablishingConsensus { + return Err(Error::InvalidStateForRequest); + } + // TODO: check master node + etc + + // process message + match message.message { + GenerationMessage::InitializeSession(ref message) => { + // if we are NOT part of consensus + if data.consensus_session.as_ref().expect("TODO").state() != ConsensusSessionState::Finished { + return Err(Error::InvalidStateForRequest); + } + + // update state + data.state = SessionState::SessionKeyGeneration; + + // create generation session + let generation_cluster = Arc::new(SigningCluster::new(self.cluster.clone(), message.nodes.keys().cloned().map(Into::into).collect())); + data.generation_cluster = Some(generation_cluster.clone()); + data.generation_session = Some(GenerationSession::new(GenerationSessionParams { + id: message.session.clone().into(), + self_node_id: self.self_node_id.clone(), + key_storage: None, + cluster: generation_cluster, + })); + + // process initialization message + data.generation_session.as_ref().expect("TODO").on_initialize_session(sender, &message)? + }, + GenerationMessage::ConfirmInitialization(ref message) => + data.generation_session.as_ref().expect("TODO").on_confirm_initialization(sender, &message)?, + GenerationMessage::CompleteInitialization(ref message) => + data.generation_session.as_ref().expect("TODO").on_complete_initialization(sender, &message)?, + GenerationMessage::KeysDissemination(ref message) => + data.generation_session.as_ref().expect("TODO").on_keys_dissemination(sender, &message)?, + GenerationMessage::PublicKeyShare(ref message) => + data.generation_session.as_ref().expect("TODO").on_public_key_share(sender, &message)?, + GenerationMessage::SessionError(ref message) => + data.generation_session.as_ref().expect("TODO").on_session_error(sender, &message)?, + GenerationMessage::SessionCompleted(ref message) => + data.generation_session.as_ref().expect("TODO").on_session_completed(sender, &message)?, + }; + SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; + + // if session key generated is not yet completed => continue + if data.state == SessionState::Failed || !data.session_key.is_some() { + return Ok(()); + } + + // else ask other nodes to generate partial signatures + SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) + } + + fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { + match action { + ConsensusSessionAction::BroadcastMessage(message) => { + cluster.broadcast(Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + }))) + }, + ConsensusSessionAction::SendMessage(to, message) => { + cluster.send(&to, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + }))) + }, + ConsensusSessionAction::CheckStatus => match data.consensus_session.as_ref().expect("TODO").consensus() { + Some(Ok(consensus)) => { + data.consensus = Some(consensus.clone()); + Ok(()) + }, + Some(Err(err)) => { + data.state = SessionState::Failed; + data.signed_message = Some(Err(err)); + completed.notify_all(); + Ok(()) + }, + None => Ok(()), + }, + } + } + + fn process_generation_session_action(id: &SessionId, access_key: &Secret, completed: &Condvar, data: &mut SessionData) -> Result<(), Error> { + let generation_cluster = data.generation_cluster.as_ref().expect("TODO").clone(); + for (to, message) in generation_cluster.messages() { + match message { + Message::Generation(message) => generation_cluster.cluster().send(&to, Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + })))?, + _ => unreachable!("generation session only sends generation messages"), + } + } + + match data.generation_session.as_ref().expect("TODO").key_share() { + Some(Ok(key_share)) => { + data.session_key = Some(key_share); Ok(()) }, Some(Err(err)) => { @@ -256,7 +396,64 @@ fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster Ok(()) }, None => Ok(()), - }, + } + } + + fn start_generating_session_key(self_node_id: NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { + // select nodes to make signature + let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").confirmed_nodes.clone(); + let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.consensus.as_ref().expect("TODO").rejected_nodes).cloned().collect(); + + // create generation session + let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), confirmed_nodes.clone())); + let generation_session = GenerationSession::new(GenerationSessionParams { + id: H256::default(), // doesn't matter + self_node_id: self_node_id.clone(), + key_storage: None, + cluster: generation_cluster.clone(), + }); + + // start generation session + let result = generation_session.initialize(data.requestor.as_ref().expect("TODO").clone(), encrypted_data.threshold, confirmed_nodes)?; + data.generation_cluster = Some(generation_cluster); + data.generation_session = Some(generation_session); + + Ok(()) + } + + fn start_waiting_for_partial_decryption(self_node_id: NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + // nodes which have formed consensus group + let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").confirmed_nodes.clone(); + let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.consensus.as_ref().expect("TODO").rejected_nodes).cloned().collect(); + + // send requests + data.partial_requests.clear(); + data.partial_signatures.clear(); + for node in confirmed_nodes.iter().filter(|n| n != &&self_node_id) { + data.partial_requests.insert(node.clone()); + cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { + session: session_id.clone().into(), + sub_session: access_key.clone().into(), + message_hash: data.message_hash.as_ref().expect("TODO").clone().into(), + })))?; + } + + // confirmation from this node + data.confirmed_nodes = confirmed_nodes; + if data.confirmed_nodes.remove(&self_node_id) { + let signing_result = { + //let requestor = data.requestor.as_ref().expect("TODO"); + //do_partial_signing(&self_node_id, &requestor, &data.confirmed_nodes, &access_key, &encrypted_data)? + SessionImpl::do_partial_signing()? + }; + data.partial_signatures.push_back(signing_result); + } + + Ok(()) + } + + fn do_partial_signing() -> Result { + unimplemented!() } } @@ -274,6 +471,42 @@ impl ClusterSession for SessionImpl { } } +impl SigningCluster { + pub fn new(cluster: Arc, subset: BTreeSet) -> Self { + SigningCluster { + cluster: cluster, + nodes: subset, + messages: Mutex::new(VecDeque::new()), + } + } + + pub fn cluster(&self) -> &Arc { + &self.cluster + } + + pub fn messages(&self) -> VecDeque<(NodeId, Message)> { + let mut lock = self.messages.lock(); + let mut messages = VecDeque::new(); + swap(&mut messages, &mut *lock); + messages + } +} + +impl Cluster for SigningCluster { + fn broadcast(&self, message: Message) -> Result<(), Error> { + let mut messages = self.messages.lock(); + for node in &self.nodes { + messages.push_back((node.clone(), message.clone())); + } + Ok(()) + } + + fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { + self.messages.lock().push_back((to.clone(), message.clone())); + Ok(()) + } +} + impl Session for SessionImpl { fn state(&self) -> SessionState { unimplemented!() diff --git a/secret_store/src/serialization.rs b/secret_store/src/serialization.rs index 4c047be954e..045afa894dd 100644 --- a/secret_store/src/serialization.rs +++ b/secret_store/src/serialization.rs @@ -23,6 +23,9 @@ use serde::de::{Visitor, Error as SerdeError}; use ethkey::{Public, Secret, Signature}; use util::{H256, Bytes}; +/// Serializable message hash. +pub type SerializableMessageHash = SerializableH256; + #[derive(Clone, Debug, Serialize, Deserialize)] /// Serializable shadow decryption result. pub struct SerializableEncryptedDocumentKeyShadow { From b29ac816cda5f32f0a6f6f9fb5593be7409555f8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 May 2017 11:39:57 +0300 Subject: [PATCH 09/45] continue signing session --- .../key_server_cluster/generation_session.rs | 15 ++++++- .../src/key_server_cluster/signing_session.rs | 45 +++++++++++++------ 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index 4487ca9c78f..b6f4b084e6b 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -33,7 +33,6 @@ pub trait Session: Send + Sync + 'static { fn state(&self) -> SessionState; /// Wait until session is completed. Returns public portion of generated server key. fn wait(&self, timeout: Option) -> Result; - #[cfg(test)] /// Get joint public key (if it is known). fn joint_public_key(&self) -> Option>; } @@ -109,6 +108,8 @@ struct SessionData { key_share: Option>, /// Jointly generated public key, which can be used to encrypt secret. Public. joint_public: Option>, + /// Secret coefficient (should not be stored anywhere!!!). + secret_coeff_result: Option>, } #[derive(Debug, Clone)] @@ -201,6 +202,7 @@ impl SessionImpl { secret_share: None, key_share: None, joint_public: None, + secret_coeff_result: None, }), } } @@ -221,6 +223,11 @@ impl SessionImpl { self.data.lock().key_share.clone() } + /// Get secret polynom coefficient. + pub fn secret_coeff(&self) -> Option> { + self.data.lock().secret_coeff_result.clone() + } + /// Simulate faulty generation session behaviour. pub fn simulate_faulty_behaviour(&self) { self.data.lock().simulate_faulty_behaviour = true; @@ -526,6 +533,7 @@ impl SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::Io(message.error.clone()))); data.joint_public = Some(Err(Error::Io(message.error.clone()))); + data.secret_coeff_result = Some(Err(Error::Io(message.error.clone()))); self.completed.notify_all(); Ok(()) @@ -663,6 +671,7 @@ impl SessionImpl { if data.master.as_ref() != Some(self.node()) { data.key_share = Some(Ok(encrypted_data)); data.joint_public = Some(Ok(joint_public)); + data.secret_coeff_result = Some(Ok(data.secret_coeff.as_ref().expect("TODO").clone())); data.state = SessionState::WaitingForGenerationConfirmation; return Ok(()); } @@ -685,6 +694,7 @@ impl SessionImpl { } data.key_share = Some(Ok(encrypted_data)); data.joint_public = Some(Ok(joint_public)); + data.secret_coeff_result = Some(Ok(data.secret_coeff.as_ref().expect("TODO").clone())); data.state = SessionState::WaitingForGenerationConfirmation; Ok(()) @@ -708,6 +718,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::NodeDisconnected)); data.joint_public = Some(Err(Error::NodeDisconnected)); + data.secret_coeff_result = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } @@ -719,6 +730,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::NodeDisconnected)); data.joint_public = Some(Err(Error::NodeDisconnected)); + data.secret_coeff_result = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } } @@ -742,7 +754,6 @@ impl Session for SessionImpl { .clone() } - #[cfg(test)] fn joint_public_key(&self) -> Option> { self.data.lock().joint_public.clone() } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 41297219e39..2ae66020272 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -28,7 +28,7 @@ use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::consensus_session::{ConsensusSession, Consensus, SessionParams as ConsensusSessionParams, SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - SessionState as GenerationSessionState}; + SessionState as GenerationSessionState, Session as GenerationSessionApi}; use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, RequestPartialSignature, GenerationMessage, ConsensusMessage}; @@ -110,8 +110,10 @@ struct SessionData { generation_cluster: Option>, /// Session key generation session. generation_session: Option, - /// Generated session key. - session_key: Option, + /// Generated session public key. + session_joint_public: Option, + /// Generated session secret coefficient. + session_secret_coeff: Option, // === Values, filled when partial signatures are generating === /// Nodes which have agreed to make partial signatures. @@ -185,7 +187,8 @@ impl SessionImpl { consensus: None, generation_cluster: None, generation_session: None, - session_key: None, + session_joint_public: None, + session_secret_coeff: None, confirmed_nodes: BTreeSet::new(), partial_requests: BTreeSet::new(), partial_signatures: VecDeque::new(), @@ -331,12 +334,12 @@ impl SessionImpl { SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; // if session key generated is not yet completed => continue - if data.state == SessionState::Failed || !data.session_key.is_some() { + if data.state == SessionState::Failed || !data.session_joint_public.is_some() { return Ok(()); } // else ask other nodes to generate partial signatures - SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) + SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { @@ -384,9 +387,10 @@ impl SessionImpl { } } - match data.generation_session.as_ref().expect("TODO").key_share() { - Some(Ok(key_share)) => { - data.session_key = Some(key_share); + match data.generation_session.as_ref().expect("TODO").joint_public_key() { + Some(Ok(session_joint_public)) => { + data.session_joint_public = Some(session_joint_public); + data.session_secret_coeff = Some(data.generation_session.as_ref().expect("TODO").secret_coeff().expect("TODO").expect("TODO")); Ok(()) }, Some(Err(err)) => { @@ -421,7 +425,7 @@ impl SessionImpl { Ok(()) } - fn start_waiting_for_partial_decryption(self_node_id: NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // nodes which have formed consensus group let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").confirmed_nodes.clone(); let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.consensus.as_ref().expect("TODO").rejected_nodes).cloned().collect(); @@ -429,7 +433,7 @@ impl SessionImpl { // send requests data.partial_requests.clear(); data.partial_signatures.clear(); - for node in confirmed_nodes.iter().filter(|n| n != &&self_node_id) { + for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { data.partial_requests.insert(node.clone()); cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { session: session_id.clone().into(), @@ -442,9 +446,12 @@ impl SessionImpl { data.confirmed_nodes = confirmed_nodes; if data.confirmed_nodes.remove(&self_node_id) { let signing_result = { + let message_hash = data.message_hash.as_ref().expect("TODO"); + let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); + let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); //let requestor = data.requestor.as_ref().expect("TODO"); //do_partial_signing(&self_node_id, &requestor, &data.confirmed_nodes, &access_key, &encrypted_data)? - SessionImpl::do_partial_signing()? + SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &data.confirmed_nodes, session_joint_public, session_secret_coeff)? }; data.partial_signatures.push_back(signing_result); } @@ -452,8 +459,18 @@ impl SessionImpl { Ok(()) } - fn do_partial_signing() -> Result { - unimplemented!() + fn do_partial_signing(self_node_id: &NodeId, message_hash: &H256, encrypted_data: &DocumentKeyShare, session_nodes: &BTreeSet, session_joint_public: &Public, session_secret_coeff: &Secret) -> Result { + debug_assert!(!session_nodes.contains(self_node_id)); + debug_assert!(session_nodes.len() == encrypted_data.threshold); + + let combined_hash = math::combine_message_hash_with_public(&message_hash, &session_joint_public)?; + math::compute_signature_share( + &combined_hash, + &session_secret_coeff, + &encrypted_data.secret_share, + &encrypted_data.id_numbers[self_node_id], + session_nodes.iter().map(|n| &encrypted_data.id_numbers[n]) + ) } } From 495e45fc5a2610ad80d6731a1699c2b0ffc54f6c Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 May 2017 12:18:38 +0300 Subject: [PATCH 10/45] continue signing session --- .../key_server_cluster/consensus_session.rs | 2 +- .../src/key_server_cluster/message.rs | 28 +++++- .../src/key_server_cluster/signing_session.rs | 99 +++++++++++++++++-- 3 files changed, 119 insertions(+), 10 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index acd55c20353..6bcbe3d3db0 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -25,7 +25,7 @@ use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; #[derive(Default, Debug, Clone)] -/// Consensus data. +/// Consensus data. TODO: also move restart logic here (it is in signing + decryption) pub struct Consensus { /// Nodes, which have been requested for signing initialization. pub requested_nodes: BTreeSet, diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 25d1bff8ff0..92926b80ca4 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -118,18 +118,20 @@ pub enum SigningMessage { SigningGenerationMessage(SigningGenerationMessage), /// Request partial signature from node. RequestPartialSignature(RequestPartialSignature), + /// Partial signature is generated. + PartialSignature(PartialSignature), + /// Partial signature is generated. + SigningSessionCompleted(SigningSessionCompleted), /* /// Initialize signing session. InitializeSigningSession(InitializeSigningSession), /// Confirm/reject signing session initialization. ConfirmSigningInitialization(ConfirmSigningInitialization), /// Nonce generation message. SigningNonceGeneration(SigningNonceGeneration), - /// Partial signature is generated. - PartialDecryption(PartialDecryption), /// When signature session error has occured. SignatureSessionError(SignatureSessionError), /// When signature session is completed. - SignatureSessionCompleted(SignatureSessionCompleted),*/ + ,*/ } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -310,6 +312,26 @@ pub struct RequestPartialSignature { pub message_hash: SerializableMessageHash, } +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Partial signature. +pub struct PartialSignature { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// S part of signature. + pub partial_signature: SerializableSecret, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +/// Signing session completed. +pub struct SigningSessionCompleted { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Node is requested to decrypt data, encrypted in given session. pub struct InitializeDecryptionSession { diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 2ae66020272..835f8f0c6ba 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -31,7 +31,7 @@ use key_server_cluster::generation_session::{SessionImpl as GenerationSession, S SessionState as GenerationSessionState, Session as GenerationSessionApi}; use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, - RequestPartialSignature, GenerationMessage, ConsensusMessage}; + RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage}; /// Signing session API. pub trait Session: Send + Sync + 'static { @@ -125,7 +125,7 @@ struct SessionData { /// === Values, filled during final decryption === /// Decrypted secret - signed_message: Option>, + signed_message: Option>, } #[derive(Debug, Clone)] @@ -147,6 +147,8 @@ pub enum SessionState { // === Intermediate states === /// Generating one-time key. SessionKeyGeneration, + /// Waiting for partial signatures. + WaitingForPartialSignature, // === Final states of the session === /// Signing is completed. @@ -339,7 +341,81 @@ impl SessionImpl { } // else ask other nodes to generate partial signatures - SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) + SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) + } + + fn on_partial_signature_requested(&self, sender: NodeId, message: &RequestPartialSignature) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(self.access_key == *message.sub_session); + debug_assert!(&sender != self.node()); + + // TODO: check message + //if message.nodes.len() != self.encrypted_data.threshold + 1 { + // return Err(Error::InvalidMessage); + //} + + let data = self.data.lock(); + + // check state + if data.master != Some(sender) { + return Err(Error::InvalidMessage); + } + if data.state != SessionState::EstablishingConsensus && data.generation_session.as_ref().expect("TODO").state() != GenerationSessionState::Finished { + return Err(Error::InvalidStateForRequest); + } + + // calculate partial signature + let message_hash = data.message_hash.as_ref().expect("TODO"); + let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); + let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); + let partial_signature = SessionImpl::do_partial_signing(&self.self_node_id, message_hash, &self.encrypted_data, &data.confirmed_nodes, session_joint_public, session_secret_coeff)?; + + self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + partial_signature: partial_signature.into(), + })))?; + + // master could ask us for another partial signature in case of restart + // => no state change is required + + Ok(()) + } + + /// When partial signature is received. + pub fn on_partial_signature(&self, sender: NodeId, message: &PartialSignature) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(self.access_key == *message.sub_session); + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + + // check state + if data.state != SessionState::WaitingForPartialSignature { + return Err(Error::InvalidStateForRequest); + } + + if !data.partial_requests.remove(&sender) { + return Err(Error::InvalidStateForRequest); + } + data.partial_signatures.push_back(message.partial_signature.clone().into()); + + // check if we have enough shadow points to decrypt the secret + if data.partial_signatures.len() != self.encrypted_data.threshold + 1 { + return Ok(()); + } + + // notify all other nodes about session completion + self.cluster.broadcast(Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + })))?; + + // do signing + SessionImpl::do_signing(&mut *data)?; + self.completed.notify_all(); + + Ok(()) } fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { @@ -425,7 +501,7 @@ impl SessionImpl { Ok(()) } - fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // nodes which have formed consensus group let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").confirmed_nodes.clone(); let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.consensus.as_ref().expect("TODO").rejected_nodes).cloned().collect(); @@ -443,14 +519,13 @@ impl SessionImpl { } // confirmation from this node + data.state = SessionState::WaitingForPartialSignature; data.confirmed_nodes = confirmed_nodes; if data.confirmed_nodes.remove(&self_node_id) { let signing_result = { let message_hash = data.message_hash.as_ref().expect("TODO"); let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); - //let requestor = data.requestor.as_ref().expect("TODO"); - //do_partial_signing(&self_node_id, &requestor, &data.confirmed_nodes, &access_key, &encrypted_data)? SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &data.confirmed_nodes, session_joint_public, session_secret_coeff)? }; data.partial_signatures.push_back(signing_result); @@ -472,6 +547,18 @@ impl SessionImpl { session_nodes.iter().map(|n| &encrypted_data.id_numbers[n]) ) } + + fn do_signing(data: &mut SessionData) -> Result<(), Error> { + let message_hash = data.message_hash.as_ref().expect("TODO"); + let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); + + let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; + let signature_s = math::compute_signature(&data.partial_signatures[0], data.partial_signatures.iter().skip(1))?; + + data.signed_message = Some(Ok((signature_c, signature_s))); + + Ok(()) + } } impl ClusterSession for SessionImpl { From 20cba71693c250027cb119a4cd99370a312e113e Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 May 2017 16:35:14 +0300 Subject: [PATCH 11/45] isolated consensus logic --- .../src/key_server_cluster/consensus.rs | 348 ++++++++++++++++++ .../key_server_cluster/consensus_session.rs | 209 +++++------ .../key_server_cluster/decryption_session.rs | 4 +- secret_store/src/key_server_cluster/mod.rs | 7 + .../src/key_server_cluster/signing_session.rs | 54 ++- 5 files changed, 481 insertions(+), 141 deletions(-) create mode 100644 secret_store/src/key_server_cluster/consensus.rs diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs new file mode 100644 index 00000000000..1455781a92d --- /dev/null +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -0,0 +1,348 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + +use std::collections::{BTreeSet, BTreeMap, VecDeque}; +use key_server_cluster::{Error, NodeId}; + +/// Consensus. +pub enum Consensus { + /// Consensus is currently establishing. + Establishing(ConsensusCore), + /// Consensus is established. + Established(ConsensusCore), + /// Consensus nodes are currently doing their job. + Active(ActiveConsensus), + /// All consensus nodes have responded. + Completed(ActiveConsensus), + /// Consensus is unreachable. + Unreachable, +} + +#[derive(Debug, Clone)] +/// Consensus core data. +pub struct ConsensusCore { + /// Consensus threshold. + pub threshold: usize, + /// Nodes, which have been requested for participatining in consensus, but not yet responded. + pub requested_nodes: BTreeSet, + /// Nodes, which have responded with reject to participation request. + pub rejected_nodes: BTreeSet, + /// Nodes, which have responded with confirm to participation request. + pub confirmed_nodes: BTreeSet, +} + +#[derive(Debug, Clone)] +/// Active consensus (i.e. consensus with sent requests). +pub struct ActiveConsensus { + /// Consensus core data. + pub core: ConsensusCore, + /// Selected nodes. + pub selected_nodes: BTreeSet, + /// Active job requests to confirmed nodes. + pub active_requests: BTreeSet, + /// Confirmed nodes responses. + pub responses: BTreeMap, +} + +impl Consensus { + /// Create new consensus. + pub fn new(threshold: usize, nodes: BTreeSet) -> Result { + if nodes.len() < threshold + 1 { + return Err(Error::InvalidThreshold); + } + + Ok(Consensus::Establishing(ConsensusCore { + threshold: threshold, + requested_nodes: nodes, + rejected_nodes: BTreeSet::new(), + confirmed_nodes: BTreeSet::new(), + })) + } + + /// Is consenus established. + pub fn is_established(&self) -> bool { + match *self { + Consensus::Established(_) | Consensus::Active(_) => true, + _ => false, + } + } + + /// When node responds to join offer. + pub fn offer_response(&mut self, node: &NodeId, is_accepted: bool) -> Result<(), Error> { + if is_accepted { + self.accept_offer(node) + } else { + self.reject_offer(node) + } + } + + /// When node has accepted join offer. + pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { + let established_consensus = match *self { + Consensus::Establishing(ref mut consensus) => { + consensus.accept_offer(node)?; + if consensus.confirmed_nodes.len() != consensus.threshold + 1 { + return Ok(()); + } + + consensus.clone() + }, + Consensus::Established(ref mut consensus) => return consensus.accept_offer(node), + Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => + return consensus.core.accept_offer(node), + Consensus::Unreachable => return Err(Error::InvalidStateForRequest), + }; + + *self = Consensus::Established(established_consensus); + Ok(()) + } + + /// When node has rejected join offer. + pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { + match *self { + Consensus::Establishing(ref mut consensus) => { + consensus.reject_offer(node)?; + if consensus.requested_nodes.len() + consensus.confirmed_nodes.len() >= consensus.threshold + 1 { + return Ok(()); + } + + // else fall through + }, + Consensus::Established(ref mut consensus) => return consensus.reject_offer(node), + Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => + return consensus.core.reject_offer(node), + _ => return Err(Error::InvalidStateForRequest), + } + + *self = Consensus::Unreachable; + Err(Error::ConsensusUnreachable) + } + + /// When starting/restarting requesting consensus nodes to do their job. + pub fn activate(&mut self) -> Result<(), Error> { + let active_consensus = match *self { + Consensus::Established(ref established_consensus) => ActiveConsensus::new(established_consensus.clone()), + Consensus::Active(ref active_consensus) => ActiveConsensus::new(active_consensus.core.clone()), + _ => return Err(Error::InvalidStateForRequest), + }; + + *self = Consensus::Active(active_consensus); + Ok(()) + } + + /// Select nodes for completing their jobs. + pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { + match *self { + Consensus::Active(ref mut consensus) => consensus.select_nodes(), + _ => Err(Error::InvalidStateForRequest), + } + } + + /// Get nodes, select nodes for completing their jobs. + pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { + match *self { + Consensus::Active(ref consensus) => consensus.selected_nodes(), + _ => Err(Error::InvalidStateForRequest), + } + } + + /// When job request is sent to the node. + pub fn job_request_sent(&mut self, node: &NodeId) -> Result<(), Error> { + match *self { + Consensus::Active(ref mut consensus) => consensus.job_request_sent(node), + _ => Err(Error::InvalidStateForRequest), + } + } + + /// When job response is received from the node. + pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { + match *self { + Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => + consensus.job_response_received(node, response), + _ => Err(Error::InvalidStateForRequest), + } + } + + /// When node is timeouted. Returns true if consensus restarted (i.e. caller must resend job requests). + pub fn node_timeouted(&mut self, node: &NodeId) -> Result { + match *self { + Consensus::Establishing(ref mut consensus) => { + consensus.node_timeouted(node)?; + if consensus.requested_nodes.len() + consensus.confirmed_nodes.len() >= consensus.threshold + 1 { + return Ok(false); + } + + // else fall through + }, + Consensus::Established(ref mut consensus) => { + consensus.node_timeouted(node)?; + if consensus.requested_nodes.len() + consensus.confirmed_nodes.len() >= consensus.threshold + 1 { + return Ok(false); + } + + // else fall through + } + Consensus::Active(ref mut consensus) => { + let is_restart_required = consensus.node_timeouted(node)?; + let is_consensus_reachable = consensus.core.requested_nodes.len() + consensus.core.confirmed_nodes.len() >= consensus.core.threshold + 1; + if is_consensus_reachable { + if !is_restart_required { + return Ok(false); + } + + consensus.restart()?; + return Ok(true); + } + + // else fall through + }, + Consensus::Completed(_) => return Ok(false), + _ => return Err(Error::InvalidStateForRequest), + } + + *self = Consensus::Unreachable; + Err(Error::ConsensusUnreachable) + } + + /// When session is timeouted. Consensus is always restarted (if active). + pub fn session_timeouted(&mut self) -> Result<(), Error> { + match *self { + Consensus::Establishing(_) => (), // fall through + Consensus::Established(_) => (), // fall through + Consensus::Active(ref mut consensus) => { + consensus.session_timeouted()?; + if consensus.core.requested_nodes.len() + consensus.core.confirmed_nodes.len() >= consensus.core.threshold + 1 { + return Ok(()); + } + + // else fall through + }, + Consensus::Completed(_) => return Ok(()), + Consensus::Unreachable => return Err(Error::ConsensusUnreachable), + } + + *self = Consensus::Unreachable; + Err(Error::ConsensusUnreachable) + } +} + +impl ConsensusCore { + /// When node has accepted join offer. + pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { + if !self.requested_nodes.remove(node) { + return Err(Error::InvalidStateForRequest); + } + + self.confirmed_nodes.insert(node.clone()); + Ok(()) + } + + /// When node has rejected join offer. + pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { + if !self.requested_nodes.remove(node) { + return Err(Error::InvalidStateForRequest); + } + + self.rejected_nodes.insert(node.clone()); + Ok(()) + } + + /// When node is timeouted. + pub fn node_timeouted(&mut self, node: &NodeId) -> Result<(), Error> { + if self.requested_nodes.remove(node) || self.confirmed_nodes.remove(node) { + self.rejected_nodes.insert(node.clone()); + } + Ok(()) + } +} + +impl ActiveConsensus { + /// Create new active consensus. + pub fn new(core: ConsensusCore) -> Self { + ActiveConsensus { + core: core, + selected_nodes: BTreeSet::new(), + active_requests: BTreeSet::new(), + responses: BTreeMap::new(), + } + } + + /// Select nodes to make job. + pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { + if !self.selected_nodes.is_empty() { + return Err(Error::InvalidStateForRequest); + } + + self.selected_nodes = self.core.confirmed_nodes.iter().cloned().take(self.core.threshold + 1).collect(); + Ok(&self.selected_nodes) + } + + /// Get nodes, selected nodes to make their job. + pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { + if self.selected_nodes.is_empty() { + return Err(Error::InvalidStateForRequest); + } + + Ok(&self.selected_nodes) + } + + /// When job request is sent to the node. + pub fn job_request_sent(&mut self, node: &NodeId) -> Result<(), Error> { + if !self.core.confirmed_nodes.contains(node) { + return Err(Error::InvalidNodeForRequest); + } + if !self.selected_nodes.contains(node) { + return Err(Error::InvalidNodeForRequest); + } + if !self.active_requests.insert(node.clone()) { + return Err(Error::InvalidNodeForRequest); + } + + Ok(()) + } + + /// When job response is received from the node. + pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { + if !self.active_requests.remove(node) { + return Err(Error::InvalidStateForRequest); + } + + self.responses.insert(node.clone(), response); + Ok(()) + } + + /// Restart jobs. + pub fn restart(&mut self) -> Result<(), Error> { + self.selected_nodes.clear(); + self.active_requests.clear(); + self.responses.clear(); + Ok(()) + } + + /// When node is timeouted. + pub fn node_timeouted(&mut self, node: &NodeId) -> Result { + self.core.node_timeouted(node)?; + Ok(self.active_requests.remove(node) || self.responses.remove(node).is_some()) + } + + /// When session is timeouted. + pub fn session_timeouted(&mut self) -> Result<(), Error> { + for timeouted_node in &self.active_requests { + self.core.node_timeouted(timeouted_node)?; + } + self.restart() + } +} diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index 6bcbe3d3db0..e94187f0151 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -22,43 +22,48 @@ use util; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::consensus::Consensus; use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; -#[derive(Default, Debug, Clone)] -/// Consensus data. TODO: also move restart logic here (it is in signing + decryption) -pub struct Consensus { - /// Nodes, which have been requested for signing initialization. - pub requested_nodes: BTreeSet, - /// Nodes, which have responded with reject to initialization request. - pub rejected_nodes: BTreeSet, - /// Nodes, which have responded with confirm to initialization request. - pub confirmed_nodes: BTreeSet, +/// Consenus checker. +pub trait ConsensusChecker { + /// Check if we want to accept offer to join consensus group. Consensus is about revealing key `key` to requestor `requestor`. + fn check_offer(&self, key: &SessionId, requestor: &Public) -> bool; } -/// Signing session. -pub struct ConsensusSession { +/// Consensus establishing session. +pub struct ConsensusSession { /// Key generation session id. id: SessionId, /// Public identifier of this node. self_node_id: NodeId, - /// Key generation data. - encrypted_data: DocumentKeyShare, + /// Master node id. + master_node_id: Public, + /// Consensus checker. + consensus_checker: C, + /// Mutable session data. + data: SessionData, +} + +/// ACL checker for consensus establishing session. +pub struct AclConsensusChecker { /// ACL storate to check access to the resource. acl_storage: Arc, - /// Mutable session data. - data: Mutex, } +/// Always accept checker for consensus establishing session. +pub struct TrueConsensusChecker; + /// SessionImpl creation parameters -pub struct SessionParams { +pub struct SessionParams { /// Key generation session id. pub id: SessionId, /// Id of node, on which this session is running. pub self_node_id: Public, - /// Encrypted data (result of running encryption_session::SessionImpl). - pub encrypted_data: DocumentKeyShare, - /// Key storage. - pub acl_storage: Arc, + /// Master node id. + pub master_node_id: Public, + /// Consensus checker. + pub consensus_checker: C, } #[derive(Debug)] @@ -66,9 +71,7 @@ pub struct SessionParams { struct SessionData { /// Current state of the session. state: SessionState, - /// Consensus data. - consensus: Consensus, - /// Consensus result. + /// Consensus establishing result. result: Option>, } @@ -82,9 +85,9 @@ pub enum SessionState { WaitingForInitializationConfirm, // === Final states of the session === - /// Signing is completed. + /// Consensus group is established. Finished, - /// Signing is failed. + /// Consensus establish has failed. Failed, } @@ -99,143 +102,133 @@ pub enum SessionAction { SendMessage(NodeId, ConsensusMessage), } -impl ConsensusSession { +impl ConsensusSession where C: ConsensusChecker { /// Create new signing session. - pub fn new(params: SessionParams) -> Result { + pub fn new(params: SessionParams) -> Result { Ok(ConsensusSession { id: params.id, self_node_id: params.self_node_id, - encrypted_data: params.encrypted_data, - acl_storage: params.acl_storage, - data: Mutex::new(SessionData { + master_node_id: params.master_node_id, + consensus_checker: params.consensus_checker, + data: SessionData { state: SessionState::WaitingForInitialization, - consensus: Consensus::default(), result: None, - }) + } }) } - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id - } - /// Get current session state. pub fn state(&self) -> SessionState { - self.data.lock().state.clone() - } - - /// Get result of consensus. - pub fn consensus(&self) -> Option> { - let data = self.data.lock(); - data.result.clone().map(|r| r.map(|_| data.consensus.clone())) + self.data.state.clone() } /// Initialize consensus session. - pub fn initialize(&self, requestor_signature: Signature) -> Result { - let mut data = self.data.lock(); + pub fn initialize(&mut self, requestor_signature: Signature, consensus: &mut Consensus) -> Result { + debug_assert_eq!(self.self_node_id, self.master_node_id); // check state - if data.state != SessionState::WaitingForInitialization { + if self.data.state != SessionState::WaitingForInitialization { return Err(Error::InvalidStateForRequest); } - // recover requestor signature - let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; + // recover requestor public + let requestor = ethkey::recover(&requestor_signature, &self.id)?; // update state - data.state = SessionState::WaitingForInitializationConfirm; - data.consensus.requested_nodes.extend(self.encrypted_data.id_numbers.keys().cloned()); + self.data.state = SessionState::WaitingForInitializationConfirm; // ..and finally check access on our's own - let is_permitted = self.acl_storage.check(&requestor_public, &self.id).unwrap_or(false); - process_initialization_response(&self.encrypted_data, &mut *data, self.node(), is_permitted)?; - - // check if we have enough nodes to sign message - match data.state { - // not enough nodes => pass initialization message to all other nodes - SessionState::WaitingForInitializationConfirm => - Ok(SessionAction::BroadcastMessage(ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requestor_signature: requestor_signature.clone().into(), - }))), - // else state must be checked - _ => Ok(SessionAction::CheckStatus), + let self_node_id = self.self_node_id.clone(); + let is_confirmed = self.consensus_checker.check_offer(&self.id, &requestor); + self.process_initialization_response(&self_node_id, is_confirmed, consensus)?; + if self.data.state != SessionState::Finished { + Ok(SessionAction::BroadcastMessage(ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: requestor_signature.into(), + }))) + } else { + Ok(SessionAction::CheckStatus) } } /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeConsensusSession) -> Result { - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); + pub fn on_initialize_session(&mut self, sender: NodeId, message: &InitializeConsensusSession) -> Result { + debug_assert!(sender != self.self_node_id); + // check message + if self.master_node_id != sender { + return Err(Error::InvalidMessage); + } // check state - if data.state != SessionState::WaitingForInitialization { + if self.data.state != SessionState::WaitingForInitialization { return Err(Error::InvalidStateForRequest); } - // recover requestor signature + // recover requestor public let requestor_public = ethkey::recover(&message.requestor_signature, &self.id)?; // check access - let is_permitted = self.acl_storage.check(&requestor_public, &self.id).unwrap_or(false); - data.state = if is_permitted { SessionState::Finished } else { SessionState::Failed }; + let is_confirmed = self.consensus_checker.check_offer(&self.id, &requestor_public); + + // update state + self.data.state = if is_confirmed { SessionState::Finished } else { SessionState::Failed }; // respond to sender Ok(SessionAction::SendMessage(sender, ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: is_permitted, + is_confirmed: is_confirmed, }))) } /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmConsensusInitialization) -> Result { - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); + pub fn on_confirm_initialization(&mut self, sender: NodeId, message: &ConfirmConsensusInitialization, consensus: &mut Consensus) -> Result { + debug_assert!(sender != self.self_node_id); // check state - if data.state != SessionState::WaitingForInitializationConfirm { + if self.self_node_id != self.master_node_id { + return Err(Error::InvalidMessage); + } + if self.data.state != SessionState::WaitingForInitializationConfirm && self.data.state != SessionState::Finished { return Err(Error::InvalidStateForRequest); } // update state - process_initialization_response(&self.encrypted_data, &mut *data, &sender, message.is_confirmed)?; - - // check if we have enough nodes for consensus - match data.state { - // we do not yet have enough nodes for consensus - SessionState::WaitingForInitializationConfirm => Ok(SessionAction::CheckStatus), - // else state must be checked - _ => Ok(SessionAction::CheckStatus), + self.process_initialization_response(&sender, message.is_confirmed, consensus) + } + + /// Process initialization response from given node. + fn process_initialization_response(&mut self, node: &NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { + match consensus.offer_response(node, is_confirmed) { + Ok(_) if consensus.is_established() => { + self.data.result = Some(Ok(())); + self.data.state = SessionState::Finished; + Ok(SessionAction::CheckStatus) + } + Ok(_) => Ok(SessionAction::CheckStatus), + Err(err) => { + self.data.result = Some(Err(err.clone())); + self.data.state = SessionState::Failed; + Err(err) + }, } } } -fn process_initialization_response(encrypted_data: &DocumentKeyShare, data: &mut SessionData, node: &NodeId, is_permitted: bool) -> Result<(), Error> { - if !data.consensus.requested_nodes.remove(node) { - return Err(Error::InvalidMessage); +impl AclConsensusChecker { + /// Create new ACL-consensus checker. + pub fn new(acl_storage: Arc) -> Self { + AclConsensusChecker { + acl_storage: acl_storage, + } } +} - match is_permitted { - true => { - data.consensus.confirmed_nodes.insert(node.clone()); - - // check if we have enough nodes for consensus? - if data.consensus.confirmed_nodes.len() == encrypted_data.threshold + 1 { - data.result = Some(Ok(())); - data.state = SessionState::Finished; - } - }, - false => { - data.consensus.rejected_nodes.insert(node.clone()); - - // check if we still can receive enough confirmations for consensus? - if encrypted_data.id_numbers.len() - data.consensus.rejected_nodes.len() < encrypted_data.threshold + 1 { - data.result = Some(Err(Error::AccessDenied)); - data.state = SessionState::Failed; - } - }, +impl ConsensusChecker for AclConsensusChecker { + fn check_offer(&self, key: &SessionId, requestor: &Public) -> bool { + self.acl_storage.check(requestor, key).unwrap_or(false) } +} - Ok(()) +impl ConsensusChecker for TrueConsensusChecker { + fn check_offer(&self, _key: &SessionId, _requestor: &Public) -> bool { + true + } } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index f4507a210b9..28eaf0910d4 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -518,7 +518,7 @@ impl ClusterSession for SessionImpl { if self.encrypted_data.id_numbers.len() - data.rejected_nodes.len() >= self.encrypted_data.threshold + 1 { return; } - } + }, SessionState::WaitingForPartialDecryption => { if data.rejected_nodes.contains(node) { // already rejected => does not affect session @@ -549,7 +549,7 @@ impl ClusterSession for SessionImpl { } // not enough nodes } - } + }, _ => (), // all other states lead to failures } } else if !is_other_master { diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 3059203045b..6b0e54e43a4 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -66,6 +66,8 @@ pub enum Error { /// Current state of encryption/decryption session does not allow to proceed request. /// This means that either there is some comm-failure or node is misbehaving/cheating. InvalidStateForRequest, + /// TODO + InvalidNodeForRequest, /// Message or some data in the message was recognized as invalid. /// This means that node is misbehaving/cheating. InvalidMessage, @@ -79,6 +81,8 @@ pub enum Error { Serde(String), /// Key storage error. KeyStorage(String), + /// Consensus is unreachable. + ConsensusUnreachable, /// Acl storage error. AccessDenied, } @@ -115,12 +119,14 @@ impl fmt::Display for Error { Error::InvalidThreshold => write!(f, "invalid threshold value has been passed"), Error::TooEarlyForRequest => write!(f, "session is not yet ready to process this request"), Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), + Error::InvalidNodeForRequest => write!(f, "node cannot respond to this request"), Error::InvalidMessage => write!(f, "invalid message is received"), Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), Error::Io(ref e) => write!(f, "i/o error {}", e), Error::Serde(ref e) => write!(f, "serde error {}", e), Error::KeyStorage(ref e) => write!(f, "key storage error {}", e), + Error::ConsensusUnreachable => write!(f, "Consensus unreachable"), Error::AccessDenied => write!(f, "Access denied"), } } @@ -134,6 +140,7 @@ impl Into for Error { mod cluster; mod cluster_sessions; +mod consensus; mod consensus_session; mod decryption_session; mod encryption_session; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 835f8f0c6ba..e0a976e173c 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -17,6 +17,7 @@ use std::collections::{BTreeSet, VecDeque}; use std::mem::swap; use std::cmp::{Ordering, Ord, PartialOrd}; +use std::ops::DerefMut; use std::sync::Arc; use std::time; use parking_lot::{Mutex, Condvar}; @@ -25,7 +26,8 @@ use util::{self, H256}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::ClusterSession; -use key_server_cluster::consensus_session::{ConsensusSession, Consensus, SessionParams as ConsensusSessionParams, +use key_server_cluster::consensus::Consensus; +use key_server_cluster::consensus_session::{ConsensusSession, AclConsensusChecker, SessionParams as ConsensusSessionParams, SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, SessionState as GenerationSessionState, Session as GenerationSessionApi}; @@ -98,12 +100,12 @@ struct SessionData { requestor: Option, /// Hash of the message to sign. message_hash: Option, + /// Signing consensus group. + consensus: Option>, // === Values, filled when consensus is establishing === /// Consensus session. - consensus_session: Option, - /// Consensus params. - consensus: Option, + consensus_session: Option>, // === Values, filled when session key is generating === /// Signing cluster subgroup. @@ -221,17 +223,18 @@ impl SessionImpl { data.master = Some(self.node().clone()); //data.requestor = Some(requestor_public.clone()); data.message_hash = Some(message_hash); + data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); // create consensus session - let consensus_session = ConsensusSession::new(ConsensusSessionParams { + let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { id: self.id.clone(), self_node_id: self.self_node_id.clone(), - encrypted_data: self.encrypted_data.clone(), - acl_storage: self.acl_storage.clone(), + master_node_id: self.self_node_id.clone(), + consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), })?; // start consensus session - let consensus_action = consensus_session.initialize(requestor_signature)?; + let consensus_action = consensus_session.initialize(requestor_signature, data.consensus.as_mut().expect("TODO"))?; data.consensus_session = Some(consensus_session); SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; @@ -249,17 +252,19 @@ impl SessionImpl { /// When consensus-related message is received. pub fn on_consensus_message(&self, sender: NodeId, message: &SigningConsensusMessage) -> Result<(), Error> { let mut data = self.data.lock(); + let mut data = data.deref_mut(); // if we are waiting for initialization if data.state == SessionState::WaitingForInitialization { data.master = Some(sender.clone()); //data.requestor = Some(requestor_public); data.state = SessionState::EstablishingConsensus; + data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { id: self.id.clone(), self_node_id: self.self_node_id.clone(), - encrypted_data: self.encrypted_data.clone(), - acl_storage: self.acl_storage.clone(), + master_node_id: sender.clone(), + consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), })?); } @@ -272,9 +277,9 @@ impl SessionImpl { // process message let consensus_action = match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => - data.consensus_session.as_ref().expect("TODO").on_initialize_session(sender, &message)?, + data.consensus_session.as_mut().expect("TODO").on_initialize_session(sender, &message)?, ConsensusMessage::ConfirmConsensusInitialization(ref message) => - data.consensus_session.as_ref().expect("TODO").on_confirm_initialization(sender, &message)?, + data.consensus_session.as_mut().expect("TODO").on_confirm_initialization(sender, &message, data.consensus.as_mut().expect("TODO"))?, }; SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; @@ -434,19 +439,7 @@ impl SessionImpl { message: message, }))) }, - ConsensusSessionAction::CheckStatus => match data.consensus_session.as_ref().expect("TODO").consensus() { - Some(Ok(consensus)) => { - data.consensus = Some(consensus.clone()); - Ok(()) - }, - Some(Err(err)) => { - data.state = SessionState::Failed; - data.signed_message = Some(Err(err)); - completed.notify_all(); - Ok(()) - }, - None => Ok(()), - }, + _ => Ok(()), } } @@ -481,11 +474,11 @@ impl SessionImpl { fn start_generating_session_key(self_node_id: NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { // select nodes to make signature - let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").confirmed_nodes.clone(); - let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.consensus.as_ref().expect("TODO").rejected_nodes).cloned().collect(); + data.consensus.as_mut().expect("TODO").activate()?; + let selected_nodes = data.consensus.as_mut().expect("TODO").select_nodes()?; // create generation session - let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), confirmed_nodes.clone())); + let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), selected_nodes.clone())); let generation_session = GenerationSession::new(GenerationSessionParams { id: H256::default(), // doesn't matter self_node_id: self_node_id.clone(), @@ -494,7 +487,7 @@ impl SessionImpl { }); // start generation session - let result = generation_session.initialize(data.requestor.as_ref().expect("TODO").clone(), encrypted_data.threshold, confirmed_nodes)?; + let result = generation_session.initialize(data.requestor.as_ref().expect("TODO").clone(), encrypted_data.threshold, selected_nodes.clone())?; data.generation_cluster = Some(generation_cluster); data.generation_session = Some(generation_session); @@ -503,8 +496,7 @@ impl SessionImpl { fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // nodes which have formed consensus group - let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").confirmed_nodes.clone(); - let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.consensus.as_ref().expect("TODO").rejected_nodes).cloned().collect(); + let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").selected_nodes()?.clone(); // send requests data.partial_requests.clear(); From c43e6bf31a93a7890994f81c0f45322920d9255d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 22 May 2017 17:43:35 +0300 Subject: [PATCH 12/45] started work on signing test --- .../key_server_cluster/generation_session.rs | 19 +-- secret_store/src/key_server_cluster/mod.rs | 6 + .../src/key_server_cluster/signing_session.rs | 129 +++++++++++++++++- 3 files changed, 145 insertions(+), 9 deletions(-) diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index b6f4b084e6b..200dc912e21 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -828,7 +828,7 @@ pub fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), } #[cfg(test)] -mod tests { +pub mod tests { use std::time; use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap, VecDeque}; @@ -842,14 +842,13 @@ mod tests { use key_server_cluster::math; use key_server_cluster::math::tests::do_encryption_and_decryption; - #[derive(Debug)] - struct Node { + pub struct Node { pub cluster: Arc, + pub key_storage: Arc, pub session: SessionImpl, } - #[derive(Debug)] - struct MessageLoop { + pub struct MessageLoop { pub session_id: SessionId, pub nodes: BTreeMap, pub queue: VecDeque<(NodeId, NodeId, Message)>, @@ -863,13 +862,14 @@ mod tests { let key_pair = Random.generate().unwrap(); let node_id = key_pair.public().clone(); let cluster = Arc::new(DummyCluster::new(node_id.clone())); + let key_storage = Arc::new(DummyKeyStorage::default()); let session = SessionImpl::new(SessionParams { id: session_id.clone(), self_node_id: node_id.clone(), - key_storage: Some(Arc::new(DummyKeyStorage::default())), + key_storage: Some(key_storage.clone()), cluster: cluster.clone(), }); - nodes.insert(node_id, Node { cluster: cluster, session: session }); + nodes.insert(node_id, Node { cluster: cluster, key_storage: key_storage, session: session }); } let nodes_ids: Vec<_> = nodes.keys().cloned().collect(); @@ -964,7 +964,10 @@ mod tests { #[test] fn fails_to_initialize_if_threshold_is_wrong() { - assert_eq!(make_simple_cluster(2, 2).unwrap_err(), Error::InvalidThreshold); + match make_simple_cluster(2, 2) { + Err(Error::InvalidThreshold) => (), + _ => panic!("unexpected"), + } } #[test] diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 6b0e54e43a4..7341c976ac3 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -14,6 +14,12 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +#![allow(unused_mut)] // TODO: remove me +#![allow(dead_code)] // TODO: remove me +#![allow(unused_imports)] // TODO: remove me +#![allow(unused_variables)] // TODO: remove me +#![allow(unreachable_code)] // TODO: remove me + use std::fmt; use std::io::Error as IoError; use ethkey; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index e0a976e173c..e572ac034e6 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -645,4 +645,131 @@ fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare let nodes = encrypted_data.id_numbers.keys().cloned().collect(); check_cluster_nodes(self_node_id, &nodes)?; check_threshold(encrypted_data.threshold, &nodes) -} \ No newline at end of file +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::collections::{BTreeMap, VecDeque}; + use ethkey::{Random, Generator, Public, Signature, sign}; + use util::H256; + use super::super::super::acl_storage::tests::DummyAclStorage; + use key_server_cluster::{NodeId, SessionId, Error, DummyKeyStorage, KeyStorage}; + use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; + use key_server_cluster::generation_session::tests::MessageLoop as KeyGenerationMessageLoop; + use key_server_cluster::message::{Message, SigningMessage}; + use key_server_cluster::signing_session::{Session, SessionImpl, SessionState, SessionParams}; + + struct Node { + pub cluster: Arc, + pub session: SessionImpl, + } + + struct MessageLoop { + pub session_id: SessionId, + pub nodes: BTreeMap, + pub queue: VecDeque<(NodeId, NodeId, Message)>, + } + + impl MessageLoop { + pub fn new(gl: &KeyGenerationMessageLoop) -> Self { + let mut nodes = BTreeMap::new(); + let session_id = gl.session_id.clone(); + for (gl_node_id, gl_node) in &gl.nodes { + let acl_storage = Arc::new(DummyAclStorage::default()); + let cluster = Arc::new(DummyCluster::new(gl_node_id.clone())); + let session = SessionImpl::new(SessionParams { + id: session_id.clone(), + access_key: Random.generate().unwrap().secret().clone(), + self_node_id: gl_node_id.clone(), + encrypted_data: gl_node.key_storage.get(&session_id).unwrap(), + acl_storage: acl_storage, + cluster: cluster.clone(), + }).unwrap(); + nodes.insert(gl_node_id.clone(), Node { cluster: cluster, session: session }); + } + + let nodes_ids: Vec<_> = nodes.keys().cloned().collect(); + for node in nodes.values() { + for node_id in &nodes_ids { + node.cluster.add_node(node_id.clone()); + } + } + + MessageLoop { + session_id: session_id, + nodes: nodes, + queue: VecDeque::new(), + } + } + + pub fn master(&self) -> &SessionImpl { + &self.nodes.values().nth(0).unwrap().session + } + + pub fn first_slave(&self) -> &SessionImpl { + &self.nodes.values().nth(1).unwrap().session + } + + pub fn second_slave(&self) -> &SessionImpl { + &self.nodes.values().nth(2).unwrap().session + } + + pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { + self.nodes.values() + .filter_map(|n| n.cluster.take_message().map(|m| (n.session.node().clone(), m.0, m.1))) + .nth(0) + .or_else(|| self.queue.pop_front()) + } + + pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { +println!("=== MESSAGE {:?}", msg); + match { + match msg.2 { + Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => self.nodes[&msg.1].session.on_consensus_message(msg.0.clone(), &message), + Message::Signing(SigningMessage::SigningGenerationMessage(ref message)) => self.nodes[&msg.1].session.on_generation_message(msg.0.clone(), &message), + Message::Signing(SigningMessage::RequestPartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature_requested(msg.0.clone(), &message), + Message::Signing(SigningMessage::PartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature(msg.0.clone(), &message), + //Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), + Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => unimplemented!(), + _ => panic!("unexpected"), + } + } { + Ok(_) => Ok(()), + Err(Error::TooEarlyForRequest) => { + self.queue.push_back(msg); + Ok(()) + }, + Err(err) => Err(err), + } + } + + pub fn take_and_process_message(&mut self) -> Result<(), Error> { + let msg = self.take_message().unwrap(); + self.process_message(msg) + } + } + + #[test] + fn complete_gen_sign_session() { + let test_cases = [(1, 3)]; + for &(threshold, num_nodes) in &test_cases { + // run key generation sessions + let mut gl = KeyGenerationMessageLoop::new(num_nodes); + gl.master().initialize(Public::default(), threshold, gl.nodes.keys().cloned().collect()).unwrap(); + while let Some((from, to, message)) = gl.take_message() { + gl.process_message((from, to, message)).unwrap(); + } + + // run signing session + let requestor_pair = Random.generate().unwrap(); + let requestor_signature = sign(&requestor_pair.secret(), &SessionId::default()).unwrap(); + let message_hash = H256::from(777); + let mut sl = MessageLoop::new(&gl); + sl.master().initialize(requestor_signature, message_hash).unwrap(); + while let Some((from, to, message)) = sl.take_message() { + sl.process_message((from, to, message)).unwrap(); + } + } + } +} From dcfb98aeaa94e16bd7c2be10952439441ee3e733 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 May 2017 11:37:51 +0300 Subject: [PATCH 13/45] complete_gen_sign_session works --- .../src/key_server_cluster/consensus.rs | 12 +- .../key_server_cluster/consensus_session.rs | 16 +- .../key_server_cluster/generation_session.rs | 11 +- secret_store/src/key_server_cluster/math.rs | 2 +- .../src/key_server_cluster/message.rs | 2 + .../src/key_server_cluster/signing_session.rs | 162 +++++++++++++----- 6 files changed, 141 insertions(+), 64 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 1455781a92d..0b4bf03ec53 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -14,11 +14,13 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +use std::fmt::Debug; use std::collections::{BTreeSet, BTreeMap, VecDeque}; use key_server_cluster::{Error, NodeId}; +#[derive(Debug, Clone)] /// Consensus. -pub enum Consensus { +pub enum Consensus { /// Consensus is currently establishing. Establishing(ConsensusCore), /// Consensus is established. @@ -46,7 +48,7 @@ pub struct ConsensusCore { #[derive(Debug, Clone)] /// Active consensus (i.e. consensus with sent requests). -pub struct ActiveConsensus { +pub struct ActiveConsensus { /// Consensus core data. pub core: ConsensusCore, /// Selected nodes. @@ -57,7 +59,7 @@ pub struct ActiveConsensus { pub responses: BTreeMap, } -impl Consensus { +impl Consensus where T: Debug { /// Create new consensus. pub fn new(threshold: usize, nodes: BTreeSet) -> Result { if nodes.len() < threshold + 1 { @@ -75,7 +77,7 @@ impl Consensus { /// Is consenus established. pub fn is_established(&self) -> bool { match *self { - Consensus::Established(_) | Consensus::Active(_) => true, + Consensus::Established(_) => true, _ => false, } } @@ -269,7 +271,7 @@ impl ConsensusCore { } } -impl ActiveConsensus { +impl ActiveConsensus where T: Debug { /// Create new active consensus. pub fn new(core: ConsensusCore) -> Self { ActiveConsensus { diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index e94187f0151..cd7a86c8261 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . use std::sync::Arc; +use std::fmt::Debug; use std::collections::BTreeSet; use parking_lot::Mutex; use ethkey::{self, Public, Secret, Signature}; @@ -123,7 +124,7 @@ impl ConsensusSession where C: ConsensusChecker { } /// Initialize consensus session. - pub fn initialize(&mut self, requestor_signature: Signature, consensus: &mut Consensus) -> Result { + pub fn initialize(&mut self, requestor_signature: Signature, consensus: &mut Consensus) -> Result { debug_assert_eq!(self.self_node_id, self.master_node_id); // check state @@ -151,7 +152,7 @@ impl ConsensusSession where C: ConsensusChecker { } /// When session initialization message is received. - pub fn on_initialize_session(&mut self, sender: NodeId, message: &InitializeConsensusSession) -> Result { + pub fn on_initialize_session(&mut self, sender: NodeId, requestor: &Public) -> Result { debug_assert!(sender != self.self_node_id); // check message @@ -163,11 +164,8 @@ impl ConsensusSession where C: ConsensusChecker { return Err(Error::InvalidStateForRequest); } - // recover requestor public - let requestor_public = ethkey::recover(&message.requestor_signature, &self.id)?; - // check access - let is_confirmed = self.consensus_checker.check_offer(&self.id, &requestor_public); + let is_confirmed = self.consensus_checker.check_offer(&self.id, &requestor); // update state self.data.state = if is_confirmed { SessionState::Finished } else { SessionState::Failed }; @@ -179,7 +177,7 @@ impl ConsensusSession where C: ConsensusChecker { } /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&mut self, sender: NodeId, message: &ConfirmConsensusInitialization, consensus: &mut Consensus) -> Result { + pub fn on_confirm_initialization(&mut self, sender: NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { debug_assert!(sender != self.self_node_id); // check state @@ -191,11 +189,11 @@ impl ConsensusSession where C: ConsensusChecker { } // update state - self.process_initialization_response(&sender, message.is_confirmed, consensus) + self.process_initialization_response(&sender, is_confirmed, consensus) } /// Process initialization response from given node. - fn process_initialization_response(&mut self, node: &NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { + fn process_initialization_response(&mut self, node: &NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { match consensus.offer_response(node, is_confirmed) { Ok(_) if consensus.is_established() => { self.data.result = Some(Ok(())); diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index 200dc912e21..000476c0df1 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -321,6 +321,7 @@ impl SessionImpl { pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); +println!("=== 1212121212121212121212"); let mut data = self.data.lock(); debug_assert!(data.nodes.contains_key(&sender)); @@ -329,12 +330,15 @@ impl SessionImpl { let next_receiver = match data.state { SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { if !visit_policy.mark_visited(&sender) { +println!("=== 55555555555555555555555555555"); return Err(Error::InvalidStateForRequest); } visit_policy.next_node() }, - _ => return Err(Error::InvalidStateForRequest), + _ => { +println!("=== 66666666666666666666666666666"); + return Err(Error::InvalidStateForRequest) }, }; // proceed message @@ -434,7 +438,10 @@ impl SessionImpl { match data.state { SessionState::WaitingForInitializationComplete | SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), + _ => { +println!("on_public_key_share: bad status {:?}", data.state); + return Err(Error::InvalidStateForRequest) + }, } } diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index 2eaed6fa7a2..c2748512920 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -366,7 +366,7 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S } /// Verify signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. -pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &Secret) -> Result { +pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { let mut addendum = math::generation_point(); math::public_mul_secret(&mut addendum, &signature.1)?; let mut nonce_public = public.clone(); diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 92926b80ca4..4ecacbc3308 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -310,6 +310,8 @@ pub struct RequestPartialSignature { pub sub_session: SerializableSecret, /// Message hash. pub message_hash: SerializableMessageHash, + /// Selected nodes. TODO: this information is known from generation session - reuse + pub nodes: BTreeSet, } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index e572ac034e6..c5066725863 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -40,7 +40,7 @@ pub trait Session: Send + Sync + 'static { /// Get generation session state. fn state(&self) -> SessionState; /// Wait until session is completed. Returns signed message. - fn wait(&self, timeout: Option) -> Result; + fn wait(&self, timeout: Option) -> Result<(Secret, Secret), Error>; } /// Signing session. @@ -163,6 +163,8 @@ pub enum SessionState { struct SigningCluster { /// Original cluster reference. cluster: Arc, + /// This node id. + self_node_id: NodeId, /// Signing group. nodes: BTreeSet, /// Generation session messages. @@ -221,7 +223,7 @@ impl SessionImpl { // update state data.state = SessionState::EstablishingConsensus; data.master = Some(self.node().clone()); - //data.requestor = Some(requestor_public.clone()); + data.requestor = Some(requestor_public.clone()); data.message_hash = Some(message_hash); data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); @@ -270,21 +272,25 @@ impl SessionImpl { // check state if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); + return Ok(()); + // TODO: received after completion return Err(Error::InvalidStateForRequest); } // TODO: check master node + etc // process message let consensus_action = match message.message { - ConsensusMessage::InitializeConsensusSession(ref message) => - data.consensus_session.as_mut().expect("TODO").on_initialize_session(sender, &message)?, + ConsensusMessage::InitializeConsensusSession(ref message) => { + let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; + data.requestor = Some(requestor.clone()); + data.consensus_session.as_mut().expect("TODO").on_initialize_session(sender, &requestor)? + }, ConsensusMessage::ConfirmConsensusInitialization(ref message) => - data.consensus_session.as_mut().expect("TODO").on_confirm_initialization(sender, &message, data.consensus.as_mut().expect("TODO"))?, + data.consensus_session.as_mut().expect("TODO").on_confirm_initialization(sender, message.is_confirmed, data.consensus.as_mut().expect("TODO"))?, }; SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; // if consensus is reached, start generating session key - if data.state == SessionState::Failed || data.consensus.is_some() { + if !data.consensus.as_ref().expect("TODO").is_established() { return Ok(()); } SessionImpl::start_generating_session_key(self.self_node_id.clone(), &self.encrypted_data, &self.cluster, &mut *data)?; @@ -294,37 +300,44 @@ impl SessionImpl { /// When session key related message is received. pub fn on_generation_message(&self, sender: NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { let mut data = self.data.lock(); - +println!("=== on_generation_message: {:?}", data.state); // check state - if data.state != SessionState::EstablishingConsensus { + if data.state == SessionState::EstablishingConsensus { + match message.message { + GenerationMessage::InitializeSession(ref message) => { + // if we are NOT part of consensus + if data.consensus_session.as_ref().expect("TODO").state() != ConsensusSessionState::Finished { +println!("=== 11111111111111111111111"); + return Err(Error::InvalidStateForRequest); + } + + // update state + data.state = SessionState::SessionKeyGeneration; + + // create generation session + let generation_cluster = Arc::new(SigningCluster::new(self.cluster.clone(), self.self_node_id.clone(), message.nodes.keys().cloned().map(Into::into).collect())); + data.generation_cluster = Some(generation_cluster.clone()); + data.generation_session = Some(GenerationSession::new(GenerationSessionParams { + id: message.session.clone().into(), + self_node_id: self.self_node_id.clone(), + key_storage: None, + cluster: generation_cluster, + })); + }, + _ => return Err(Error::InvalidStateForRequest), + } + } + if data.state != SessionState::SessionKeyGeneration { +println!("=== 2222222222222222222222"); return Err(Error::InvalidStateForRequest); } // TODO: check master node + etc +println!("=== 3333333333333333333333"); // process message match message.message { - GenerationMessage::InitializeSession(ref message) => { - // if we are NOT part of consensus - if data.consensus_session.as_ref().expect("TODO").state() != ConsensusSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.state = SessionState::SessionKeyGeneration; - - // create generation session - let generation_cluster = Arc::new(SigningCluster::new(self.cluster.clone(), message.nodes.keys().cloned().map(Into::into).collect())); - data.generation_cluster = Some(generation_cluster.clone()); - data.generation_session = Some(GenerationSession::new(GenerationSessionParams { - id: message.session.clone().into(), - self_node_id: self.self_node_id.clone(), - key_storage: None, - cluster: generation_cluster, - })); - - // process initialization message - data.generation_session.as_ref().expect("TODO").on_initialize_session(sender, &message)? - }, + GenerationMessage::InitializeSession(ref message) => + data.generation_session.as_ref().expect("TODO").on_initialize_session(sender, &message)?, GenerationMessage::ConfirmInitialization(ref message) => data.generation_session.as_ref().expect("TODO").on_confirm_initialization(sender, &message)?, GenerationMessage::CompleteInitialization(ref message) => @@ -341,10 +354,11 @@ impl SessionImpl { SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; // if session key generated is not yet completed => continue - if data.state == SessionState::Failed || !data.session_joint_public.is_some() { + //if data.state == SessionState::Failed || !data.session_joint_public.is_some() { + if data.state == SessionState::Failed || data.generation_session.as_ref().expect("TODO").state() != GenerationSessionState::Finished { return Ok(()); } - +println!("=== before start_waiting_for_partial_signing"); // else ask other nodes to generate partial signatures SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } @@ -359,7 +373,7 @@ impl SessionImpl { // return Err(Error::InvalidMessage); //} - let data = self.data.lock(); + let mut data = self.data.lock(); // check state if data.master != Some(sender) { @@ -369,11 +383,15 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } + // update data + data.message_hash = Some(message.message_hash.clone().into()); + // calculate partial signature - let message_hash = data.message_hash.as_ref().expect("TODO"); let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); - let partial_signature = SessionImpl::do_partial_signing(&self.self_node_id, message_hash, &self.encrypted_data, &data.confirmed_nodes, session_joint_public, session_secret_coeff)?; + let mut nodes: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).collect(); + nodes.remove(&self.self_node_id); + let partial_signature = SessionImpl::do_partial_signing(&self.self_node_id, &message.message_hash.clone().into(), &self.encrypted_data, &nodes, session_joint_public, session_secret_coeff)?; self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { session: self.id.clone().into(), @@ -423,6 +441,28 @@ impl SessionImpl { Ok(()) } + /// When session is completed. + pub fn on_session_completed(&self, sender: NodeId, message: &SigningSessionCompleted) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(self.access_key == *message.sub_session); + debug_assert!(&sender != self.node()); + + let mut data = self.data.lock(); + + // check state + /* TODO: if data.state != SessionState::WaitingForPartialDecryptionRequest { + return Err(Error::InvalidStateForRequest); + }*/ + if data.master != Some(sender) { + return Err(Error::InvalidMessage); + } + + // update state + data.state = SessionState::Finished; + + Ok(()) + } + fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { match action { ConsensusSessionAction::BroadcastMessage(message) => { @@ -476,9 +516,9 @@ impl SessionImpl { // select nodes to make signature data.consensus.as_mut().expect("TODO").activate()?; let selected_nodes = data.consensus.as_mut().expect("TODO").select_nodes()?; - +println!("start_generating_session_key.selected_nodes = {:?}", selected_nodes); // create generation session - let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), selected_nodes.clone())); + let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); let generation_session = GenerationSession::new(GenerationSessionParams { id: H256::default(), // doesn't matter self_node_id: self_node_id.clone(), @@ -491,13 +531,24 @@ impl SessionImpl { data.generation_cluster = Some(generation_cluster); data.generation_session = Some(generation_session); + // update state + data.state = SessionState::SessionKeyGeneration; + Ok(()) } fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + data.state = SessionState::WaitingForPartialSignature; + if data.master.as_ref() != Some(self_node_id) { + return Ok(()); + } + +println!("=== start_waiting_for_partial_signing: {:?}", data.consensus.as_ref().expect("TODO")); // nodes which have formed consensus group + data.consensus.as_mut().expect("TODO").activate()?; + data.consensus.as_mut().expect("TODO").select_nodes()?; let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").selected_nodes()?.clone(); - +println!("=== start_waiting_for_partial_signing. confirmed_nodes = {:?}", confirmed_nodes); // send requests data.partial_requests.clear(); data.partial_signatures.clear(); @@ -507,11 +558,11 @@ impl SessionImpl { session: session_id.clone().into(), sub_session: access_key.clone().into(), message_hash: data.message_hash.as_ref().expect("TODO").clone().into(), + nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), })))?; } // confirmation from this node - data.state = SessionState::WaitingForPartialSignature; data.confirmed_nodes = confirmed_nodes; if data.confirmed_nodes.remove(&self_node_id) { let signing_result = { @@ -528,6 +579,7 @@ impl SessionImpl { fn do_partial_signing(self_node_id: &NodeId, message_hash: &H256, encrypted_data: &DocumentKeyShare, session_nodes: &BTreeSet, session_joint_public: &Public, session_secret_coeff: &Secret) -> Result { debug_assert!(!session_nodes.contains(self_node_id)); +println!("nodes.len() = {} threshold = {}", session_nodes.len(), encrypted_data.threshold); debug_assert!(session_nodes.len() == encrypted_data.threshold); let combined_hash = math::combine_message_hash_with_public(&message_hash, &session_joint_public)?; @@ -568,9 +620,10 @@ impl ClusterSession for SessionImpl { } impl SigningCluster { - pub fn new(cluster: Arc, subset: BTreeSet) -> Self { + pub fn new(cluster: Arc, self_node_id: NodeId, subset: BTreeSet) -> Self { SigningCluster { cluster: cluster, + self_node_id: self_node_id, nodes: subset, messages: Mutex::new(VecDeque::new()), } @@ -592,7 +645,9 @@ impl Cluster for SigningCluster { fn broadcast(&self, message: Message) -> Result<(), Error> { let mut messages = self.messages.lock(); for node in &self.nodes { - messages.push_back((node.clone(), message.clone())); + if node != &self.self_node_id { + messages.push_back((node.clone(), message.clone())); + } } Ok(()) } @@ -608,8 +663,15 @@ impl Session for SessionImpl { unimplemented!() } - fn wait(&self, timeout: Option) -> Result { - unimplemented!() + fn wait(&self, timeout: Option) -> Result<(Secret, Secret), Error> { + let mut data = self.data.lock(); + if !data.signed_message.is_some() { + self.completed.wait(&mut data); + } + + data.signed_message.as_ref() + .expect("checked above or waited for completed; completed is only signaled when signed_message.is_some(); qed") + .clone() } } @@ -656,7 +718,9 @@ mod tests { use super::super::super::acl_storage::tests::DummyAclStorage; use key_server_cluster::{NodeId, SessionId, Error, DummyKeyStorage, KeyStorage}; use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; + use key_server_cluster::generation_session::{Session as GenerationSession}; use key_server_cluster::generation_session::tests::MessageLoop as KeyGenerationMessageLoop; + use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage}; use key_server_cluster::signing_session::{Session, SessionImpl, SessionState, SessionParams}; @@ -680,7 +744,7 @@ mod tests { let cluster = Arc::new(DummyCluster::new(gl_node_id.clone())); let session = SessionImpl::new(SessionParams { id: session_id.clone(), - access_key: Random.generate().unwrap().secret().clone(), + access_key: "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), self_node_id: gl_node_id.clone(), encrypted_data: gl_node.key_storage.get(&session_id).unwrap(), acl_storage: acl_storage, @@ -730,8 +794,7 @@ println!("=== MESSAGE {:?}", msg); Message::Signing(SigningMessage::SigningGenerationMessage(ref message)) => self.nodes[&msg.1].session.on_generation_message(msg.0.clone(), &message), Message::Signing(SigningMessage::RequestPartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature_requested(msg.0.clone(), &message), Message::Signing(SigningMessage::PartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature(msg.0.clone(), &message), - //Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), - Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => unimplemented!(), + Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), _ => panic!("unexpected"), } } { @@ -770,6 +833,11 @@ println!("=== MESSAGE {:?}", msg); while let Some((from, to, message)) = sl.take_message() { sl.process_message((from, to, message)).unwrap(); } + + // verify signature + let public = gl.master().joint_public_key().unwrap().unwrap(); + let signature = sl.master().wait(None).unwrap(); + assert!(math::verify_signature(&public, &signature, &message_hash).unwrap()); } } } From 537a5756cfa56e811c17b96acfcd9a46d320bee9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 May 2017 13:13:14 +0300 Subject: [PATCH 14/45] consensus tests --- .../src/key_server_cluster/consensus.rs | 64 ++++++++--- .../key_server_cluster/consensus_session.rs | 6 +- .../key_server_cluster/generation_session.rs | 11 +- secret_store/src/key_server_cluster/math.rs | 6 +- .../src/key_server_cluster/message.rs | 15 ++- .../src/key_server_cluster/signing_session.rs | 107 ++++++++++++++---- 6 files changed, 157 insertions(+), 52 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 0b4bf03ec53..f2df417edf4 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -20,7 +20,7 @@ use key_server_cluster::{Error, NodeId}; #[derive(Debug, Clone)] /// Consensus. -pub enum Consensus { +pub enum Consensus { /// Consensus is currently establishing. Establishing(ConsensusCore), /// Consensus is established. @@ -37,29 +37,29 @@ pub enum Consensus { /// Consensus core data. pub struct ConsensusCore { /// Consensus threshold. - pub threshold: usize, + threshold: usize, /// Nodes, which have been requested for participatining in consensus, but not yet responded. - pub requested_nodes: BTreeSet, + requested_nodes: BTreeSet, /// Nodes, which have responded with reject to participation request. - pub rejected_nodes: BTreeSet, + rejected_nodes: BTreeSet, /// Nodes, which have responded with confirm to participation request. - pub confirmed_nodes: BTreeSet, + confirmed_nodes: BTreeSet, } #[derive(Debug, Clone)] /// Active consensus (i.e. consensus with sent requests). -pub struct ActiveConsensus { +pub struct ActiveConsensus { /// Consensus core data. - pub core: ConsensusCore, + core: ConsensusCore, /// Selected nodes. - pub selected_nodes: BTreeSet, + selected_nodes: BTreeSet, /// Active job requests to confirmed nodes. - pub active_requests: BTreeSet, + active_requests: BTreeSet, /// Confirmed nodes responses. - pub responses: BTreeMap, + responses: BTreeMap, } -impl Consensus where T: Debug { +impl Consensus where T: Debug + Clone { /// Create new consensus. pub fn new(threshold: usize, nodes: BTreeSet) -> Result { if nodes.len() < threshold + 1 { @@ -82,6 +82,14 @@ impl Consensus where T: Debug { } } + /// Are consenus jobs completed. + pub fn is_completed(&self) -> bool { + match *self { + Consensus::Completed(_) => true, + _ => false, + } + } + /// When node responds to join offer. pub fn offer_response(&mut self, node: &NodeId, is_accepted: bool) -> Result<(), Error> { if is_accepted { @@ -171,11 +179,21 @@ impl Consensus where T: Debug { /// When job response is received from the node. pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { - match *self { - Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => - consensus.job_response_received(node, response), - _ => Err(Error::InvalidStateForRequest), - } + let completed_consensus = match *self { + Consensus::Active(ref mut consensus) => { + consensus.job_response_received(node, response)?; + if consensus.responses.len() != consensus.core.threshold + 1 { + return Ok(()); + } + + // else fall through + consensus.clone() + }, + _ => return Err(Error::InvalidStateForRequest), + }; + + *self = Consensus::Completed(completed_consensus); + Ok(()) } /// When node is timeouted. Returns true if consensus restarted (i.e. caller must resend job requests). @@ -271,7 +289,7 @@ impl ConsensusCore { } } -impl ActiveConsensus where T: Debug { +impl ActiveConsensus where T: Debug + Clone { /// Create new active consensus. pub fn new(core: ConsensusCore) -> Self { ActiveConsensus { @@ -348,3 +366,15 @@ impl ActiveConsensus where T: Debug { self.restart() } } + +#[cfg(test)] +mod tests { + use std::collections::BTreeSet; + use key_server_cluster::Error; + use super::Consensus; + + #[test] + fn consensus_is_not_created_when_not_enough_nodes() { + assert_eq!(Consensus::::new(0, vec![].into_iter().collect()).unwrap_err(), Error::InvalidThreshold); + } +} diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index cd7a86c8261..bdcfe8e6f84 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -124,7 +124,7 @@ impl ConsensusSession where C: ConsensusChecker { } /// Initialize consensus session. - pub fn initialize(&mut self, requestor_signature: Signature, consensus: &mut Consensus) -> Result { + pub fn initialize(&mut self, requestor_signature: Signature, consensus: &mut Consensus) -> Result { debug_assert_eq!(self.self_node_id, self.master_node_id); // check state @@ -177,7 +177,7 @@ impl ConsensusSession where C: ConsensusChecker { } /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&mut self, sender: NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { + pub fn on_confirm_initialization(&mut self, sender: NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { debug_assert!(sender != self.self_node_id); // check state @@ -193,7 +193,7 @@ impl ConsensusSession where C: ConsensusChecker { } /// Process initialization response from given node. - fn process_initialization_response(&mut self, node: &NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { + fn process_initialization_response(&mut self, node: &NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { match consensus.offer_response(node, is_confirmed) { Ok(_) if consensus.is_established() => { self.data.result = Some(Ok(())); diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index 000476c0df1..200dc912e21 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -321,7 +321,6 @@ impl SessionImpl { pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); -println!("=== 1212121212121212121212"); let mut data = self.data.lock(); debug_assert!(data.nodes.contains_key(&sender)); @@ -330,15 +329,12 @@ println!("=== 1212121212121212121212"); let next_receiver = match data.state { SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { if !visit_policy.mark_visited(&sender) { -println!("=== 55555555555555555555555555555"); return Err(Error::InvalidStateForRequest); } visit_policy.next_node() }, - _ => { -println!("=== 66666666666666666666666666666"); - return Err(Error::InvalidStateForRequest) }, + _ => return Err(Error::InvalidStateForRequest), }; // proceed message @@ -438,10 +434,7 @@ println!("=== 66666666666666666666666666666"); match data.state { SessionState::WaitingForInitializationComplete | SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), - _ => { -println!("on_public_key_share: bad status {:?}", data.state); - return Err(Error::InvalidStateForRequest) - }, + _ => return Err(Error::InvalidStateForRequest), } } diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index c2748512920..fcee3607a29 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -345,8 +345,8 @@ pub fn check_signature_share<'a, I>(combined_hash: &Secret, signature_share: &Se } /// Compute signature. -pub fn compute_signature<'a, I>(self_share: &'a Secret, other_shares: I) -> Result where I: Iterator { - compute_secret_sum(other_shares.chain(once(self_share))) +pub fn compute_signature<'a, I>(signature_shares: I) -> Result where I: Iterator { + compute_secret_sum(signature_shares) } #[cfg(test)] @@ -561,7 +561,7 @@ pub mod tests { // step 5: compute signature let signatures: Vec<_> = (0..n) - .map(|i| (combined_hash.clone(), compute_signature(&partial_signatures[i], received_signatures[i].iter()).unwrap())) + .map(|i| (combined_hash.clone(), compute_signature(received_signatures[i].iter().chain(once(&partial_signatures[i]))).unwrap())) .collect(); // === verify signature === diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 4ecacbc3308..5b0d952dcf4 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -120,7 +120,9 @@ pub enum SigningMessage { RequestPartialSignature(RequestPartialSignature), /// Partial signature is generated. PartialSignature(PartialSignature), - /// Partial signature is generated. + /// Signing error occured. + SigningSessionError(SigningSessionError), + /// Signing session completed. SigningSessionCompleted(SigningSessionCompleted), /* /// Initialize signing session. InitializeSigningSession(InitializeSigningSession), @@ -325,6 +327,17 @@ pub struct PartialSignature { pub partial_signature: SerializableSecret, } +#[derive(Clone, Debug, Serialize, Deserialize)] +/// When signing session error has occured. +pub struct SigningSessionError { + /// Encryption session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Error description. + pub error: String, +} + #[derive(Clone, Debug, Serialize, Deserialize)] /// Signing session completed. pub struct SigningSessionCompleted { diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index c5066725863..4d6e25b20c2 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -33,7 +33,7 @@ use key_server_cluster::generation_session::{SessionImpl as GenerationSession, S SessionState as GenerationSessionState, Session as GenerationSessionApi}; use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, - RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage}; + RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage, SigningSessionError}; /// Signing session API. pub trait Session: Send + Sync + 'static { @@ -300,14 +300,12 @@ impl SessionImpl { /// When session key related message is received. pub fn on_generation_message(&self, sender: NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { let mut data = self.data.lock(); -println!("=== on_generation_message: {:?}", data.state); // check state if data.state == SessionState::EstablishingConsensus { match message.message { GenerationMessage::InitializeSession(ref message) => { // if we are NOT part of consensus if data.consensus_session.as_ref().expect("TODO").state() != ConsensusSessionState::Finished { -println!("=== 11111111111111111111111"); return Err(Error::InvalidStateForRequest); } @@ -328,11 +326,9 @@ println!("=== 11111111111111111111111"); } } if data.state != SessionState::SessionKeyGeneration { -println!("=== 2222222222222222222222"); return Err(Error::InvalidStateForRequest); } // TODO: check master node + etc -println!("=== 3333333333333333333333"); // process message match message.message { @@ -358,7 +354,7 @@ println!("=== 3333333333333333333333"); if data.state == SessionState::Failed || data.generation_session.as_ref().expect("TODO").state() != GenerationSessionState::Finished { return Ok(()); } -println!("=== before start_waiting_for_partial_signing"); + // else ask other nodes to generate partial signatures SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } @@ -422,11 +418,15 @@ println!("=== before start_waiting_for_partial_signing"); return Err(Error::InvalidStateForRequest); } data.partial_signatures.push_back(message.partial_signature.clone().into()); + data.consensus.as_mut().expect("TODO").job_response_received(&sender, message.partial_signature.clone().into())?; // check if we have enough shadow points to decrypt the secret - if data.partial_signatures.len() != self.encrypted_data.threshold + 1 { + if !data.consensus.as_ref().expect("TODO").is_completed() { return Ok(()); } + //if data.partial_signatures.len() != self.encrypted_data.threshold + 1 { + // return Ok(()); + //} // notify all other nodes about session completion self.cluster.broadcast(Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { @@ -463,6 +463,19 @@ println!("=== before start_waiting_for_partial_signing"); Ok(()) } + /// When error has occured on another node. + pub fn on_session_error(&self, sender: NodeId, message: &SigningSessionError) -> Result<(), Error> { + let mut data = self.data.lock(); + + warn!("{}: signing session failed with error: {:?} from {}", self.node(), message.error, sender); + + data.state = SessionState::Failed; + data.signed_message = Some(Err(Error::Io(message.error.clone()))); + self.completed.notify_all(); + + Ok(()) + } + fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { match action { ConsensusSessionAction::BroadcastMessage(message) => { @@ -516,7 +529,7 @@ println!("=== before start_waiting_for_partial_signing"); // select nodes to make signature data.consensus.as_mut().expect("TODO").activate()?; let selected_nodes = data.consensus.as_mut().expect("TODO").select_nodes()?; -println!("start_generating_session_key.selected_nodes = {:?}", selected_nodes); + // create generation session let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); let generation_session = GenerationSession::new(GenerationSessionParams { @@ -543,17 +556,17 @@ println!("start_generating_session_key.selected_nodes = {:?}", selected_nodes); return Ok(()); } -println!("=== start_waiting_for_partial_signing: {:?}", data.consensus.as_ref().expect("TODO")); // nodes which have formed consensus group data.consensus.as_mut().expect("TODO").activate()?; data.consensus.as_mut().expect("TODO").select_nodes()?; let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").selected_nodes()?.clone(); -println!("=== start_waiting_for_partial_signing. confirmed_nodes = {:?}", confirmed_nodes); + // send requests data.partial_requests.clear(); data.partial_signatures.clear(); for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { data.partial_requests.insert(node.clone()); + data.consensus.as_mut().expect("TODO").job_request_sent(node)?; cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { session: session_id.clone().into(), sub_session: access_key.clone().into(), @@ -571,7 +584,9 @@ println!("=== start_waiting_for_partial_signing. confirmed_nodes = {:?}", confir let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &data.confirmed_nodes, session_joint_public, session_secret_coeff)? }; - data.partial_signatures.push_back(signing_result); + data.partial_signatures.push_back(signing_result.clone()); + data.consensus.as_mut().expect("TODO").job_request_sent(&self_node_id)?; + data.consensus.as_mut().expect("TODO").job_response_received(&self_node_id, signing_result)?; } Ok(()) @@ -579,7 +594,6 @@ println!("=== start_waiting_for_partial_signing. confirmed_nodes = {:?}", confir fn do_partial_signing(self_node_id: &NodeId, message_hash: &H256, encrypted_data: &DocumentKeyShare, session_nodes: &BTreeSet, session_joint_public: &Public, session_secret_coeff: &Secret) -> Result { debug_assert!(!session_nodes.contains(self_node_id)); -println!("nodes.len() = {} threshold = {}", session_nodes.len(), encrypted_data.threshold); debug_assert!(session_nodes.len() == encrypted_data.threshold); let combined_hash = math::combine_message_hash_with_public(&message_hash, &session_joint_public)?; @@ -597,7 +611,7 @@ println!("nodes.len() = {} threshold = {}", session_nodes.len(), encrypted_data. let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; - let signature_s = math::compute_signature(&data.partial_signatures[0], data.partial_signatures.iter().skip(1))?; + let signature_s = math::compute_signature(data.partial_signatures.iter())?; data.signed_message = Some(Ok((signature_c, signature_s))); @@ -607,15 +621,71 @@ println!("nodes.len() = {} threshold = {}", session_nodes.len(), encrypted_data. impl ClusterSession for SessionImpl { fn is_finished(&self) -> bool { - unimplemented!() + let data = self.data.lock(); + data.state == SessionState::Failed + || data.state == SessionState::Finished } - fn on_session_timeout(&self) { - unimplemented!() + fn on_node_timeout(&self, node: &NodeId) { + let mut data = self.data.lock(); + + let is_self_master = data.master.as_ref() == Some(self.node()); + let is_other_master = data.master.as_ref() == Some(node); + // if this is master node, we might have to restart + if is_self_master { + let is_restart_required = match data.consensus.as_mut() { + None => false, + Some(consensus) => match consensus.node_timeouted(node) { + Ok(false) => return, + Ok(true) => true, + Err(_) => false, //fall through + }, + }; + if is_restart_required { + if SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + return; + } + } + } else if !is_other_master { + // disconnected from non-master node on non-master node + // => this does not affect this session + return; + } + // else: disconnecting from master node means failure + + warn!("{}: signing session failed because {} connection has timeouted", self.node(), node); + + data.state = SessionState::Failed; + data.signed_message = Some(Err(Error::NodeDisconnected)); + self.completed.notify_all(); } - fn on_node_timeout(&self, node_id: &NodeId) { - unimplemented!() + fn on_session_timeout(&self) { + let mut data = self.data.lock(); + + let is_self_master = data.master.as_ref() == Some(self.node()); + // if this is master node, we might have to restart + if is_self_master { + let is_restart_required = match data.consensus.as_mut() { + None => false, + Some(consensus) => match consensus.session_timeouted() { + Ok(_) => true, + Err(_) => false, + }, + }; + if is_restart_required { + if SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + return; + } + } + } + + // no more nodes left for decryption => fail + warn!("{}: decryption session failed with timeout", self.node()); + + data.state = SessionState::Failed; + data.signed_message = Some(Err(Error::NodeDisconnected)); + self.completed.notify_all(); } } @@ -787,7 +857,6 @@ mod tests { } pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { -println!("=== MESSAGE {:?}", msg); match { match msg.2 { Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => self.nodes[&msg.1].session.on_consensus_message(msg.0.clone(), &message), From 10210ae010637f205bfc1b4abf91d3292d2f58e8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 May 2017 13:45:47 +0300 Subject: [PATCH 15/45] get rid of duplicated data in SigningSession --- .../src/key_server_cluster/consensus.rs | 13 +++++ .../src/key_server_cluster/signing_session.rs | 47 +++++-------------- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index f2df417edf4..1979c076162 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -196,6 +196,14 @@ impl Consensus where T: Debug + Clone { Ok(()) } + /// Return job responses. + pub fn job_responses(&self) -> Result<&BTreeMap, Error> { + match *self { + Consensus::Completed(ref consensus) => consensus.job_responses(), + _ => Err(Error::InvalidStateForRequest), + } + } + /// When node is timeouted. Returns true if consensus restarted (i.e. caller must resend job requests). pub fn node_timeouted(&mut self, node: &NodeId) -> Result { match *self { @@ -344,6 +352,11 @@ impl ActiveConsensus where T: Debug + Clone { Ok(()) } + /// Return job responses. + pub fn job_responses(&self) -> Result<&BTreeMap, Error> { + Ok(&self.responses) + } + /// Restart jobs. pub fn restart(&mut self) -> Result<(), Error> { self.selected_nodes.clear(); diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 4d6e25b20c2..9fc67a07634 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -117,26 +117,11 @@ struct SessionData { /// Generated session secret coefficient. session_secret_coeff: Option, - // === Values, filled when partial signatures are generating === - /// Nodes which have agreed to make partial signatures. - confirmed_nodes: BTreeSet, - /// Active partial requests. - partial_requests: BTreeSet, - /// Partial signatures. - partial_signatures: VecDeque, - /// === Values, filled during final decryption === /// Decrypted secret signed_message: Option>, } -#[derive(Debug, Clone)] -/// Mutable node-specific data. -struct NodeData { - /// Random unique scalar. Persistent. - pub id_number: Secret, -} - #[derive(Debug, Clone, PartialEq)] /// Distributed key generation session state. pub enum SessionState { @@ -145,10 +130,16 @@ pub enum SessionState { WaitingForInitialization, /// Establishing consensus. EstablishingConsensus, + /// Consensus established + EstablishedConsensus, - // === Intermediate states === + /// === One-time key genration states === /// Generating one-time key. SessionKeyGeneration, + /// One-time key generated. + SessionKeyGenerated, + + // === Signature generation states === /// Waiting for partial signatures. WaitingForPartialSignature, @@ -195,9 +186,6 @@ impl SessionImpl { generation_session: None, session_joint_public: None, session_secret_coeff: None, - confirmed_nodes: BTreeSet::new(), - partial_requests: BTreeSet::new(), - partial_signatures: VecDeque::new(), signed_message: None, }) }) @@ -414,19 +402,12 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } - if !data.partial_requests.remove(&sender) { - return Err(Error::InvalidStateForRequest); - } - data.partial_signatures.push_back(message.partial_signature.clone().into()); data.consensus.as_mut().expect("TODO").job_response_received(&sender, message.partial_signature.clone().into())?; // check if we have enough shadow points to decrypt the secret if !data.consensus.as_ref().expect("TODO").is_completed() { return Ok(()); } - //if data.partial_signatures.len() != self.encrypted_data.threshold + 1 { - // return Ok(()); - //} // notify all other nodes about session completion self.cluster.broadcast(Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { @@ -558,14 +539,10 @@ impl SessionImpl { // nodes which have formed consensus group data.consensus.as_mut().expect("TODO").activate()?; - data.consensus.as_mut().expect("TODO").select_nodes()?; - let confirmed_nodes: BTreeSet<_> = data.consensus.as_ref().expect("TODO").selected_nodes()?.clone(); + let mut confirmed_nodes = data.consensus.as_mut().expect("TODO").select_nodes()?.clone(); // send requests - data.partial_requests.clear(); - data.partial_signatures.clear(); for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { - data.partial_requests.insert(node.clone()); data.consensus.as_mut().expect("TODO").job_request_sent(node)?; cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { session: session_id.clone().into(), @@ -576,15 +553,13 @@ impl SessionImpl { } // confirmation from this node - data.confirmed_nodes = confirmed_nodes; - if data.confirmed_nodes.remove(&self_node_id) { + if confirmed_nodes.remove(self_node_id) { let signing_result = { let message_hash = data.message_hash.as_ref().expect("TODO"); let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); - SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &data.confirmed_nodes, session_joint_public, session_secret_coeff)? + SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &confirmed_nodes, session_joint_public, session_secret_coeff)? }; - data.partial_signatures.push_back(signing_result.clone()); data.consensus.as_mut().expect("TODO").job_request_sent(&self_node_id)?; data.consensus.as_mut().expect("TODO").job_response_received(&self_node_id, signing_result)?; } @@ -611,7 +586,7 @@ impl SessionImpl { let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; - let signature_s = math::compute_signature(data.partial_signatures.iter())?; + let signature_s = math::compute_signature(data.consensus.as_ref().expect("TODO").job_responses()?.values())?; data.signed_message = Some(Ok((signature_c, signature_s))); From 40e7a688cb0a288863e28afb7b976b4a69da4138 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 May 2017 17:23:28 +0300 Subject: [PATCH 16/45] TODOs in signing session --- .../src/key_server_cluster/cluster.rs | 12 +- .../src/key_server_cluster/consensus.rs | 4 + .../key_server_cluster/generation_session.rs | 54 ++-- .../src/key_server_cluster/message.rs | 10 - .../src/key_server_cluster/signing_session.rs | 299 +++++++++++------- 5 files changed, 213 insertions(+), 166 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 97b6d583278..c987c33251b 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -1093,15 +1093,15 @@ pub mod tests { // start && wait for generation session to fail let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); - loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_key().is_some()); - assert!(session.joint_public_key().unwrap().is_err()); + loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_and_secret().is_some()); + assert!(session.joint_public_and_secret().unwrap().is_err()); // check that faulty session is either removed from all nodes, or nonexistent (already removed) assert!(clusters[0].client().generation_session(&SessionId::default()).is_none()); for i in 1..3 { if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { - loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_key().is_some()); - assert!(session.joint_public_key().unwrap().is_err()); + loop_until(&mut core, time::Duration::from_millis(300), || session.joint_public_and_secret().is_some()); + assert!(session.joint_public_and_secret().unwrap().is_err()); assert!(clusters[i].client().generation_session(&SessionId::default()).is_none()); } } @@ -1117,14 +1117,14 @@ pub mod tests { // start && wait for generation session to complete let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished); - assert!(session.joint_public_key().unwrap().is_ok()); + assert!(session.joint_public_and_secret().unwrap().is_ok()); // check that session is either removed from all nodes, or nonexistent (already removed) assert!(clusters[0].client().generation_session(&SessionId::default()).is_none()); for i in 1..3 { if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished); - assert!(session.joint_public_key().unwrap().is_err()); + assert!(session.joint_public_and_secret().unwrap().is_err()); assert!(clusters[i].client().generation_session(&SessionId::default()).is_none()); } } diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 1979c076162..11cae8f891f 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . +// TODO: when job requests sent && we restart, we could receive previous responses => there should be some kind of control (random secret in each group of jobs) + use std::fmt::Debug; use std::collections::{BTreeSet, BTreeMap, VecDeque}; use key_server_cluster::{Error, NodeId}; @@ -185,6 +187,7 @@ impl Consensus where T: Debug + Clone { if consensus.responses.len() != consensus.core.threshold + 1 { return Ok(()); } + debug_assert!(consensus.active_requests.is_empty()); // else fall through consensus.clone() @@ -339,6 +342,7 @@ impl ActiveConsensus where T: Debug + Clone { return Err(Error::InvalidNodeForRequest); } + debug_assert!(self.active_requests.len() <= self.core.threshold + 1); Ok(()) } diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index 200dc912e21..bedfd4671e7 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -34,7 +34,7 @@ pub trait Session: Send + Sync + 'static { /// Wait until session is completed. Returns public portion of generated server key. fn wait(&self, timeout: Option) -> Result; /// Get joint public key (if it is known). - fn joint_public_key(&self) -> Option>; + fn joint_public_and_secret(&self) -> Option>; } /// Distributed key generation session. @@ -107,9 +107,7 @@ struct SessionData { /// Key share. key_share: Option>, /// Jointly generated public key, which can be used to encrypt secret. Public. - joint_public: Option>, - /// Secret coefficient (should not be stored anywhere!!!). - secret_coeff_result: Option>, + joint_public_and_secret: Option>, } #[derive(Debug, Clone)] @@ -201,8 +199,7 @@ impl SessionImpl { secret_coeff: None, secret_share: None, key_share: None, - joint_public: None, - secret_coeff_result: None, + joint_public_and_secret: None, }), } } @@ -223,11 +220,6 @@ impl SessionImpl { self.data.lock().key_share.clone() } - /// Get secret polynom coefficient. - pub fn secret_coeff(&self) -> Option> { - self.data.lock().secret_coeff_result.clone() - } - /// Simulate faulty generation session behaviour. pub fn simulate_faulty_behaviour(&self) { self.data.lock().simulate_faulty_behaviour = true; @@ -532,8 +524,7 @@ impl SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::Io(message.error.clone()))); - data.joint_public = Some(Err(Error::Io(message.error.clone()))); - data.secret_coeff_result = Some(Err(Error::Io(message.error.clone()))); + data.joint_public_and_secret = Some(Err(Error::Io(message.error.clone()))); self.completed.notify_all(); Ok(()) @@ -668,10 +659,10 @@ impl SessionImpl { }; // if we are at the slave node - wait for session completion + let secret_coeff = data.secret_coeff.as_ref().expect("secret coeff is selected on initialization phase; current phase follows initialization; qed").clone(); if data.master.as_ref() != Some(self.node()) { data.key_share = Some(Ok(encrypted_data)); - data.joint_public = Some(Ok(joint_public)); - data.secret_coeff_result = Some(Ok(data.secret_coeff.as_ref().expect("TODO").clone())); + data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff))); data.state = SessionState::WaitingForGenerationConfirmation; return Ok(()); } @@ -693,8 +684,7 @@ impl SessionImpl { self_node.completion_confirmed = true; } data.key_share = Some(Ok(encrypted_data)); - data.joint_public = Some(Ok(joint_public)); - data.secret_coeff_result = Some(Ok(data.secret_coeff.as_ref().expect("TODO").clone())); + data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff))); data.state = SessionState::WaitingForGenerationConfirmation; Ok(()) @@ -717,8 +707,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::NodeDisconnected)); - data.joint_public = Some(Err(Error::NodeDisconnected)); - data.secret_coeff_result = Some(Err(Error::NodeDisconnected)); + data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } @@ -729,8 +718,7 @@ impl ClusterSession for SessionImpl { data.state = SessionState::Failed; data.key_share = Some(Err(Error::NodeDisconnected)); - data.joint_public = Some(Err(Error::NodeDisconnected)); - data.secret_coeff_result = Some(Err(Error::NodeDisconnected)); + data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); self.completed.notify_all(); } } @@ -742,20 +730,20 @@ impl Session for SessionImpl { fn wait(&self, timeout: Option) -> Result { let mut data = self.data.lock(); - if !data.joint_public.is_some() { + if !data.joint_public_and_secret.is_some() { match timeout { None => self.completed.wait(&mut data), Some(timeout) => { self.completed.wait_for(&mut data, timeout); }, } } - data.joint_public.as_ref() + data.joint_public_and_secret.clone() .expect("checked above or waited for completed; completed is only signaled when joint_public.is_some(); qed") - .clone() + .map(|p| p.0) } - fn joint_public_key(&self) -> Option> { - self.data.lock().joint_public.clone() + fn joint_public_and_secret(&self) -> Option> { + self.data.lock().joint_public_and_secret.clone() } } @@ -1181,17 +1169,17 @@ pub mod tests { #[test] fn encryption_fails_on_session_timeout() { let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); - assert!(l.master().joint_public_key().is_none()); + assert!(l.master().joint_public_and_secret().is_none()); l.master().on_session_timeout(); - assert!(l.master().joint_public_key().unwrap().unwrap_err() == Error::NodeDisconnected); + assert!(l.master().joint_public_and_secret().unwrap().unwrap_err() == Error::NodeDisconnected); } #[test] fn encryption_fails_on_node_timeout() { let (_, _, _, l) = make_simple_cluster(0, 2).unwrap(); - assert!(l.master().joint_public_key().is_none()); + assert!(l.master().joint_public_and_secret().is_none()); l.master().on_node_timeout(l.first_slave().node()); - assert!(l.master().joint_public_key().unwrap().unwrap_err() == Error::NodeDisconnected); + assert!(l.master().joint_public_and_secret().unwrap().unwrap_err() == Error::NodeDisconnected); } #[test] @@ -1208,11 +1196,11 @@ pub mod tests { } // check that all nodes has finished joint public generation - let joint_public_key = l.master().joint_public_key().unwrap().unwrap(); + let joint_public_key = l.master().joint_public_and_secret().unwrap().unwrap().0; for node in l.nodes.values() { let state = node.session.state(); assert_eq!(state, SessionState::Finished); - assert_eq!(node.session.joint_public_key().as_ref(), Some(&Ok(joint_public_key))); + assert_eq!(node.session.joint_public_and_secret().map(|p| p.map(|p| p.0)), Some(Ok(joint_public_key))); } // now let's encrypt some secret (which is a point on EC) @@ -1247,7 +1235,7 @@ pub mod tests { // run session to completion let session_id = SessionId::default(); let session = clusters[0].client().new_generation_session(session_id, Public::default(), threshold).unwrap(); - loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_key().is_some()); + loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_and_secret().is_some()); } } } diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 5b0d952dcf4..5d436ca5078 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -124,16 +124,6 @@ pub enum SigningMessage { SigningSessionError(SigningSessionError), /// Signing session completed. SigningSessionCompleted(SigningSessionCompleted), -/* /// Initialize signing session. - InitializeSigningSession(InitializeSigningSession), - /// Confirm/reject signing session initialization. - ConfirmSigningInitialization(ConfirmSigningInitialization), - /// Nonce generation message. - SigningNonceGeneration(SigningNonceGeneration), - /// When signature session error has occured. - SignatureSessionError(SignatureSessionError), - /// When signature session is completed. - ,*/ } #[derive(Clone, Debug, Serialize, Deserialize)] diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 9fc67a07634..8ae81cce671 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -213,9 +213,9 @@ impl SessionImpl { data.master = Some(self.node().clone()); data.requestor = Some(requestor_public.clone()); data.message_hash = Some(message_hash); - data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); // create consensus session + let mut consensus = Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?; let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { id: self.id.clone(), self_node_id: self.self_node_id.clone(), @@ -224,16 +224,20 @@ impl SessionImpl { })?; // start consensus session - let consensus_action = consensus_session.initialize(requestor_signature, data.consensus.as_mut().expect("TODO"))?; + let consensus_action = consensus_session.initialize(requestor_signature, &mut consensus)?; + data.consensus = Some(consensus); data.consensus_session = Some(consensus_session); + + // process consensus action SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; // if single node is required to sign message, proceed - if data.state != SessionState::Failed && data.consensus.is_some() { - //data.state = SessionState::Finished; - //SessionImpl::start_waiting_for_partial_signature(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; - //SessionImpl::make_signature(self.access_key.clone(), &self.encrypted_data, &mut *data)?; - //self.completed.notify_all(); + if data.state == SessionState::EstablishedConsensus { + SessionImpl::start_generating_session_key(self.self_node_id.clone(), &self.encrypted_data, &self.cluster, &mut *data)?; + SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; + SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; + SessionImpl::do_signing(&mut *data)?; + self.completed.notify_all(); } Ok(()) @@ -241,13 +245,14 @@ impl SessionImpl { /// When consensus-related message is received. pub fn on_consensus_message(&self, sender: NodeId, message: &SigningConsensusMessage) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(self.access_key == *message.sub_session); + let mut data = self.data.lock(); - let mut data = data.deref_mut(); // if we are waiting for initialization if data.state == SessionState::WaitingForInitialization { data.master = Some(sender.clone()); - //data.requestor = Some(requestor_public); data.state = SessionState::EstablishingConsensus; data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { @@ -260,49 +265,69 @@ impl SessionImpl { // check state if data.state != SessionState::EstablishingConsensus { - return Ok(()); - // TODO: received after completion return Err(Error::InvalidStateForRequest); + // consensus is already established => mark node as confirmed (for restart case) and ignore + return data.consensus.as_mut().map(|consensus| consensus.accept_offer(&sender)).unwrap_or(Ok(())); } - // TODO: check master node + etc // process message - let consensus_action = match message.message { - ConsensusMessage::InitializeConsensusSession(ref message) => { - let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; - data.requestor = Some(requestor.clone()); - data.consensus_session.as_mut().expect("TODO").on_initialize_session(sender, &requestor)? - }, - ConsensusMessage::ConfirmConsensusInitialization(ref message) => - data.consensus_session.as_mut().expect("TODO").on_confirm_initialization(sender, message.is_confirmed, data.consensus.as_mut().expect("TODO"))?, + let consensus_action = { + let mut data = data.deref_mut(); + let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest)?; + match message.message { + ConsensusMessage::InitializeConsensusSession(ref message) => { + let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; + data.requestor = Some(requestor.clone()); + consensus_session.on_initialize_session(sender, &requestor)? + }, + ConsensusMessage::ConfirmConsensusInitialization(ref message) => { + let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; + consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? + }, + } }; SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; - // if consensus is reached, start generating session key - if !data.consensus.as_ref().expect("TODO").is_established() { + // if consensus is established, start generating session key + if data.state != SessionState::EstablishedConsensus { return Ok(()); } + SessionImpl::start_generating_session_key(self.self_node_id.clone(), &self.encrypted_data, &self.cluster, &mut *data)?; SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data) } /// When session key related message is received. pub fn on_generation_message(&self, sender: NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { + debug_assert!(self.id == *message.session); + debug_assert!(self.access_key == *message.sub_session); + let mut data = self.data.lock(); + // check state if data.state == SessionState::EstablishingConsensus { + // on 'slave' nodes, consensus is established when session key generation starts + if data.master.as_ref() != Some(&sender) { + return Err(Error::InvalidMessage); + } + + data.state = SessionState::EstablishedConsensus; + } + if data.state == SessionState::EstablishedConsensus { match message.message { GenerationMessage::InitializeSession(ref message) => { - // if we are NOT part of consensus - if data.consensus_session.as_ref().expect("TODO").state() != ConsensusSessionState::Finished { - return Err(Error::InvalidStateForRequest); + // check message + if data.master.as_ref() != Some(&sender) { + return Err(Error::InvalidMessage); } // update state data.state = SessionState::SessionKeyGeneration; - // create generation session + // cluster for generation session would only include nodes, which has fodmed consensus group let generation_cluster = Arc::new(SigningCluster::new(self.cluster.clone(), self.self_node_id.clone(), message.nodes.keys().cloned().map(Into::into).collect())); data.generation_cluster = Some(generation_cluster.clone()); + + // create generation session data.generation_session = Some(GenerationSession::new(GenerationSessionParams { id: message.session.clone().into(), self_node_id: self.self_node_id.clone(), @@ -313,37 +338,37 @@ impl SessionImpl { _ => return Err(Error::InvalidStateForRequest), } } - if data.state != SessionState::SessionKeyGeneration { - return Err(Error::InvalidStateForRequest); - } - // TODO: check master node + etc + // do not check for other states, as completion message can come after moving to other states + // if generation session exists, let it process message // process message - match message.message { - GenerationMessage::InitializeSession(ref message) => - data.generation_session.as_ref().expect("TODO").on_initialize_session(sender, &message)?, - GenerationMessage::ConfirmInitialization(ref message) => - data.generation_session.as_ref().expect("TODO").on_confirm_initialization(sender, &message)?, - GenerationMessage::CompleteInitialization(ref message) => - data.generation_session.as_ref().expect("TODO").on_complete_initialization(sender, &message)?, - GenerationMessage::KeysDissemination(ref message) => - data.generation_session.as_ref().expect("TODO").on_keys_dissemination(sender, &message)?, - GenerationMessage::PublicKeyShare(ref message) => - data.generation_session.as_ref().expect("TODO").on_public_key_share(sender, &message)?, - GenerationMessage::SessionError(ref message) => - data.generation_session.as_ref().expect("TODO").on_session_error(sender, &message)?, - GenerationMessage::SessionCompleted(ref message) => - data.generation_session.as_ref().expect("TODO").on_session_completed(sender, &message)?, - }; + let is_generation_completed = data.session_joint_public.is_some(); + { + let generation_session = data.generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; + match message.message { + GenerationMessage::InitializeSession(ref message) => + generation_session.on_initialize_session(sender, message)?, + GenerationMessage::ConfirmInitialization(ref message) => + generation_session.on_confirm_initialization(sender, message)?, + GenerationMessage::CompleteInitialization(ref message) => + generation_session.on_complete_initialization(sender, message)?, + GenerationMessage::KeysDissemination(ref message) => + generation_session.on_keys_dissemination(sender, message)?, + GenerationMessage::PublicKeyShare(ref message) => + generation_session.on_public_key_share(sender, message)?, + GenerationMessage::SessionError(ref message) => + generation_session.on_session_error(sender, message)?, + GenerationMessage::SessionCompleted(ref message) => + generation_session.on_session_completed(sender, message)?, + } + } SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; - // if session key generated is not yet completed => continue - //if data.state == SessionState::Failed || !data.session_joint_public.is_some() { - if data.state == SessionState::Failed || data.generation_session.as_ref().expect("TODO").state() != GenerationSessionState::Finished { + // if session key generated just now => start generating partial signatures + if data.state != SessionState::SessionKeyGenerated || is_generation_completed { return Ok(()); } - // else ask other nodes to generate partial signatures SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } @@ -352,18 +377,13 @@ impl SessionImpl { debug_assert!(self.access_key == *message.sub_session); debug_assert!(&sender != self.node()); - // TODO: check message - //if message.nodes.len() != self.encrypted_data.threshold + 1 { - // return Err(Error::InvalidMessage); - //} - let mut data = self.data.lock(); // check state if data.master != Some(sender) { return Err(Error::InvalidMessage); } - if data.state != SessionState::EstablishingConsensus && data.generation_session.as_ref().expect("TODO").state() != GenerationSessionState::Finished { + if data.state != SessionState::SessionKeyGenerated { return Err(Error::InvalidStateForRequest); } @@ -371,10 +391,9 @@ impl SessionImpl { data.message_hash = Some(message.message_hash.clone().into()); // calculate partial signature - let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); - let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); - let mut nodes: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).collect(); - nodes.remove(&self.self_node_id); + let session_joint_public = data.session_joint_public.as_ref().expect("we are in SessionKeyGenerated state; public is generated during SessionKeyGenerating; qed"); + let session_secret_coeff = data.session_secret_coeff.as_ref().expect("we are in SessionKeyGenerated state; coeff is generated during SessionKeyGenerating; qed"); + let mut nodes: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).filter(|n| n != &self.self_node_id).collect(); let partial_signature = SessionImpl::do_partial_signing(&self.self_node_id, &message.message_hash.clone().into(), &self.encrypted_data, &nodes, session_joint_public, session_secret_coeff)?; self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { @@ -402,11 +421,15 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } - data.consensus.as_mut().expect("TODO").job_response_received(&sender, message.partial_signature.clone().into())?; + // remember partial signature + { + let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; + consensus.job_response_received(&sender, message.partial_signature.clone().into())?; - // check if we have enough shadow points to decrypt the secret - if !data.consensus.as_ref().expect("TODO").is_completed() { - return Ok(()); + // check if we have enough shadow points to decrypt the secret + if !consensus.is_completed() { + return Ok(()); + } } // notify all other nodes about session completion @@ -430,13 +453,14 @@ impl SessionImpl { let mut data = self.data.lock(); - // check state - /* TODO: if data.state != SessionState::WaitingForPartialDecryptionRequest { - return Err(Error::InvalidStateForRequest); - }*/ if data.master != Some(sender) { return Err(Error::InvalidMessage); } + // it is up to master node to decide when to complete session + // => we could only fail if already failed + if data.state == SessionState::Failed { + return Err(Error::InvalidStateForRequest); + } // update state data.state = SessionState::Finished; @@ -457,6 +481,7 @@ impl SessionImpl { Ok(()) } + /// Process nested consensus session action. fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { match action { ConsensusSessionAction::BroadcastMessage(message) => { @@ -473,43 +498,29 @@ impl SessionImpl { message: message, }))) }, - _ => Ok(()), - } - } - - fn process_generation_session_action(id: &SessionId, access_key: &Secret, completed: &Condvar, data: &mut SessionData) -> Result<(), Error> { - let generation_cluster = data.generation_cluster.as_ref().expect("TODO").clone(); - for (to, message) in generation_cluster.messages() { - match message { - Message::Generation(message) => generation_cluster.cluster().send(&to, Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - })))?, - _ => unreachable!("generation session only sends generation messages"), - } - } - - match data.generation_session.as_ref().expect("TODO").joint_public_key() { - Some(Ok(session_joint_public)) => { - data.session_joint_public = Some(session_joint_public); - data.session_secret_coeff = Some(data.generation_session.as_ref().expect("TODO").secret_coeff().expect("TODO").expect("TODO")); - Ok(()) - }, - Some(Err(err)) => { - data.state = SessionState::Failed; - data.signed_message = Some(Err(err)); - completed.notify_all(); + ConsensusSessionAction::CheckStatus => { + match data.consensus_session.as_ref() + .expect("we are processing consensus session action; action is a result of processing message by session; qed") + .state() { + ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, + ConsensusSessionState::Failed => { + data.state = SessionState::Failed; + data.signed_message = Some(Err(Error::ConsensusUnreachable)); + completed.notify_all(); + }, + _ => (), + } Ok(()) }, - None => Ok(()), } } + /// Start generating one-time session key. fn start_generating_session_key(self_node_id: NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { // select nodes to make signature - data.consensus.as_mut().expect("TODO").activate()?; - let selected_nodes = data.consensus.as_mut().expect("TODO").select_nodes()?; + let mut consensus = data.consensus.as_mut().expect("consensus is filled during initialization phase; key generation phase follows initialization; qed"); + consensus.activate()?; + let selected_nodes = consensus.select_nodes()?; // create generation session let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); @@ -521,7 +532,8 @@ impl SessionImpl { }); // start generation session - let result = generation_session.initialize(data.requestor.as_ref().expect("TODO").clone(), encrypted_data.threshold, selected_nodes.clone())?; + let result = generation_session.initialize(Public::default(), // doesn't matter + encrypted_data.threshold, selected_nodes.clone())?; data.generation_cluster = Some(generation_cluster); data.generation_session = Some(generation_session); @@ -531,42 +543,92 @@ impl SessionImpl { Ok(()) } + /// Process nested key generation session action. + fn process_generation_session_action(id: &SessionId, access_key: &Secret, completed: &Condvar, data: &mut SessionData) -> Result<(), Error> { + // it only makes sense to process actions if session is currently active + if data.state != SessionState::SessionKeyGeneration { + return Ok(()); + } + + // send every scheduled message + { + let generation_cluster = data.generation_cluster.as_ref() + .expect("generation cluster is crated when SessionKeyGeneration state starts; we are in SessionKeyGeneration state; qed"); + for (to, message) in generation_cluster.messages() { + match message { + Message::Generation(message) => generation_cluster.cluster().send(&to, Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + })))?, + _ => unreachable!("generation session only sends generation messages"), + } + } + } + + // and now check if session key is generated + let generation_session = data.generation_session.as_ref() + .expect("generation session is crated when SessionKeyGeneration state starts; we are in SessionKeyGeneration state; qed"); + match generation_session.joint_public_and_secret() { + Some(Ok(session_joint_public_and_secret)) => { + data.state = SessionState::SessionKeyGenerated; + data.session_joint_public = Some(session_joint_public_and_secret.0); + data.session_secret_coeff = Some(session_joint_public_and_secret.1); + Ok(()) + }, + Some(Err(err)) => { + data.state = SessionState::Failed; + data.signed_message = Some(Err(err)); + completed.notify_all(); + Ok(()) + }, + None => Ok(()), + } + } + + /// Start waiting for partial signatures/partial signatures requests. fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - data.state = SessionState::WaitingForPartialSignature; + data.state = SessionState::SessionKeyGenerated; if data.master.as_ref() != Some(self_node_id) { + // if we are on the slave node, wait for partial signature requests return Ok(()); } - // nodes which have formed consensus group - data.consensus.as_mut().expect("TODO").activate()?; - let mut confirmed_nodes = data.consensus.as_mut().expect("TODO").select_nodes()?.clone(); + // update state + data.state = SessionState::WaitingForPartialSignature; + + // send jobs to all selected nodes + let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial signing phase follows initialization; qed"); + let mut confirmed_nodes = consensus.selected_nodes()?.clone(); // send requests + let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { - data.consensus.as_mut().expect("TODO").job_request_sent(node)?; + consensus.job_request_sent(node)?; cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { session: session_id.clone().into(), sub_session: access_key.clone().into(), - message_hash: data.message_hash.as_ref().expect("TODO").clone().into(), + message_hash: message_hash.clone().into(), nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), })))?; } - // confirmation from this node + // confirmation from this node, if this node is in consensus group if confirmed_nodes.remove(self_node_id) { let signing_result = { - let message_hash = data.message_hash.as_ref().expect("TODO"); - let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); - let session_secret_coeff = data.session_secret_coeff.as_ref().expect("TODO"); + let session_joint_public = data.session_joint_public.as_ref().expect("session key is generated on key generation phase; partial signing phase follows initialization; qed"); + let session_secret_coeff = data.session_secret_coeff.as_ref().expect("sessin coeff is generated on key generation phase; partial signing phase follows initialization; qed"); SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &confirmed_nodes, session_joint_public, session_secret_coeff)? }; - data.consensus.as_mut().expect("TODO").job_request_sent(&self_node_id)?; - data.consensus.as_mut().expect("TODO").job_response_received(&self_node_id, signing_result)?; + + consensus.job_request_sent(&self_node_id)?; + consensus.job_response_received(&self_node_id, signing_result)?; } Ok(()) } + /// Compute partial signature. fn do_partial_signing(self_node_id: &NodeId, message_hash: &H256, encrypted_data: &DocumentKeyShare, session_nodes: &BTreeSet, session_joint_public: &Public, session_secret_coeff: &Secret) -> Result { debug_assert!(!session_nodes.contains(self_node_id)); debug_assert!(session_nodes.len() == encrypted_data.threshold); @@ -581,12 +643,14 @@ impl SessionImpl { ) } + /// Compute signature fn do_signing(data: &mut SessionData) -> Result<(), Error> { - let message_hash = data.message_hash.as_ref().expect("TODO"); - let session_joint_public = data.session_joint_public.as_ref().expect("TODO"); - + let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); + let session_joint_public = data.session_joint_public.as_ref().expect("session key is generated on key generation phase; signing phase follows initialization; qed"); + let partial_signatures = data.consensus.as_ref().expect("consensus on master is filled in initialization phase; this is master node; qed").job_responses()?.values(); + let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; - let signature_s = math::compute_signature(data.consensus.as_ref().expect("TODO").job_responses()?.values())?; + let signature_s = math::compute_signature(partial_signatures)?; data.signed_message = Some(Ok((signature_c, signature_s))); @@ -705,7 +769,7 @@ impl Cluster for SigningCluster { impl Session for SessionImpl { fn state(&self) -> SessionState { - unimplemented!() + self.data.lock().state.clone() } fn wait(&self, timeout: Option) -> Result<(Secret, Secret), Error> { @@ -859,6 +923,7 @@ mod tests { #[test] fn complete_gen_sign_session() { + //let test_cases = [(0, 1)]; let test_cases = [(1, 3)]; for &(threshold, num_nodes) in &test_cases { // run key generation sessions @@ -879,7 +944,7 @@ mod tests { } // verify signature - let public = gl.master().joint_public_key().unwrap().unwrap(); + let public = gl.master().joint_public_and_secret().unwrap().unwrap().0; let signature = sl.master().wait(None).unwrap(); assert!(math::verify_signature(&public, &signature, &message_hash).unwrap()); } From 9e1d817e76404dba5cdeb67a0ef8f70ff32bc180 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 May 2017 19:55:28 +0300 Subject: [PATCH 17/45] fixing tests --- .../src/key_server_cluster/cluster.rs | 14 +- .../src/key_server_cluster/consensus.rs | 64 ++- .../key_server_cluster/consensus_session.rs | 7 +- .../key_server_cluster/decryption_session.rs | 490 ++++++++---------- .../src/key_server_cluster/io/message.rs | 22 +- .../src/key_server_cluster/message.rs | 44 +- .../src/key_server_cluster/signing_session.rs | 9 +- 7 files changed, 319 insertions(+), 331 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index c987c33251b..5ff7fe1a8ad 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -29,7 +29,8 @@ use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; use key_server_cluster::cluster_sessions::ClusterSessions; -use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage}; +use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, + SigningMessage, ConsensusMessage}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; #[cfg(test)] use key_server_cluster::generation_session::SessionImpl as GenerationSessionImpl; @@ -547,7 +548,10 @@ impl ClusterCore { let decryption_session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); let mut sender = connection.node_id().clone(); let session = match message { - DecryptionMessage::InitializeDecryptionSession(_) => { + DecryptionMessage::DecryptionConsensusMessage(ref message) if match message.message { + ConsensusMessage::InitializeConsensusSession(_) => true, + _ => false, + } => { let mut connected_nodes = data.connections.connected_nodes(); connected_nodes.insert(data.self_key_pair.public().clone()); @@ -563,10 +567,8 @@ impl ClusterCore { let mut is_queued_message = false; loop { match session.clone().and_then(|session| match message { - DecryptionMessage::InitializeDecryptionSession(ref message) => - session.on_initialize_session(sender.clone(), message), - DecryptionMessage::ConfirmDecryptionInitialization(ref message) => - session.on_confirm_initialization(sender.clone(), message), + DecryptionMessage::DecryptionConsensusMessage(ref message) => + session.on_consensus_message(sender.clone(), message), DecryptionMessage::RequestPartialDecryption(ref message) => session.on_partial_decryption_requested(sender.clone(), message), DecryptionMessage::PartialDecryption(ref message) => diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 11cae8f891f..61a3ce5fb41 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -76,6 +76,15 @@ impl Consensus where T: Debug + Clone { })) } + /// Return consensus core reference. + pub fn core(&self) -> Option<&ConsensusCore> { + match *self { + Consensus::Establishing(ref consensus) | Consensus::Established(ref consensus) => Some(&consensus), + Consensus::Active(ref consensus) | Consensus::Completed(ref consensus) => Some(&consensus.core), + Consensus::Unreachable => None, + } + } + /// Is consenus established. pub fn is_established(&self) -> bool { match *self { @@ -92,6 +101,14 @@ impl Consensus where T: Debug + Clone { } } + /// Is consensus unreachable. + pub fn is_unreachable(&self) -> bool { + match *self { + Consensus::Unreachable => true, + _ => false, + } + } + /// When node responds to join offer. pub fn offer_response(&mut self, node: &NodeId, is_accepted: bool) -> Result<(), Error> { if is_accepted { @@ -115,7 +132,7 @@ impl Consensus where T: Debug + Clone { Consensus::Established(ref mut consensus) => return consensus.accept_offer(node), Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => return consensus.core.accept_offer(node), - Consensus::Unreachable => return Err(Error::InvalidStateForRequest), + Consensus::Unreachable => { println!("=== a"); return Err(Error::InvalidStateForRequest) }, }; *self = Consensus::Established(established_consensus); @@ -136,11 +153,11 @@ impl Consensus where T: Debug + Clone { Consensus::Established(ref mut consensus) => return consensus.reject_offer(node), Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => return consensus.core.reject_offer(node), - _ => return Err(Error::InvalidStateForRequest), + _ => {println!("=== b"); return Err(Error::InvalidStateForRequest) }, } *self = Consensus::Unreachable; - Err(Error::ConsensusUnreachable) + Ok(()) } /// When starting/restarting requesting consensus nodes to do their job. @@ -148,7 +165,7 @@ impl Consensus where T: Debug + Clone { let active_consensus = match *self { Consensus::Established(ref established_consensus) => ActiveConsensus::new(established_consensus.clone()), Consensus::Active(ref active_consensus) => ActiveConsensus::new(active_consensus.core.clone()), - _ => return Err(Error::InvalidStateForRequest), + _ => { println!("=== c"); return Err(Error::InvalidStateForRequest) }, }; *self = Consensus::Active(active_consensus); @@ -159,7 +176,7 @@ impl Consensus where T: Debug + Clone { pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { match *self { Consensus::Active(ref mut consensus) => consensus.select_nodes(), - _ => Err(Error::InvalidStateForRequest), + _ => {println!("=== d"); Err(Error::InvalidStateForRequest) }, } } @@ -167,7 +184,7 @@ impl Consensus where T: Debug + Clone { pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { match *self { Consensus::Active(ref consensus) => consensus.selected_nodes(), - _ => Err(Error::InvalidStateForRequest), + _ => {println!("=== e"); Err(Error::InvalidStateForRequest) }, } } @@ -175,7 +192,7 @@ impl Consensus where T: Debug + Clone { pub fn job_request_sent(&mut self, node: &NodeId) -> Result<(), Error> { match *self { Consensus::Active(ref mut consensus) => consensus.job_request_sent(node), - _ => Err(Error::InvalidStateForRequest), + _ => {println!("=== f"); Err(Error::InvalidStateForRequest) }, } } @@ -192,18 +209,27 @@ impl Consensus where T: Debug + Clone { // else fall through consensus.clone() }, - _ => return Err(Error::InvalidStateForRequest), + _ => {println!("=== g"); return Err(Error::InvalidStateForRequest) }, }; *self = Consensus::Completed(completed_consensus); Ok(()) } + /// Return job reqeuests. + pub fn job_requests(&self) -> Result<&BTreeSet, Error> { + match *self { + Consensus::Active(ref consensus) => consensus.job_requests(), + _ => {println!("=== h"); Err(Error::InvalidStateForRequest) }, + } + } + /// Return job responses. pub fn job_responses(&self) -> Result<&BTreeMap, Error> { match *self { + Consensus::Active(ref consensus) => consensus.job_responses(), Consensus::Completed(ref consensus) => consensus.job_responses(), - _ => Err(Error::InvalidStateForRequest), + _ => {println!("=== i"); Err(Error::InvalidStateForRequest)}, } } @@ -241,7 +267,7 @@ impl Consensus where T: Debug + Clone { // else fall through }, Consensus::Completed(_) => return Ok(false), - _ => return Err(Error::InvalidStateForRequest), + _ => {println!("=== j"); return Err(Error::InvalidStateForRequest) }, } *self = Consensus::Unreachable; @@ -261,8 +287,7 @@ impl Consensus where T: Debug + Clone { // else fall through }, - Consensus::Completed(_) => return Ok(()), - Consensus::Unreachable => return Err(Error::ConsensusUnreachable), + Consensus::Completed(_) | Consensus::Unreachable => return Ok(()), } *self = Consensus::Unreachable; @@ -271,9 +296,15 @@ impl Consensus where T: Debug + Clone { } impl ConsensusCore { + /// Return rejected nodes list. + pub fn rejected_nodes(&self) -> &BTreeSet { + &self.rejected_nodes + } + /// When node has accepted join offer. pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { if !self.requested_nodes.remove(node) { +println!("=== k"); return Err(Error::InvalidStateForRequest); } @@ -284,6 +315,7 @@ impl ConsensusCore { /// When node has rejected join offer. pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { if !self.requested_nodes.remove(node) { +println!("=== l"); return Err(Error::InvalidStateForRequest); } @@ -314,6 +346,7 @@ impl ActiveConsensus where T: Debug + Clone { /// Select nodes to make job. pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { if !self.selected_nodes.is_empty() { +println!("=== m"); return Err(Error::InvalidStateForRequest); } @@ -324,6 +357,7 @@ impl ActiveConsensus where T: Debug + Clone { /// Get nodes, selected nodes to make their job. pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { if self.selected_nodes.is_empty() { +println!("=== n"); return Err(Error::InvalidStateForRequest); } @@ -349,6 +383,7 @@ impl ActiveConsensus where T: Debug + Clone { /// When job response is received from the node. pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { if !self.active_requests.remove(node) { +println!("=== o"); return Err(Error::InvalidStateForRequest); } @@ -356,6 +391,11 @@ impl ActiveConsensus where T: Debug + Clone { Ok(()) } + /// Return job requests. + pub fn job_requests(&self) -> Result<&BTreeSet, Error> { + Ok(&self.active_requests) + } + /// Return job responses. pub fn job_responses(&self) -> Result<&BTreeMap, Error> { Ok(&self.responses) diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index bdcfe8e6f84..37a4a46ae66 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -199,7 +199,12 @@ impl ConsensusSession where C: ConsensusChecker { self.data.result = Some(Ok(())); self.data.state = SessionState::Finished; Ok(SessionAction::CheckStatus) - } + }, + Ok(_) if consensus.is_unreachable() => { + self.data.result = Some(Err(Error::ConsensusUnreachable)); + self.data.state = SessionState::Failed; + Ok(SessionAction::CheckStatus) + }, Ok(_) => Ok(SessionAction::CheckStatus), Err(err) => { self.data.result = Some(Err(err.clone())); diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 28eaf0910d4..6cd29cc2769 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -16,6 +16,7 @@ use std::cmp::{Ord, PartialOrd, Ordering}; use std::collections::{BTreeSet, BTreeMap}; +use std::ops::DerefMut; use std::sync::Arc; use parking_lot::{Mutex, Condvar}; use ethcrypto::ecies::encrypt; @@ -24,9 +25,12 @@ use ethkey::{self, Secret, Public, Signature}; use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, EncryptedDocumentKeyShadow}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::ClusterSession; +use key_server_cluster::consensus::Consensus; +use key_server_cluster::consensus_session::{ConsensusSession, AclConsensusChecker, SessionParams as ConsensusSessionParams, + SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; use key_server_cluster::math; -use key_server_cluster::message::{Message, DecryptionMessage, InitializeDecryptionSession, ConfirmDecryptionInitialization, - RequestPartialDecryption, PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted}; +use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, + PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage}; /// Decryption session API. pub trait Session: Send + Sync + 'static { @@ -86,7 +90,7 @@ pub struct SessionParams { pub cluster: Arc, } -#[derive(Debug)] +#[derive(Debug, Clone)] /// Partial decryption result. struct PartialDecryptionResult { /// Shadow point. @@ -95,7 +99,6 @@ struct PartialDecryptionResult { pub decrypt_shadow: Option>, } -#[derive(Debug)] /// Mutable data of encryption (distributed key generation) session. struct SessionData { /// Current state of the session. @@ -108,20 +111,12 @@ struct SessionData { requestor: Option, /// Is shadow decryption requested? is_shadow_decryption: Option, + /// Decryption consensus group. + consensus: Option>, - // === Values, filled during session initialization === - /// Nodes, which have been requested for decryption initialization. - requested_nodes: BTreeSet, - /// Nodes, which have responded with reject to initialization request. - rejected_nodes: BTreeSet, - /// Nodes, which have responded with confirm to initialization request. - confirmed_nodes: BTreeSet, - - // === Values, filled during partial decryption === - /// Nodes, which have been asked for partial decryption. - shadow_requests: BTreeSet, - /// Shadow points, received from nodes as a response to partial decryption request. - shadow_points: BTreeMap, + // === Values, filled when consensus is establishing === + /// Consensus session. + consensus_session: Option>, /// === Values, filled during final decryption === /// Decrypted secret @@ -131,14 +126,21 @@ struct SessionData { #[derive(Debug, Clone, PartialEq)] /// Decryption session data. pub enum SessionState { + // === Initialization states === /// Every node starts in this state. WaitingForInitialization, - /// Master node waits for other nodes to confirm decryption. - WaitingForInitializationConfirm, + /// Establishing consensus. + EstablishingConsensus, + /// Consensus established + EstablishedConsensus, + + // === Decryption states === /// Waiting for partial decrypion request. WaitingForPartialDecryptionRequest, /// Waiting for partial decryption responses. WaitingForPartialDecryption, + + // === Final states of the session === /// Decryption session is finished for this node. Finished, /// Decryption session is failed for this node. @@ -163,11 +165,8 @@ impl SessionImpl { master: None, requestor: None, is_shadow_decryption: None, - requested_nodes: BTreeSet::new(), - rejected_nodes: BTreeSet::new(), - confirmed_nodes: BTreeSet::new(), - shadow_requests: BTreeSet::new(), - shadow_points: BTreeMap::new(), + consensus: None, + consensus_session: None, decrypted_secret: None, }) }) @@ -201,6 +200,7 @@ impl SessionImpl { // check state if data.state != SessionState::WaitingForInitialization { +println!("=== 0"); return Err(Error::InvalidStateForRequest); } @@ -209,120 +209,90 @@ impl SessionImpl { // update state data.master = Some(self.node().clone()); - data.state = SessionState::WaitingForInitializationConfirm; + data.state = SessionState::EstablishingConsensus; data.requestor = Some(requestor_public.clone()); data.is_shadow_decryption = Some(is_shadow_decryption); - data.requested_nodes.extend(self.encrypted_data.id_numbers.keys().cloned()); - - // ..and finally check access on our's own - let is_requestor_allowed_to_read = self.acl_storage.check(&requestor_public, &self.id).unwrap_or(false); - process_initialization_response(&self.encrypted_data, &mut *data, self.node(), is_requestor_allowed_to_read)?; - - // check if we have enough nodes to decrypt data - match data.state { - // not enough nodes => pass initialization message to all other nodes - SessionState::WaitingForInitializationConfirm => { - for node in self.encrypted_data.id_numbers.keys().filter(|n| *n != self.node()) { - self.cluster.send(node, Message::Decryption(DecryptionMessage::InitializeDecryptionSession(InitializeDecryptionSession { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - requestor_signature: requestor_signature.clone().into(), - is_shadow_decryption: is_shadow_decryption, - })))?; - } - }, - // we can decrypt data on our own - SessionState::WaitingForPartialDecryption => { - data.confirmed_nodes.insert(self.node().clone()); - SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; - SessionImpl::do_decryption(self.access_key.clone(), &self.encrypted_data, &mut *data)?; - self.completed.notify_all(); - }, - // we can not decrypt data - SessionState::Failed => self.completed.notify_all(), - // cannot reach other states - _ => unreachable!("process_initialization_response can change state to WaitingForPartialDecryption or Failed; checked that we are in WaitingForInitializationConfirm state above; qed"), + + // create consensus session + let mut consensus = Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?; + let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { + id: self.id.clone(), + self_node_id: self.self_node_id.clone(), + master_node_id: self.self_node_id.clone(), + consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), + })?; + + // start consensus session + let consensus_action = consensus_session.initialize(requestor_signature, &mut consensus)?; + data.consensus = Some(consensus); + data.consensus_session = Some(consensus_session); + + // process consensus action + SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; + + // if single node is required to sign message, proceed + if data.state == SessionState::EstablishedConsensus { + SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; + SessionImpl::do_decryption(self.access_key.clone(), &self.encrypted_data, &mut *data)?; + self.completed.notify_all(); } Ok(()) } - /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeDecryptionSession) -> Result<(), Error> { + /// When consensus-related message is received. + pub fn on_consensus_message(&self, sender: NodeId, message: &DecryptionConsensusMessage) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); let mut data = self.data.lock(); - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); + // if we are waiting for initialization + if data.state == SessionState::WaitingForInitialization { + data.master = Some(sender.clone()); + data.state = SessionState::EstablishingConsensus; + data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); + data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { + id: self.id.clone(), + self_node_id: self.self_node_id.clone(), + master_node_id: sender.clone(), + consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), + })?); } - // recover requestor signature - let requestor_public = ethkey::recover(&message.requestor_signature, &self.id)?; - - // check access - let is_requestor_allowed_to_read = self.acl_storage.check(&requestor_public, &self.id).unwrap_or(false); - data.state = if is_requestor_allowed_to_read { SessionState::WaitingForPartialDecryptionRequest } - else { SessionState::Failed }; - data.requestor = Some(requestor_public); - data.is_shadow_decryption = Some(message.is_shadow_decryption); - - // respond to master node - data.master = Some(sender.clone()); - self.cluster.send(&sender, Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(ConfirmDecryptionInitialization { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - is_confirmed: is_requestor_allowed_to_read, - }))) - } - - /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmDecryptionInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - // check state - if data.state == SessionState::WaitingForPartialDecryption { - // if there were enough confirmations/rejections before this message - // we have already moved to the next state - if !data.requested_nodes.remove(&sender) { - return Err(Error::InvalidMessage); + if data.state != SessionState::EstablishingConsensus { + // consensus is already established => mark node as confirmed (for restart case) and ignore + return data.consensus.as_mut().map(|consensus| consensus.accept_offer(&sender)).unwrap_or(Ok(())); + } + + // process message + let consensus_action = { + let mut data = data.deref_mut(); + let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest).map_err(|e| { println!("=== 1");e })?; + match message.message { + ConsensusMessage::InitializeConsensusSession(ref message) => { + let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; + data.requestor = Some(requestor.clone()); + consensus_session.on_initialize_session(sender, &requestor)? + }, + ConsensusMessage::ConfirmConsensusInitialization(ref message) => { + let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest).map_err(|e| { println!("=== 2"); e })?; + consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? + }, } + }; + SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; - data.confirmed_nodes.insert(sender); - return Ok(()); - } - if data.state != SessionState::WaitingForInitializationConfirm { + // if consensus is established and we are on master node => ask for partial decryption +println!("====================== before state check: {:?}", data.state); + if data.state != SessionState::EstablishedConsensus { return Ok(()); } - - // update state - process_initialization_response(&self.encrypted_data, &mut *data, &sender, message.is_confirmed)?; - - // check if we have enough nodes to decrypt data - match data.state { - // we do not yet have enough nodes for decryption - SessionState::WaitingForInitializationConfirm => Ok(()), - // we have enough nodes for decryption - SessionState::WaitingForPartialDecryption => - SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data), - // we can not have enough nodes for decryption - SessionState::Failed => { - self.completed.notify_all(); - Ok(()) - }, - // cannot reach other states - _ => unreachable!("process_initialization_response can change state to WaitingForPartialDecryption or Failed; checked that we are in WaitingForInitializationConfirm state above; qed"), - } +println!("====================== before start_waiting_for_partial_decryption"); + SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } - /// When partial decryption is requested. pub fn on_partial_decryption_requested(&self, sender: NodeId, message: &RequestPartialDecryption) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); @@ -333,22 +303,26 @@ impl SessionImpl { return Err(Error::InvalidMessage); } - let data = self.data.lock(); + let mut data = self.data.lock(); // check state if data.master != Some(sender) { return Err(Error::InvalidMessage); } + if data.state == SessionState::EstablishingConsensus { + // for slave nodes consensus is established when partial decryption request is received + data.state = SessionState::WaitingForPartialDecryptionRequest; + } if data.state != SessionState::WaitingForPartialDecryptionRequest { +println!("=== 3: {:?}", data.state); return Err(Error::InvalidStateForRequest); } // calculate shadow point let decryption_result = { let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryptionRequest follows initialization; qed"); - let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; WaitingForPartialDecryptionRequest follows initialization; qed"); let nodes = message.nodes.iter().cloned().map(Into::into).collect(); - do_partial_decryption(self.node(), &requestor, is_shadow_decryption, &nodes, &self.access_key, &self.encrypted_data)? + do_partial_decryption(self.node(), &requestor, message.is_shadow_decryption, &nodes, &self.access_key, &self.encrypted_data)? }; self.cluster.send(&sender, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { session: self.id.clone().into(), @@ -373,20 +347,22 @@ impl SessionImpl { // check state if data.state != SessionState::WaitingForPartialDecryption { +println!("=== 4"); return Err(Error::InvalidStateForRequest); } - if !data.shadow_requests.remove(&sender) { - return Err(Error::InvalidStateForRequest); - } - data.shadow_points.insert(sender, PartialDecryptionResult { - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - }); - - // check if we have enough shadow points to decrypt the secret - if data.shadow_points.len() != self.encrypted_data.threshold + 1 { - return Ok(()); + // remember partial signature + { + let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest).map_err(|e| { println!("=== 5"); e })?; + consensus.job_response_received(&sender, PartialDecryptionResult { + shadow_point: message.shadow_point.clone().into(), + decrypt_shadow: message.decrypt_shadow.clone(), + })?; + + // check if we have enough shadow points to decrypt the secret + if !consensus.is_completed() { + return Ok(()); + } } // notify all other nodes about session completion @@ -410,13 +386,14 @@ impl SessionImpl { let mut data = self.data.lock(); - // check state - if data.state != SessionState::WaitingForPartialDecryptionRequest { - return Err(Error::InvalidStateForRequest); - } if data.master != Some(sender) { return Err(Error::InvalidMessage); } + // it is up to master node to decide when to complete session + // => we could only fail if already failed + if data.state == SessionState::Failed { + return Err(Error::InvalidStateForRequest); + } // update state data.state = SessionState::Finished; @@ -437,28 +414,76 @@ impl SessionImpl { Ok(()) } - fn start_waiting_for_partial_decryption(self_node_id: NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - let confirmed_nodes: BTreeSet<_> = data.confirmed_nodes.clone(); - let confirmed_nodes: BTreeSet<_> = confirmed_nodes.difference(&data.rejected_nodes).cloned().collect(); + /// Process nested consensus session action. + fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { + match action { + ConsensusSessionAction::BroadcastMessage(message) => { + cluster.broadcast(Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + }))) + }, + ConsensusSessionAction::SendMessage(to, message) => { + cluster.send(&to, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { + session: id.clone().into(), + sub_session: access_key.clone().into(), + message: message, + }))) + }, + ConsensusSessionAction::CheckStatus => { + match data.consensus_session.as_ref() + .expect("we are processing consensus session action; action is a result of processing message by session; qed") + .state() { + ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, + ConsensusSessionState::Failed => { + data.state = SessionState::Failed; + data.decrypted_secret = Some(Err(Error::ConsensusUnreachable)); + completed.notify_all(); + }, + _ => (), + } + Ok(()) + }, + } + } - data.shadow_requests.clear(); - data.shadow_points.clear(); - for node in confirmed_nodes.iter().filter(|n| n != &&self_node_id) { - data.shadow_requests.insert(node.clone()); + fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + if data.master.as_ref() != Some(self_node_id) { + // if we are on the slave node, wait for partial decryption requests + data.state = SessionState::WaitingForPartialDecryptionRequest; + return Ok(()); + } + + // update state + data.state = SessionState::WaitingForPartialDecryption; + + // send jobs to all selected nodes + let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial decryption phase follows initialization; qed"); + consensus.activate()?; + let mut confirmed_nodes = consensus.select_nodes()?.clone(); + + // send requests + for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { + consensus.job_request_sent(node)?; cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { session: session_id.clone().into(), sub_session: access_key.clone().into(), + is_shadow_decryption: data.is_shadow_decryption.expect("TODO"), nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), })))?; } - if data.confirmed_nodes.remove(&self_node_id) { + // confirmation from this node, if this node is in consensus group + if confirmed_nodes.remove(self_node_id) { let decryption_result = { let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); - do_partial_decryption(&self_node_id, &requestor, is_shadow_decryption, &data.confirmed_nodes, &access_key, &encrypted_data)? + do_partial_decryption(self_node_id, &requestor, is_shadow_decryption, &confirmed_nodes, &access_key, &encrypted_data)? }; - data.shadow_points.insert(self_node_id.clone(), decryption_result); + + consensus.job_request_sent(self_node_id)?; + consensus.job_response_received(self_node_id, decryption_result)?; } Ok(()) @@ -466,7 +491,8 @@ impl SessionImpl { fn do_decryption(access_key: Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // decrypt the secret using shadow points - let joint_shadow_point = math::compute_joint_shadow_point(data.shadow_points.values().map(|s| &s.shadow_point))?; + let job_responses = data.consensus.as_ref().expect("TODO").job_responses()?; + let joint_shadow_point = math::compute_joint_shadow_point(job_responses.values().map(|s| &s.shadow_point))?; let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, &access_key, encrypted_point, &joint_shadow_point)?; @@ -474,7 +500,7 @@ impl SessionImpl { let (common_point, decrypt_shadows) = if is_shadow_decryption { ( Some(math::make_common_shadow_point(encrypted_data.threshold, common_point.clone())?), - Some(data.shadow_points.values() + Some(job_responses.values() .map(|s| s.decrypt_shadow.as_ref().expect("decrypt_shadow is filled during partial decryption; decryption follows partial decryption; qed").clone()) .collect()) ) @@ -508,49 +534,18 @@ impl ClusterSession for SessionImpl { let is_other_master = data.master.as_ref() == Some(node); // if this is master node, we might have to restart if is_self_master { - match data.state { - SessionState::WaitingForInitializationConfirm => { - // we will never receive confirmation from this node => treat as reject - if data.requested_nodes.remove(node) || data.confirmed_nodes.remove(node) { - data.rejected_nodes.insert(node.clone()); - } - // check if we still have enough nodes for decryption - if self.encrypted_data.id_numbers.len() - data.rejected_nodes.len() >= self.encrypted_data.threshold + 1 { - return; - } - }, - SessionState::WaitingForPartialDecryption => { - if data.rejected_nodes.contains(node) { - // already rejected => does not affect session - return; - } - if data.requested_nodes.remove(node) { - // we have tried to initialize this node, but it has failed - // => no restart required, just mark as rejected - data.rejected_nodes.insert(node.clone()); - return; - } - if data.confirmed_nodes.contains(node) { - if data.shadow_points.contains_key(node) { - // we have already received partial decryption from this node - // => just ignore this connection drop - return; - } - - // the worst case: we have sent partial decryption request to other nodes - // => we have to restart the session - data.confirmed_nodes.remove(node); - data.rejected_nodes.insert(node.clone()); - // check if we still have enough nodes for decryption - if self.encrypted_data.id_numbers.len() - data.rejected_nodes.len() >= self.encrypted_data.threshold + 1 { - // we are going to stop session anyway => ignore error - let _ = SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data); - return; - } - // not enough nodes - } + let is_restart_required = match data.consensus.as_mut() { + None => false, + Some(consensus) => match consensus.node_timeouted(node) { + Ok(false) => return, + Ok(true) => true, + Err(_) => false, //fall through }, - _ => (), // all other states lead to failures + }; + if is_restart_required { + if SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + return; + } } } else if !is_other_master { // disconnected from non-master node on non-master node @@ -559,7 +554,6 @@ impl ClusterSession for SessionImpl { } // else: disconnecting from master node means failure - // no more nodes left for decryption => fail warn!("{}: decryption session failed because {} connection has timeouted", self.node(), node); data.state = SessionState::Failed; @@ -573,32 +567,20 @@ impl ClusterSession for SessionImpl { let is_self_master = data.master.as_ref() == Some(self.node()); // if this is master node, we might have to restart if is_self_master { - match data.state { - SessionState::WaitingForInitializationConfirm => - // we have sent initialization requests to all nodes, but haven't received confirmation - // => nodes will never respond => fail - (), - SessionState::WaitingForPartialDecryption => { - // we have requested partial decryption, but some nodes have failed to respond - // => mark these nodes as rejected && restart - for timeouted_node in data.shadow_requests.iter().cloned().collect::>() { - data.confirmed_nodes.remove(&timeouted_node); - data.rejected_nodes.insert(timeouted_node); - } - - // check if we still have enough nodes for decryption - if self.encrypted_data.id_numbers.len() - data.rejected_nodes.len() >= self.encrypted_data.threshold + 1 { - // we are going to stop session anyway => ignore error - let _ = SessionImpl::start_waiting_for_partial_decryption(self.node().clone(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data); - return; - } + let is_restart_required = match data.consensus.as_mut() { + None => false, + Some(consensus) => match consensus.session_timeouted() { + Ok(_) => true, + Err(_) => false, }, - // no nodes has responded to our requests => session is failed - _ => return, + }; + if is_restart_required { + if SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + return; + } } } - // no more nodes left for decryption => fail warn!("{}: decryption session failed with timeout", self.node()); data.state = SessionState::Failed; @@ -660,34 +642,6 @@ fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare } -fn process_initialization_response(encrypted_data: &DocumentKeyShare, data: &mut SessionData, node: &NodeId, check_result: bool) -> Result<(), Error> { - if !data.requested_nodes.remove(node) { - return Err(Error::InvalidMessage); - } - - match check_result { - true => { - data.confirmed_nodes.insert(node.clone()); - - // check if we have enough nodes to do a decryption? - if data.confirmed_nodes.len() == encrypted_data.threshold + 1 { - data.state = SessionState::WaitingForPartialDecryption; - } - }, - false => { - data.rejected_nodes.insert(node.clone()); - - // check if we still can receive enough confirmations to do a decryption? - if encrypted_data.id_numbers.len() - data.rejected_nodes.len() < encrypted_data.threshold + 1 { - data.decrypted_secret = Some(Err(Error::AccessDenied)); - data.state = SessionState::Failed; - } - }, - } - - Ok(()) -} - fn do_partial_decryption(node: &NodeId, requestor_public: &Public, is_shadow_decryption: bool, participants: &BTreeSet, access_key: &Secret, encrypted_data: &DocumentKeyShare) -> Result { let node_id_number = &encrypted_data.id_numbers[node]; let node_secret_share = &encrypted_data.secret_share; @@ -787,8 +741,7 @@ mod tests { } match message { - Message::Decryption(DecryptionMessage::InitializeDecryptionSession(message)) => session.on_initialize_session(from, &message).unwrap(), - Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(message)) => session.on_confirm_initialization(from, &message).unwrap(), + Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(message)) => session.on_consensus_message(from, &message).unwrap(), Message::Decryption(DecryptionMessage::RequestPartialDecryption(message)) => session.on_partial_decryption_requested(from, &message).unwrap(), Message::Decryption(DecryptionMessage::PartialDecryption(message)) => session.on_partial_decryption(from, &message).unwrap(), Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(message)) => session.on_session_completed(from, &message).unwrap(), @@ -885,26 +838,29 @@ mod tests { fn fails_to_accept_initialization_when_already_initialized() { let (_, _, sessions) = prepare_decryption_sessions(); assert_eq!(sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(), ()); - assert_eq!(sessions[0].on_initialize_session(sessions[1].node().clone(), &message::InitializeDecryptionSession { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), - is_shadow_decryption: false, - }).unwrap_err(), Error::InvalidStateForRequest); + assert_eq!(sessions[0].on_consensus_message(sessions[1].node().clone(), &message::DecryptionConsensusMessage { + session: SessionId::default().into(), + sub_session: sessions[0].access_key().clone().into(), + message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { + requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), + }), + }).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn fails_to_partial_decrypt_if_requested_by_slave() { let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_initialize_session(sessions[0].node().clone(), &message::InitializeDecryptionSession { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), - is_shadow_decryption: false, + assert_eq!(sessions[1].on_consensus_message(sessions[0].node().clone(), &message::DecryptionConsensusMessage { + session: SessionId::default().into(), + sub_session: sessions[0].access_key().clone().into(), + message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { + requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), + }), }).unwrap(), ()); assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node().clone(), &message::RequestPartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), + is_shadow_decryption: false, nodes: sessions.iter().map(|s| s.node().clone().into()).take(4).collect(), }).unwrap_err(), Error::InvalidMessage); } @@ -912,15 +868,17 @@ mod tests { #[test] fn fails_to_partial_decrypt_if_wrong_number_of_nodes_participating() { let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_initialize_session(sessions[0].node().clone(), &message::InitializeDecryptionSession { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), - is_shadow_decryption: false, + assert_eq!(sessions[1].on_consensus_message(sessions[0].node().clone(), &message::DecryptionConsensusMessage { + session: SessionId::default().into(), + sub_session: sessions[0].access_key().clone().into(), + message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { + requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), + }), }).unwrap(), ()); assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node().clone(), &message::RequestPartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), + is_shadow_decryption: false, nodes: sessions.iter().map(|s| s.node().clone().into()).take(2).collect(), }).unwrap_err(), Error::InvalidMessage); } @@ -971,12 +929,12 @@ mod tests { // 1 node disconnects => we still can recover secret sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().rejected_nodes.contains(sessions[1].node())); - assert!(sessions[0].data.lock().state == SessionState::WaitingForInitializationConfirm); + assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().unwrap().rejected_nodes().contains(sessions[1].node())); + assert!(sessions[0].data.lock().state == SessionState::EstablishingConsensus); // 2 node are disconnected => we can not recover secret sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].data.lock().rejected_nodes.contains(sessions[2].node())); + assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().unwrap().rejected_nodes().contains(sessions[2].node())); assert!(sessions[0].data.lock().state == SessionState::Failed); } @@ -1017,11 +975,11 @@ mod tests { sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption - && sessions[0].data.lock().shadow_points.len() == 2); + && sessions[0].data.lock().consensus.as_ref().unwrap().job_responses().unwrap().len() == 2); // disconnects from the node which has already sent us its own shadow point let disconnected = sessions[0].data.lock(). - shadow_points.keys() + consensus.as_ref().unwrap().job_responses().unwrap().keys() .filter(|n| *n != sessions[0].node()) .cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); @@ -1036,11 +994,11 @@ mod tests { do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption); // disconnects from the node which has already confirmed its participation - let disconnected = sessions[0].data.lock().shadow_requests.iter().cloned().nth(0).unwrap(); + let disconnected = sessions[0].data.lock().consensus.as_ref().unwrap().job_requests().unwrap().iter().cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); assert!(sessions[0].data.lock().state == SessionState::WaitingForPartialDecryption); - assert!(sessions[0].data.lock().rejected_nodes.contains(&disconnected)); - assert!(!sessions[0].data.lock().shadow_requests.contains(&disconnected)); + assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().unwrap().rejected_nodes().contains(&disconnected)); + assert!(!sessions[0].data.lock().consensus.as_ref().unwrap().job_requests().unwrap().contains(&disconnected)); } #[test] diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index 4d8948f48f7..b225d9d1893 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -80,12 +80,11 @@ pub fn serialize_message(message: Message) -> Result { Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(payload)) => (101, serde_json::to_vec(&payload)), Message::Encryption(EncryptionMessage::EncryptionSessionError(payload)) => (102, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::InitializeDecryptionSession(payload)) => (150, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(payload)) => (151, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::RequestPartialDecryption(payload)) => (152, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (153, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (154, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (155, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(payload)) => (150, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::RequestPartialDecryption(payload)) => (151, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (152, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (153, serde_json::to_vec(&payload)), + Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (154, serde_json::to_vec(&payload)), Message::Consensus(ConsensusMessage::InitializeConsensusSession(payload)) => (200, serde_json::to_vec(&payload)), Message::Consensus(ConsensusMessage::ConfirmConsensusInitialization(payload)) => (201, serde_json::to_vec(&payload)), @@ -121,12 +120,11 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 102 => Message::Encryption(EncryptionMessage::EncryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 150 => Message::Decryption(DecryptionMessage::InitializeDecryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 151 => Message::Decryption(DecryptionMessage::ConfirmDecryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 152 => Message::Decryption(DecryptionMessage::RequestPartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 153 => Message::Decryption(DecryptionMessage::PartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 154 => Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 155 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 150 => Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 151 => Message::Decryption(DecryptionMessage::RequestPartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 152 => Message::Decryption(DecryptionMessage::PartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 153 => Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 154 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 200 => Message::Consensus(ConsensusMessage::InitializeConsensusSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 201 => Message::Consensus(ConsensusMessage::ConfirmConsensusInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 5d436ca5078..d0378cac58e 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -95,10 +95,8 @@ pub enum ConsensusMessage { #[derive(Clone, Debug)] /// All possible messages that can be sent during decryption session. pub enum DecryptionMessage { - /// Initialize decryption session. - InitializeDecryptionSession(InitializeDecryptionSession), - /// Confirm/reject decryption session initialization. - ConfirmDecryptionInitialization(ConfirmDecryptionInitialization), + /// Consensus establishing message. + DecryptionConsensusMessage(DecryptionConsensusMessage), /// Request partial decryption from node. RequestPartialDecryption(RequestPartialDecryption), /// Partial decryption is completed. @@ -338,28 +336,14 @@ pub struct SigningSessionCompleted { } #[derive(Clone, Debug, Serialize, Deserialize)] -/// Node is requested to decrypt data, encrypted in given session. -pub struct InitializeDecryptionSession { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Requestor signature. - pub requestor_signature: SerializableSignature, - /// Is shadow decryption requested? When true, decryption result - /// will be visible to the owner of requestor public key only. - pub is_shadow_decryption: bool, -} - -#[derive(Clone, Debug, Serialize, Deserialize)] -/// Node is responding to decryption request. -pub struct ConfirmDecryptionInitialization { - /// Encryption session Id. +/// Consensus-related decryption message. +pub struct DecryptionConsensusMessage { + /// Generation session Id. pub session: MessageSessionId, - /// Decryption session Id. + /// Signing session Id. pub sub_session: SerializableSecret, - /// Is node confirmed to make a decryption?. - pub is_confirmed: bool, + /// Consensus message. + pub message: ConsensusMessage, } #[derive(Clone, Debug, Serialize, Deserialize)] @@ -369,6 +353,9 @@ pub struct RequestPartialDecryption { pub session: MessageSessionId, /// Decryption session Id. pub sub_session: SerializableSecret, + /// Is shadow decryption requested? When true, decryption result + /// will be visible to the owner of requestor public key only. + pub is_shadow_decryption: bool, /// Nodes that are agreed to do a decryption. pub nodes: BTreeSet, } @@ -433,8 +420,7 @@ impl EncryptionMessage { impl DecryptionMessage { pub fn session_id(&self) -> &SessionId { match *self { - DecryptionMessage::InitializeDecryptionSession(ref msg) => &msg.session, - DecryptionMessage::ConfirmDecryptionInitialization(ref msg) => &msg.session, + DecryptionMessage::DecryptionConsensusMessage(ref msg) => &msg.session, DecryptionMessage::RequestPartialDecryption(ref msg) => &msg.session, DecryptionMessage::PartialDecryption(ref msg) => &msg.session, DecryptionMessage::DecryptionSessionError(ref msg) => &msg.session, @@ -444,8 +430,7 @@ impl DecryptionMessage { pub fn sub_session_id(&self) -> &Secret { match *self { - DecryptionMessage::InitializeDecryptionSession(ref msg) => &msg.sub_session, - DecryptionMessage::ConfirmDecryptionInitialization(ref msg) => &msg.sub_session, + DecryptionMessage::DecryptionConsensusMessage(ref msg) => &msg.sub_session, DecryptionMessage::RequestPartialDecryption(ref msg) => &msg.sub_session, DecryptionMessage::PartialDecryption(ref msg) => &msg.sub_session, DecryptionMessage::DecryptionSessionError(ref msg) => &msg.sub_session, @@ -524,8 +509,7 @@ impl fmt::Display for ConsensusMessage { impl fmt::Display for DecryptionMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - DecryptionMessage::InitializeDecryptionSession(_) => write!(f, "InitializeDecryptionSession"), - DecryptionMessage::ConfirmDecryptionInitialization(_) => write!(f, "ConfirmDecryptionInitialization"), + DecryptionMessage::DecryptionConsensusMessage(_) => write!(f, "DecryptionConsensusMessage"), DecryptionMessage::RequestPartialDecryption(_) => write!(f, "RequestPartialDecryption"), DecryptionMessage::PartialDecryption(_) => write!(f, "PartialDecryption"), DecryptionMessage::DecryptionSessionError(_) => write!(f, "DecryptionSessionError"), diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 8ae81cce671..43902d72321 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -142,6 +142,8 @@ pub enum SessionState { // === Signature generation states === /// Waiting for partial signatures. WaitingForPartialSignature, + /// Waiting for partial signature request. + WaitingForPartialSignatureRequest, // === Final states of the session === /// Signing is completed. @@ -383,7 +385,7 @@ impl SessionImpl { if data.master != Some(sender) { return Err(Error::InvalidMessage); } - if data.state != SessionState::SessionKeyGenerated { + if data.state != SessionState::WaitingForPartialSignatureRequest { return Err(Error::InvalidStateForRequest); } @@ -588,9 +590,9 @@ impl SessionImpl { /// Start waiting for partial signatures/partial signatures requests. fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - data.state = SessionState::SessionKeyGenerated; if data.master.as_ref() != Some(self_node_id) { // if we are on the slave node, wait for partial signature requests + data.state = SessionState::WaitingForPartialSignatureRequest; return Ok(()); } @@ -719,8 +721,7 @@ impl ClusterSession for SessionImpl { } } - // no more nodes left for decryption => fail - warn!("{}: decryption session failed with timeout", self.node()); + warn!("{}: signing session failed with timeout", self.node()); data.state = SessionState::Failed; data.signed_message = Some(Err(Error::NodeDisconnected)); From 7efdad0ee1f42bfb39e0165264ac0dd4828c3197 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 23 May 2017 20:34:03 +0300 Subject: [PATCH 18/45] fixed last test --- .../src/key_server_cluster/consensus.rs | 2 +- .../key_server_cluster/decryption_session.rs | 35 ++++++++-------- .../src/key_server_cluster/signing_session.rs | 41 ++++++++++--------- 3 files changed, 40 insertions(+), 38 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 61a3ce5fb41..c916c58f89c 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -165,7 +165,7 @@ impl Consensus where T: Debug + Clone { let active_consensus = match *self { Consensus::Established(ref established_consensus) => ActiveConsensus::new(established_consensus.clone()), Consensus::Active(ref active_consensus) => ActiveConsensus::new(active_consensus.core.clone()), - _ => { println!("=== c"); return Err(Error::InvalidStateForRequest) }, + _ => { panic!("=== c"); println!("=== c: {:?}", self); return Err(Error::InvalidStateForRequest) }, }; *self = Consensus::Active(active_consensus); diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 6cd29cc2769..f96898233fb 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -422,30 +422,31 @@ println!("=== 4"); session: id.clone().into(), sub_session: access_key.clone().into(), message: message, - }))) + })))? }, ConsensusSessionAction::SendMessage(to, message) => { cluster.send(&to, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { session: id.clone().into(), sub_session: access_key.clone().into(), message: message, - }))) + })))? }, - ConsensusSessionAction::CheckStatus => { - match data.consensus_session.as_ref() - .expect("we are processing consensus session action; action is a result of processing message by session; qed") - .state() { - ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, - ConsensusSessionState::Failed => { - data.state = SessionState::Failed; - data.decrypted_secret = Some(Err(Error::ConsensusUnreachable)); - completed.notify_all(); - }, - _ => (), - } - Ok(()) + ConsensusSessionAction::CheckStatus => (), + } + + match data.consensus_session.as_ref() + .expect("we are processing consensus session action; action is a result of processing message by session; qed") + .state() { + ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, + ConsensusSessionState::Failed => { + data.state = SessionState::Failed; + data.decrypted_secret = Some(Err(Error::ConsensusUnreachable)); + completed.notify_all(); }, + _ => (), } + + Ok(()) } fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { @@ -844,7 +845,7 @@ mod tests { message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), }), - }).unwrap_err(), Error::InvalidStateForRequest); + }).unwrap_err(), Error::InvalidMessage); } #[test] @@ -934,7 +935,7 @@ mod tests { // 2 node are disconnected => we can not recover secret sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().unwrap().rejected_nodes().contains(sessions[2].node())); + assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().is_none()); assert!(sessions[0].data.lock().state == SessionState::Failed); } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 43902d72321..e9d9f89a944 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -289,8 +289,8 @@ impl SessionImpl { }; SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; - // if consensus is established, start generating session key - if data.state != SessionState::EstablishedConsensus { + // if consensus is established, start generating session key on master + if data.state != SessionState::EstablishedConsensus || data.master.as_ref() != Some(&self.self_node_id) { return Ok(()); } @@ -491,34 +491,38 @@ impl SessionImpl { session: id.clone().into(), sub_session: access_key.clone().into(), message: message, - }))) + })))? }, ConsensusSessionAction::SendMessage(to, message) => { cluster.send(&to, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { session: id.clone().into(), sub_session: access_key.clone().into(), message: message, - }))) + })))? }, - ConsensusSessionAction::CheckStatus => { - match data.consensus_session.as_ref() - .expect("we are processing consensus session action; action is a result of processing message by session; qed") - .state() { - ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, - ConsensusSessionState::Failed => { - data.state = SessionState::Failed; - data.signed_message = Some(Err(Error::ConsensusUnreachable)); - completed.notify_all(); - }, - _ => (), - } - Ok(()) + ConsensusSessionAction::CheckStatus => (), + } + + match data.consensus_session.as_ref() + .expect("we are processing consensus session action; action is a result of processing message by session; qed") + .state() { + ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, + ConsensusSessionState::Failed => { + data.state = SessionState::Failed; + data.signed_message = Some(Err(Error::ConsensusUnreachable)); + completed.notify_all(); }, + _ => (), } + + Ok(()) } /// Start generating one-time session key. fn start_generating_session_key(self_node_id: NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { + // update state + data.state = SessionState::SessionKeyGeneration; + // select nodes to make signature let mut consensus = data.consensus.as_mut().expect("consensus is filled during initialization phase; key generation phase follows initialization; qed"); consensus.activate()?; @@ -539,9 +543,6 @@ impl SessionImpl { data.generation_cluster = Some(generation_cluster); data.generation_session = Some(generation_session); - // update state - data.state = SessionState::SessionKeyGeneration; - Ok(()) } From e503113823e4acbae14ec69fe807f45570f7c390 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 11:07:28 +0300 Subject: [PATCH 19/45] signing session in http listener --- secret_store/src/http_listener.rs | 186 +++++++++++++----- secret_store/src/key_server.rs | 19 +- .../src/key_server_cluster/cluster.rs | 82 ++++---- .../key_server_cluster/cluster_sessions.rs | 46 +++++ .../src/key_server_cluster/signing_session.rs | 56 ++---- secret_store/src/traits.rs | 4 +- secret_store/src/types/all.rs | 6 +- 7 files changed, 258 insertions(+), 141 deletions(-) diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index dc96ebe2dc7..66e1f7beea5 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -21,15 +21,23 @@ use hyper::method::Method as HttpMethod; use hyper::status::StatusCode as HttpStatusCode; use hyper::server::{Server as HttpServer, Request as HttpRequest, Response as HttpResponse, Handler as HttpHandler, Listening as HttpListening}; +use serde::Serialize; use serde_json; use url::percent_encoding::percent_decode; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; -use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes}; -use types::all::{Error, Public, MessageData, NodeAddress, RequestSignature, ServerKeyId, +use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; +use types::all::{Error, Public, MessageHash, MessageSignature, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow}; -/// Key server http-requests listener +/// Key server http-requests listener. Available requests: +/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold} +/// To store pregenerated encrypted document key: POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} +/// To generate server && document key: POST /{server_key_id}/{signature}/{threshold} +/// 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} + pub struct KeyServerHttpListener { _http_server: HttpListening, handler: Arc>, @@ -40,12 +48,18 @@ pub struct KeyServerHttpListener { enum Request { /// Invalid request Invalid, + /// Generate server key. + GenerateServerKey(ServerKeyId, RequestSignature, usize), + /// Store document key. + StoreDocumentKey(ServerKeyId, RequestSignature, Public, Public), /// Generate encryption key. GenerateDocumentKey(ServerKeyId, RequestSignature, usize), /// Request encryption key of given document for given requestor. GetDocumentKey(ServerKeyId, RequestSignature), /// Request shadow of encryption key of given document for given requestor. GetDocumentKeyShadow(ServerKeyId, RequestSignature), + /// Sign message. + SignMessage(ServerKeyId, RequestSignature, MessageHash), } /// Cloneable http handler @@ -106,8 +120,8 @@ impl DocumentKeyServer for KeyServerHttpListener where T: KeyServer + 'sta } impl MessageSigner for KeyServerHttpListener where T: KeyServer + 'static { - fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageData) -> Result { - unimplemented!() + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + self.handler.key_server.sign_message(key_id, signature, message) } } @@ -130,6 +144,20 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { let req_uri = req.uri.clone(); match &req_uri { &RequestUri::AbsolutePath(ref path) => match parse_request(&req_method, &path) { + Request::GenerateServerKey(document, signature, threshold) => { + return_server_pubic_key(req, res, self.handler.key_server.generate_key(&document, &signature, threshold) + .map_err(|err| { + warn!(target: "secretstore", "GenerateServerKey request {} has failed with: {}", req_uri, err); + err + })); + }, + Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => { + return_empty(req, res, self.handler.key_server.store_document_key(&document, &signature, common_point, encrypted_document_key) + .map_err(|err| { + warn!(target: "secretstore", "StoreDocumentKey request {} has failed with: {}", req_uri, err); + err + })); + }, Request::GenerateDocumentKey(document, signature, threshold) => { return_document_key(req, res, self.handler.key_server.generate_document_key(&document, &signature, threshold) .map_err(|err| { @@ -145,32 +173,19 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { })); }, Request::GetDocumentKeyShadow(document, signature) => { - match self.handler.key_server.restore_document_key_shadow(&document, &signature) + return_document_key_shadow(req, res, self.handler.key_server.restore_document_key_shadow(&document, &signature) .map_err(|err| { warn!(target: "secretstore", "GetDocumentKeyShadow request {} has failed with: {}", req_uri, err); err - }) { - Ok(document_key_shadow) => { - let document_key_shadow = SerializableEncryptedDocumentKeyShadow { - decrypted_secret: document_key_shadow.decrypted_secret.into(), - common_point: document_key_shadow.common_point.expect("always filled when requesting document_key_shadow; qed").into(), - decrypt_shadows: document_key_shadow.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect(), - }; - match serde_json::to_vec(&document_key_shadow) { - Ok(document_key) => { - res.headers_mut().set(header::ContentType::json()); - if let Err(err) = res.send(&document_key) { - // nothing to do, but to log an error - warn!(target: "secretstore", "response to request {} has failed with: {}", req.uri, err); - } - }, - Err(err) => { - warn!(target: "secretstore", "response to request {} has failed with: {}", req.uri, err); - } - } - }, - Err(err) => return_error(res, err), - } + })); + }, + Request::SignMessage(document, signature, message_hash) => { + return_bytes(req, res, self.handler.key_server.sign_message(&document, &signature, message_hash) + .map(|s| Some(SerializableBytes(s))) + .map_err(|err| { + warn!(target: "secretstore", "SignMessage request {} has failed with: {}", req_uri, err); + err + })); }, Request::Invalid => { warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); @@ -185,17 +200,41 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { } } -fn return_document_key(req: HttpRequest, mut res: HttpResponse, document_key: Result) { - let document_key = document_key. - and_then(|k| serde_json::to_vec(&SerializableBytes(k)).map_err(|e| Error::Serde(e.to_string()))); - match document_key { - Ok(document_key) => { - res.headers_mut().set(header::ContentType::plaintext()); - if let Err(err) = res.send(&document_key) { - // nothing to do, but to log an error +fn return_empty(req: HttpRequest, res: HttpResponse, empty: Result<(), Error>) { + return_bytes::(req, res, empty.map(|_| None)) +} + +fn return_server_pubic_key(req: HttpRequest, res: HttpResponse, server_public: Result) { + return_bytes(req, res, server_public.map(|k| Some(SerializablePublic(k)))) +} + +fn return_document_key(req: HttpRequest, res: HttpResponse, document_key: Result) { + return_bytes(req, res, document_key.map(|k| Some(SerializableBytes(k)))) +} + +fn return_document_key_shadow(req: HttpRequest, res: HttpResponse, document_key_shadow: Result) { + return_bytes(req, res, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow { + decrypted_secret: k.decrypted_secret.into(), + common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(), + decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect(), + }))) +} + +fn return_bytes(req: HttpRequest, mut res: HttpResponse, result: Result, Error>) { + match result { + Ok(Some(result)) => match serde_json::to_vec(&result) { + Ok(result) => { + res.headers_mut().set(header::ContentType::json()); + if let Err(err) = res.send(&result) { + // nothing to do, but to log an error + warn!(target: "secretstore", "response to request {} has failed with: {}", req.uri, err); + } + }, + Err(err) => { warn!(target: "secretstore", "response to request {} has failed with: {}", req.uri, err); } }, + Ok(None) => *res.status_mut() = HttpStatusCode::Ok, Err(err) => return_error(res, err), } } @@ -221,24 +260,39 @@ fn parse_request(method: &HttpMethod, uri_path: &str) -> Request { if path.len() == 0 { return Request::Invalid; } - let (args_prefix, args_offset) = if &path[0] == "shadow" { - ("shadow", 1) - } else { - ("", 0) - }; - if path.len() < 2 + args_offset || path[args_offset].is_empty() || path[args_offset + 1].is_empty() { + 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() { return Request::Invalid; } - let args_len = path.len(); - let document = path[args_offset].parse(); - let signature = path[args_offset + 1].parse(); - let threshold = (if args_len > args_offset + 2 { &path[args_offset + 2] } else { "" }).parse(); - match (args_prefix, args_len, method, document, signature, threshold) { - ("", 3, &HttpMethod::Post, Ok(document), Ok(signature), Ok(threshold)) => Request::GenerateDocumentKey(document, signature, threshold), - ("", 2, &HttpMethod::Get, Ok(document), Ok(signature), _) => Request::GetDocumentKey(document, signature), - ("shadow", 3, &HttpMethod::Get, Ok(document), Ok(signature), _) => Request::GetDocumentKeyShadow(document, signature), + let document = match path[args_offset].parse() { + Ok(document) => document, + _ => return Request::Invalid, + }; + let signature = match path[args_offset + 1].parse() { + Ok(signature) => signature, + _ => return Request::Invalid, + }; + + let threshold = path.get(args_offset + 2).map(|v| v.parse()); + let message_hash = path.get(args_offset + 2).map(|v| v.parse()); + let common_point = path.get(args_offset + 2).map(|v| v.parse()); + let encrypted_key = path.get(args_offset + 3).map(|v| v.parse()); + match (is_shadow_request, args_count, method, threshold, message_hash, common_point, encrypted_key) { + (true, 3, &HttpMethod::Post, Some(Ok(threshold)), _, _, _) => + Request::GenerateServerKey(document, signature, threshold), + (true, 4, &HttpMethod::Post, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) => + Request::StoreDocumentKey(document, signature, common_point, encrypted_key), + (false, 3, &HttpMethod::Post, Some(Ok(threshold)), _, _, _) => + Request::GenerateDocumentKey(document, signature, threshold), + (false, 2, &HttpMethod::Get, _, _, _, _) => + Request::GetDocumentKey(document, signature), + (true, 2, &HttpMethod::Get, _, _, _, _) => + Request::GetDocumentKeyShadow(document, signature), + (false, 3, &HttpMethod::Get, _, Some(Ok(message_hash)), _, _) => + Request::SignMessage(document, signature, message_hash), _ => Request::Invalid, } } @@ -260,19 +314,49 @@ 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"), + 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"), + 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"), + Request::GenerateDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(), + "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), + 2)); + // GET /{server_key_id}/{signature} => get document key assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"), Request::GetDocumentKey("0000000000000000000000000000000000000000000000000000000000000001".into(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); assert_eq!(parse_request(&HttpMethod::Get, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01"), 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"), + 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"), + Request::SignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(), + "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), + "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); } #[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"), Request::Invalid); + assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002"), Request::Invalid); } } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 00421e1864d..914ef043fd5 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -27,7 +27,7 @@ use super::key_storage::KeyStorage; use key_server_cluster::{math, ClusterCore}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, - ClusterConfiguration, MessageData}; + ClusterConfiguration, MessageHash, MessageSignature}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration}; /// Secret store key server implementation @@ -104,7 +104,6 @@ impl DocumentKeyServer for KeyServerImpl { let public = ethkey::recover(signature, key_id) .map_err(|_| Error::BadSignature)?; - // decrypt document key let decryption_session = self.data.lock().cluster.new_decryption_session(key_id.clone(), signature.clone(), false)?; let document_key = decryption_session.wait()?.decrypted_secret; @@ -122,8 +121,16 @@ impl DocumentKeyServer for KeyServerImpl { } impl MessageSigner for KeyServerImpl { - fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageData) -> Result { - unimplemented!() + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + // sign message + let signing_session = self.data.lock().cluster.new_signing_session(key_id.clone(), signature.clone(), message)?; + let message_signature = signing_session.wait()?; + + // compose two message signature components into single + let mut composed_signature: Vec = Vec::with_capacity(64); + composed_signature.extend_from_slice(&**message_signature.0); + composed_signature.extend_from_slice(&**message_signature.1); + Ok(composed_signature.into()) } } @@ -183,7 +190,7 @@ pub mod tests { use acl_storage::tests::DummyAclStorage; use key_storage::tests::DummyKeyStorage; use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageData}; + EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, MessageSignature}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use super::KeyServerImpl; @@ -216,7 +223,7 @@ pub mod tests { } impl MessageSigner for DummyKeyServer { - fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageData) -> Result { + fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { unimplemented!() } } diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 5ff7fe1a8ad..f15adddc7b3 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -27,6 +27,7 @@ use tokio_io::IoFuture; use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; +use util::H256; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; use key_server_cluster::cluster_sessions::ClusterSessions; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, @@ -67,6 +68,8 @@ pub trait ClusterClient: Send + Sync { fn new_encryption_session(&self, session_id: SessionId, requestor_signature: Signature, common_point: Public, encrypted_point: Public) -> Result, Error>; /// Start new decryption session. fn new_decryption_session(&self, session_id: SessionId, requestor_signature: Signature, is_shadow_decryption: bool) -> Result, Error>; + /// Start new signing session. + fn new_signing_session(&self, session_id: SessionId, requestor_signature: Signature, message_hash: H256) -> Result, Error>; #[cfg(test)] /// Ask node to make 'faulty' generation sessions. @@ -79,7 +82,7 @@ pub trait ClusterClient: Send + Sync { fn connect(&self); } -/// Cluster access for single encryption/decryption participant. +/// Cluster access for single encryption/decryption/signing participant. pub trait Cluster: Send + Sync { /// Broadcast message to all other nodes. fn broadcast(&self, message: Message) -> Result<(), Error>; @@ -564,7 +567,6 @@ impl ClusterCore { }, }; - let mut is_queued_message = false; loop { match session.clone().and_then(|session| match message { DecryptionMessage::DecryptionConsensusMessage(ref message) => @@ -593,17 +595,12 @@ impl ClusterCore { // try to dequeue message match data.sessions.decryption_sessions.dequeue_message(&decryption_session_id) { Some((msg_sender, msg)) => { - is_queued_message = true; sender = msg_sender; message = msg; }, None => break, } }, - Err(Error::TooEarlyForRequest) => { - data.sessions.decryption_sessions.enqueue_message(&decryption_session_id, sender, message, is_queued_message); - break; - }, Err(err) => { warn!(target: "secretstore_net", "{}: decryption session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); data.sessions.respond_with_decryption_error(&session_id, &sub_session_id, &sender, message::DecryptionSessionError { @@ -624,78 +621,74 @@ impl ClusterCore { fn process_signing_message(data: Arc, connection: Arc, mut message: SigningMessage) { let session_id = message.session_id().clone(); let sub_session_id = message.sub_session_id().clone(); - let decryption_session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone()); + let signing_session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone()); let mut sender = connection.node_id().clone(); let session = match message { - _ => unimplemented!(), -/* DecryptionMessage::InitializeDecryptionSession(_) => { + SigningMessage::SigningConsensusMessage(ref message) if match message.message { + ConsensusMessage::InitializeConsensusSession(_) => true, + _ => false, + } => { let mut connected_nodes = data.connections.connected_nodes(); connected_nodes.insert(data.self_key_pair.public().clone()); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); - data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster) + data.sessions.new_signing_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster) }, _ => { - data.sessions.decryption_sessions.get(&decryption_session_id) + data.sessions.signing_sessions.get(&signing_session_id) .ok_or(Error::InvalidSessionId) - },*/ + }, }; - let mut is_queued_message = false; loop { -/* match session.clone().and_then(|session| match message { - DecryptionMessage::InitializeDecryptionSession(ref message) => - session.on_initialize_session(sender.clone(), message), - DecryptionMessage::ConfirmDecryptionInitialization(ref message) => - session.on_confirm_initialization(sender.clone(), message), - DecryptionMessage::RequestPartialDecryption(ref message) => - session.on_partial_decryption_requested(sender.clone(), message), - DecryptionMessage::PartialDecryption(ref message) => - session.on_partial_decryption(sender.clone(), message), - DecryptionMessage::DecryptionSessionError(ref message) => + match session.clone().and_then(|session| match message { + SigningMessage::SigningConsensusMessage(ref message) => + session.on_consensus_message(sender.clone(), message), + SigningMessage::SigningGenerationMessage(ref message) => + session.on_generation_message(sender.clone(), message), + SigningMessage::RequestPartialSignature(ref message) => + session.on_partial_signature_requested(sender.clone(), message), + SigningMessage::PartialSignature(ref message) => + session.on_partial_signature(sender.clone(), message), + SigningMessage::SigningSessionError(ref message) => session.on_session_error(sender.clone(), message), - DecryptionMessage::DecryptionSessionCompleted(ref message) => + SigningMessage::SigningSessionCompleted(ref message) => session.on_session_completed(sender.clone(), message), }) { Ok(_) => { // if session is completed => stop let session = session.clone().expect("session.method() call finished with success; session exists; qed"); let session_state = session.state(); - if session_state == DecryptionSessionState::Finished { - info!(target: "secretstore_net", "{}: decryption session completed", data.self_key_pair.public()); + if session_state == SigningSessionState::Finished { + info!(target: "secretstore_net", "{}: signing session completed", data.self_key_pair.public()); } - if session_state == DecryptionSessionState::Finished || session_state == DecryptionSessionState::Failed { - data.sessions.decryption_sessions.remove(&decryption_session_id); + if session_state == SigningSessionState::Finished || session_state == SigningSessionState::Failed { + data.sessions.signing_sessions.remove(&signing_session_id); break; } // try to dequeue message - match data.sessions.decryption_sessions.dequeue_message(&decryption_session_id) { + match data.sessions.signing_sessions.dequeue_message(&signing_session_id) { Some((msg_sender, msg)) => { - is_queued_message = true; sender = msg_sender; message = msg; }, None => break, } }, - Err(Error::TooEarlyForRequest) => { - data.sessions.decryption_sessions.enqueue_message(&decryption_session_id, sender, message, is_queued_message); - break; - }, Err(err) => { - warn!(target: "secretstore_net", "{}: decryption session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); - data.sessions.respond_with_decryption_error(&session_id, &sub_session_id, &sender, message::DecryptionSessionError { + warn!(target: "secretstore_net", "{}: signing session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); + data.sessions.respond_with_signing_error(&session_id, &sub_session_id, &sender, message::SigningSessionError { session: session_id.clone().into(), sub_session: sub_session_id.clone().into(), error: format!("{:?}", err), }); if err != Error::InvalidSessionId { - data.sessions.decryption_sessions.remove(&decryption_session_id); + data.sessions.signing_sessions.remove(&signing_session_id); } break; }, - }*/ + } } } @@ -930,6 +923,17 @@ impl ClusterClient for ClusterClientImpl { Ok(session) } + fn new_signing_session(&self, session_id: SessionId, requestor_signature: Signature, message_hash: H256) -> Result, Error> { + let mut connected_nodes = self.data.connections.connected_nodes(); + connected_nodes.insert(self.data.self_key_pair.public().clone()); + + let access_key = Random.generate()?.secret().clone(); + let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); + let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key, cluster)?; + session.initialize(requestor_signature, message_hash)?; + Ok(session) + } + #[cfg(test)] fn connect(&self) { ClusterCore::connect_disconnected_nodes(self.data.clone()); diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 103ce7389f9..077fcf4e313 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -230,11 +230,56 @@ impl ClusterSessions { }); } + /// Create new signing session. + pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { + let session_id = SigningSessionId::new(session_id, sub_session_id); + if self.signing_sessions.contains(&session_id) { + return Err(Error::DuplicateSessionId); + } + + // some of nodes, which were encrypting secret may be down + // => do not use these in signing session + let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); + for disconnected_node in disconnected_nodes { + encrypted_data.id_numbers.remove(&disconnected_node); + } + + Ok(self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), SigningSessionImpl::new(SigningSessionParams { + id: session_id.id, + access_key: session_id.access_key, + self_node_id: self.self_node_id.clone(), + encrypted_data: encrypted_data, + acl_storage: self.acl_storage.clone(), + cluster: cluster, + })?)) + } + + /// Send signing session error. + pub fn respond_with_signing_error(&self, session_id: &SessionId, sub_session_id: &Secret, to: &NodeId, error: message::SigningSessionError) { + let session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone()); + self.signing_sessions.sessions.read().get(&session_id) + .map(|s| { + // error in signing session is non-fatal, if occurs on slave node + // => either respond with error + // => or broadcast error + + // do not bother processing send error, as we already processing error + if &s.master == s.session.node() { + let _ = s.cluster_view.broadcast(Message::Signing(SigningMessage::SigningSessionError(error))); + } else { + let _ = s.cluster_view.send(to, Message::Signing(SigningMessage::SigningSessionError(error))); + } + }); + } + /// Stop sessions that are stalling. pub fn stop_stalled_sessions(&self) { self.generation_sessions.stop_stalled_sessions(); self.encryption_sessions.stop_stalled_sessions(); self.decryption_sessions.stop_stalled_sessions(); + self.signing_sessions.stop_stalled_sessions(); } /// When connection to node is lost. @@ -242,6 +287,7 @@ impl ClusterSessions { self.generation_sessions.on_connection_timeout(node_id); self.encryption_sessions.on_connection_timeout(node_id); self.decryption_sessions.on_connection_timeout(node_id); + self.signing_sessions.on_connection_timeout(node_id); } } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index e9d9f89a944..2d0dd947a80 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -35,15 +35,23 @@ use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage, SigningSessionError}; +pub use key_server_cluster::decryption_session::DecryptionSessionId as SigningSessionId; + /// Signing session API. pub trait Session: Send + Sync + 'static { - /// Get generation session state. + /// Get session state. fn state(&self) -> SessionState; /// Wait until session is completed. Returns signed message. - fn wait(&self, timeout: Option) -> Result<(Secret, Secret), Error>; + fn wait(&self) -> Result<(Secret, Secret), Error>; } -/// Signing session. +/// Distributed signing session. +/// Based on "Efficient Multi-Party Digital Signature using Adaptive Secret Sharing for Low-Power Devices in Wireless Network" paper. +/// Brief overview: +/// 1) initialization: master node (which has received request for signing the message) requests all other nodes to sign the message +/// 2) ACL check: all nodes which have received the request are querying ACL-contract to check if requestor has access to the private key +/// 3) partial signing: every node which has succussfully checked access for the requestor do a partial signing +/// 4) signing: master node receives all partial signatures of the secret and computes the signature pub struct SessionImpl { /// Key generation session id. id: SessionId, @@ -63,15 +71,6 @@ pub struct SessionImpl { data: Mutex, } -/// Signing session Id. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SigningSessionId { - /// Encryption session id. - pub id: SessionId, - /// Signing session access key. - pub access_key: Secret, -} - /// SessionImpl creation parameters pub struct SessionParams { /// SessionImpl identifier. @@ -374,7 +373,8 @@ impl SessionImpl { SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } - fn on_partial_signature_requested(&self, sender: NodeId, message: &RequestPartialSignature) -> Result<(), Error> { + /// When partial signature is requested. + pub fn on_partial_signature_requested(&self, sender: NodeId, message: &RequestPartialSignature) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); debug_assert!(&sender != self.node()); @@ -774,7 +774,7 @@ impl Session for SessionImpl { self.data.lock().state.clone() } - fn wait(&self, timeout: Option) -> Result<(Secret, Secret), Error> { + fn wait(&self) -> Result<(Secret, Secret), Error> { let mut data = self.data.lock(); if !data.signed_message.is_some() { self.completed.wait(&mut data); @@ -786,32 +786,6 @@ impl Session for SessionImpl { } } -impl SigningSessionId { - /// Create new decryption session Id. - pub fn new(session_id: SessionId, sub_session_id: Secret) -> Self { - SigningSessionId { - id: session_id, - access_key: sub_session_id, - } - } -} - -impl PartialOrd for SigningSessionId { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - - -impl Ord for SigningSessionId { - fn cmp(&self, other: &Self) -> Ordering { - match self.id.cmp(&other.id) { - Ordering::Equal => self.access_key.cmp(&other.access_key), - r @ _ => r, - } - } -} - fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; @@ -947,7 +921,7 @@ mod tests { // verify signature let public = gl.master().joint_public_and_secret().unwrap().unwrap().0; - let signature = sl.master().wait(None).unwrap(); + let signature = sl.master().wait().unwrap(); assert!(math::verify_signature(&public, &signature, &message_hash).unwrap()); } } diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 27828760f4e..4a5dea8fb82 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use types::all::{Error, Public, ServerKeyId, MessageData, RequestSignature, EncryptedDocumentKey, +use types::all::{Error, Public, ServerKeyId, MessageHash, MessageSignature, RequestSignature, EncryptedDocumentKey, EncryptedDocumentKeyShadow}; /// Server key (SK) generator. @@ -66,7 +66,7 @@ pub trait MessageSigner: ServerKeyGenerator { /// `signature` is `key_id`, signed with caller public key. /// `message` is the message to be signed. /// Result is a signed message. - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageData) -> Result; + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; } diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index f0c6456c9fe..fad2282f7c3 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -28,8 +28,10 @@ pub type NodeId = ethkey::Public; pub type ServerKeyId = util::H256; /// Encrypted document key type. pub type EncryptedDocumentKey = util::Bytes; -/// Message data. -pub type MessageData = util::Bytes; +/// Message hash. +pub type MessageHash = util::H256; +/// Message signature. +pub type MessageSignature = util::Bytes; /// Request signature type. pub type RequestSignature = ethkey::Signature; /// Public key type. From 3a843d400bae3d5177b01229f1fd0ea372af30e4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 12:07:56 +0300 Subject: [PATCH 20/45] new key server tests --- secret_store/src/key_server.rs | 69 +++++++++++++++++-- .../src/key_server_cluster/io/message.rs | 20 ++++-- .../src/key_server_cluster/message.rs | 18 ++++- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 914ef043fd5..ecd3561f0ad 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -126,11 +126,11 @@ impl MessageSigner for KeyServerImpl { let signing_session = self.data.lock().cluster.new_signing_session(key_id.clone(), signature.clone(), message)?; let message_signature = signing_session.wait()?; - // compose two message signature components into single - let mut composed_signature: Vec = Vec::with_capacity(64); - composed_signature.extend_from_slice(&**message_signature.0); - composed_signature.extend_from_slice(&**message_signature.1); - Ok(composed_signature.into()) + // compose two message signature components into single one + let mut combined_signature: Vec = Vec::with_capacity(64); + combined_signature.extend_from_slice(&**message_signature.0); + combined_signature.extend_from_slice(&**message_signature.1); + Ok(combined_signature.into()) } } @@ -186,9 +186,11 @@ pub mod tests { use std::time; use std::sync::Arc; use ethcrypto; - use ethkey::{self, Random, Generator}; + use ethkey::{self, Secret, Random, Generator}; use acl_storage::tests::DummyAclStorage; use key_storage::tests::DummyKeyStorage; + use key_server_cluster::math; + use util::H256; use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, MessageSignature}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; @@ -321,4 +323,59 @@ pub mod tests { } } } + + #[test] + fn server_key_generation_and_storing_document_key_works_over_network_with_3_nodes() { + //::logger::init_log(); + let key_servers = make_key_servers(6090, 3); + + let test_cases = [0, 1, 2]; + for threshold in &test_cases { + // generate server key + let server_key_id = Random.generate().unwrap().secret().clone(); + let requestor_secret = Random.generate().unwrap().secret().clone(); + let signature = ethkey::sign(&requestor_secret, &server_key_id).unwrap(); + let server_public = key_servers[0].generate_key(&server_key_id, &signature, *threshold).unwrap(); + + // generate document key (this is done by KS client so that document key is unknown to any KS) + let generated_key = Random.generate().unwrap().public().clone(); + let encrypted_document_key = math::encrypt_secret(&generated_key, &server_public).unwrap(); + + // store document key + key_servers[0].store_document_key(&server_key_id, &signature, encrypted_document_key.common_point, encrypted_document_key.encrypted_point).unwrap(); + + // now let's try to retrieve key back + for key_server in key_servers.iter() { + let retrieved_key = key_server.restore_document_key(&server_key_id, &signature).unwrap(); + let retrieved_key = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &retrieved_key).unwrap(); + let retrieved_key = Public::from_slice(&retrieved_key); + assert_eq!(retrieved_key, generated_key); + } + } + } + + #[test] + fn server_key_generation_and_message_signing_works_over_network_with_3_nodes() { + //::logger::init_log(); + let key_servers = make_key_servers(6100, 3); + + //let test_cases = [0, 1, 2]; + let test_cases = [1]; + for threshold in &test_cases { + // generate server key + let server_key_id = Random.generate().unwrap().secret().clone(); + let requestor_secret = Random.generate().unwrap().secret().clone(); + let signature = ethkey::sign(&requestor_secret, &server_key_id).unwrap(); + let server_public = key_servers[0].generate_key(&server_key_id, &signature, *threshold).unwrap(); + + // sign message + let message_hash = H256::from(42); + let combined_signature = key_servers[0].sign_message(&server_key_id, &signature, message_hash.clone()).unwrap(); + let signature_c = Secret::from_slice(&combined_signature[..32]).unwrap(); + let signature_s = Secret::from_slice(&combined_signature[32..]).unwrap(); + + // check signature + assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); + } + } } diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index b225d9d1893..d235a6f0fec 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -26,7 +26,7 @@ use ethkey::math::curve_order; use util::{H256, U256}; use key_server_cluster::Error; use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, - DecryptionMessage, ConsensusMessage}; + DecryptionMessage, SigningMessage}; /// Size of serialized header. pub const MESSAGE_HEADER_SIZE: usize = 4; @@ -86,10 +86,14 @@ pub fn serialize_message(message: Message) -> Result { Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (153, serde_json::to_vec(&payload)), Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (154, serde_json::to_vec(&payload)), - Message::Consensus(ConsensusMessage::InitializeConsensusSession(payload)) => (200, serde_json::to_vec(&payload)), - Message::Consensus(ConsensusMessage::ConfirmConsensusInitialization(payload)) => (201, serde_json::to_vec(&payload)), + Message::Signing(SigningMessage::SigningConsensusMessage(payload)) => (200, serde_json::to_vec(&payload)), + Message::Signing(SigningMessage::SigningGenerationMessage(payload)) => (201, serde_json::to_vec(&payload)), + Message::Signing(SigningMessage::RequestPartialSignature(payload)) => (202, serde_json::to_vec(&payload)), + Message::Signing(SigningMessage::PartialSignature(payload)) => (203, serde_json::to_vec(&payload)), + Message::Signing(SigningMessage::SigningSessionError(payload)) => (204, serde_json::to_vec(&payload)), + Message::Signing(SigningMessage::SigningSessionCompleted(payload)) => (205, serde_json::to_vec(&payload)), - Message::Signing(_) => unreachable!(), + Message::Consensus(_) => unreachable!("always wrapped"), }; let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; @@ -126,8 +130,12 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 154 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 200 => Message::Consensus(ConsensusMessage::InitializeConsensusSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 201 => Message::Consensus(ConsensusMessage::ConfirmConsensusInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 200 => Message::Signing(SigningMessage::SigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 201 => Message::Signing(SigningMessage::SigningGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 202 => Message::Signing(SigningMessage::RequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 203 => Message::Signing(SigningMessage::PartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 204 => Message::Signing(SigningMessage::SigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 205 => Message::Signing(SigningMessage::SigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), _ => return Err(Error::Serde(format!("unknown message type {}", header.kind))), }) diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index d0378cac58e..9e41d0b3009 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -441,11 +441,25 @@ impl DecryptionMessage { impl SigningMessage { pub fn session_id(&self) -> &SessionId { - unimplemented!() + match *self { + SigningMessage::SigningConsensusMessage(ref msg) => &msg.session, + SigningMessage::SigningGenerationMessage(ref msg) => &msg.session, + SigningMessage::RequestPartialSignature(ref msg) => &msg.session, + SigningMessage::PartialSignature(ref msg) => &msg.session, + SigningMessage::SigningSessionError(ref msg) => &msg.session, + SigningMessage::SigningSessionCompleted(ref msg) => &msg.session, + } } pub fn sub_session_id(&self) -> &Secret { - unimplemented!() + match *self { + SigningMessage::SigningConsensusMessage(ref msg) => &msg.sub_session, + SigningMessage::SigningGenerationMessage(ref msg) => &msg.sub_session, + SigningMessage::RequestPartialSignature(ref msg) => &msg.sub_session, + SigningMessage::PartialSignature(ref msg) => &msg.sub_session, + SigningMessage::SigningSessionError(ref msg) => &msg.sub_session, + SigningMessage::SigningSessionCompleted(ref msg) => &msg.sub_session, + } } } From b0f344a9f4ffac2fc0e4f9a43737284c2f873ba1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 12:48:05 +0300 Subject: [PATCH 21/45] fix after merge --- secret_store/src/key_server.rs | 4 +- .../src/key_server_cluster/cluster.rs | 100 ++-------- .../key_server_cluster/cluster_sessions.rs | 175 +++++++++++++++++- .../src/key_server_cluster/consensus.rs | 25 +-- .../key_server_cluster/decryption_session.rs | 11 +- secret_store/src/key_server_cluster/math.rs | 3 +- 6 files changed, 195 insertions(+), 123 deletions(-) diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index ecd3561f0ad..47704d23503 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -371,8 +371,8 @@ pub mod tests { // sign message let message_hash = H256::from(42); let combined_signature = key_servers[0].sign_message(&server_key_id, &signature, message_hash.clone()).unwrap(); - let signature_c = Secret::from_slice(&combined_signature[..32]).unwrap(); - let signature_s = Secret::from_slice(&combined_signature[32..]).unwrap(); + let signature_c = Secret::from_slice(&combined_signature[..32]); + let signature_s = Secret::from_slice(&combined_signature[32..]); // check signature assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index a03fc83d500..030065ed6f6 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -26,10 +26,11 @@ use parking_lot::{RwLock, Mutex}; use tokio_io::IoFuture; use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; -use ethkey::{Public, KeyPair, Signature, Random, Generator}; +use ethkey::{Public, Secret, KeyPair, Signature, Random, Generator}; use util::H256; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; -use key_server_cluster::cluster_sessions::ClusterSessions; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, EncryptedDocumentKeyShadow}; +use key_server_cluster::cluster_sessions::{ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, + DecryptionSessionWrapper, SigningSessionWrapper}; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage, ConsensusMessage}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; @@ -186,28 +187,6 @@ pub struct Connection { last_message_time: Mutex, } -/// Encryption session implementation, which removes session from cluster on drop. -struct EncryptionSessionWrapper { - /// Wrapped session. - session: Arc, - /// Session Id. - session_id: SessionId, - /// Cluster data reference. - cluster: Weak, -} - -/// Decryption session implementation, which removes session from cluster on drop. -struct DecryptionSessionWrapper { - /// Wrapped session. - session: Arc, - /// Session Id. - session_id: SessionId, - /// Session sub id. - access_key: Secret, - /// Cluster data reference. - cluster: Weak, -} - impl ClusterCore { pub fn new(handle: Handle, config: ClusterConfiguration) -> Result, Error> { let listen_address = make_socket_address(&config.listen_address.0, config.listen_address.1)?; @@ -812,6 +791,11 @@ impl ClusterData { self.connections.get(node) } + /// Get sessions reference. + pub fn sessions(&self) -> &ClusterSessions { + &self.sessions + } + /// Spawns a future using thread pool and schedules execution of it with event loop handle. pub fn spawn(&self, f: F) where F: Future + Send + 'static, F::Item: Send + 'static, F::Error: Send + 'static { let pool_work = self.pool.spawn(f); @@ -921,7 +905,7 @@ impl ClusterClient for ClusterClientImpl { let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let session = self.data.sessions.new_generation_session(self.data.self_key_pair.public().clone(), session_id, cluster)?; session.initialize(author, threshold, connected_nodes)?; - Ok(session) + Ok(GenerationSessionWrapper::new(Arc::downgrade(&self.data), session_id, session)) } fn new_encryption_session(&self, session_id: SessionId, requestor_signature: Signature, common_point: Public, encrypted_point: Public) -> Result, Error> { @@ -942,7 +926,7 @@ impl ClusterClient for ClusterClientImpl { let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let session = self.data.sessions.new_decryption_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster)?; session.initialize(requestor_signature, is_shadow_decryption)?; - Ok(DecryptionSessionWrapper::new(Arc::downgrade(&self.data), session_id, access_key, session)) + Ok(DecryptionSessionWrapper::new(Arc::downgrade(&self.data), DecryptionSessionId::new(session_id, access_key), session)) } fn new_signing_session(&self, session_id: SessionId, requestor_signature: Signature, message_hash: H256) -> Result, Error> { @@ -951,9 +935,9 @@ impl ClusterClient for ClusterClientImpl { let access_key = Random.generate()?.secret().clone(); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); - let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key, cluster)?; + let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster)?; session.initialize(requestor_signature, message_hash)?; - Ok(session) + Ok(SigningSessionWrapper::new(Arc::downgrade(&self.data), SigningSessionId::new(session_id, access_key), session)) } #[cfg(test)] @@ -972,64 +956,6 @@ impl ClusterClient for ClusterClientImpl { } } -impl EncryptionSessionWrapper { - pub fn new(cluster: Weak, session_id: SessionId, session: Arc) -> Arc { - Arc::new(EncryptionSessionWrapper { - session: session, - session_id: session_id, - cluster: cluster, - }) - } -} - -impl EncryptionSession for EncryptionSessionWrapper { - fn state(&self) -> EncryptionSessionState { - self.session.state() - } - - fn wait(&self, timeout: Option) -> Result { - self.session.wait(timeout) - } - - #[cfg(test)] - fn joint_public_key(&self) -> Option> { - self.session.joint_public_key() - } -} - -impl Drop for EncryptionSessionWrapper { - fn drop(&mut self) { - if let Some(cluster) = self.cluster.upgrade() { - cluster.sessions.remove_encryption_session(&self.session_id); - } - } -} - -impl DecryptionSessionWrapper { - pub fn new(cluster: Weak, session_id: SessionId, access_key: Secret, session: Arc) -> Arc { - Arc::new(DecryptionSessionWrapper { - session: session, - session_id: session_id, - access_key: access_key, - cluster: cluster, - }) - } -} - -impl DecryptionSession for DecryptionSessionWrapper { - fn wait(&self) -> Result { - self.session.wait() - } -} - -impl Drop for DecryptionSessionWrapper { - fn drop(&mut self) { - if let Some(cluster) = self.cluster.upgrade() { - cluster.sessions.remove_decryption_session(&self.session_id, &self.access_key); - } - } -} - fn make_socket_address(address: &str, port: u16) -> Result { let ip_address: IpAddr = address.parse().map_err(|_| Error::InvalidNodeAddress)?; Ok(SocketAddr::new(ip_address, port)) diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 077fcf4e313..2e6b02943b9 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -15,20 +15,22 @@ // along with Parity. If not, see . use std::time; -use std::sync::Arc; +use std::sync::{Arc, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; use std::collections::{VecDeque, BTreeSet, BTreeMap}; use parking_lot::RwLock; -use ethkey::Secret; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; -use key_server_cluster::cluster::{Cluster, ClusterView, ClusterConfiguration}; +use ethkey::{Public, Secret}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, EncryptedDocumentKeyShadow}; +use key_server_cluster::cluster::{Cluster, ClusterData, ClusterView, ClusterConfiguration}; use key_server_cluster::message::{self, Message, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionParams as GenerationSessionParams}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, DecryptionSessionId, - SessionParams as DecryptionSessionParams}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams}; -use key_server_cluster::signing_session::{SessionImpl as SigningSessionImpl, SigningSessionId, - SessionParams as SigningSessionParams}; +use key_server_cluster::generation_session::{Session as GenerationSession, SessionImpl as GenerationSessionImpl, + SessionParams as GenerationSessionParams, SessionState as GenerationSessionState}; +use key_server_cluster::decryption_session::{Session as DecryptionSession, SessionImpl as DecryptionSessionImpl, + DecryptionSessionId, SessionParams as DecryptionSessionParams}; +use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionImpl as EncryptionSessionImpl, + SessionParams as EncryptionSessionParams, SessionState as EncryptionSessionState}; +use key_server_cluster::signing_session::{Session as SigningSession, SessionImpl as SigningSessionImpl, + SigningSessionId, SessionParams as SigningSessionParams, SessionState as SigningSessionState}; /// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, /// we must treat this session as stalled && finish it with an error. @@ -45,6 +47,7 @@ pub trait ClusterSession { /// When it takes too much time to receive response from the node. fn on_node_timeout(&self, node_id: &NodeId); } + /// Active sessions on this cluster. pub struct ClusterSessions { /// Key generation sessions. @@ -87,6 +90,46 @@ pub struct QueuedSession { pub queue: VecDeque<(NodeId, M)>, } +/// Generation session implementation, which removes session from cluster on drop. +pub struct GenerationSessionWrapper { + /// Wrapped session. + session: Arc, + /// Session Id. + session_id: SessionId, + /// Cluster data reference. + cluster: Weak, +} + +/// Encryption session implementation, which removes session from cluster on drop. +pub struct EncryptionSessionWrapper { + /// Wrapped session. + session: Arc, + /// Session Id. + session_id: SessionId, + /// Cluster data reference. + cluster: Weak, +} + +/// Decryption session implementation, which removes session from cluster on drop. +pub struct DecryptionSessionWrapper { + /// Wrapped session. + session: Arc, + /// Session Id. + session_id: DecryptionSessionId, + /// Cluster data reference. + cluster: Weak, +} + +/// Signing session implementation, which removes session from cluster on drop. +pub struct SigningSessionWrapper { + /// Wrapped session. + session: Arc, + /// Session Id. + session_id: SigningSessionId, + /// Cluster data reference. + cluster: Weak, +} + impl ClusterSessions { /// Create new cluster sessions container. pub fn new(config: &ClusterConfiguration) -> Self { @@ -367,3 +410,115 @@ impl ClusterSessionsContainer where K: Clone + Ord, V: Cluster } } } + +impl GenerationSessionWrapper { + pub fn new(cluster: Weak, session_id: SessionId, session: Arc) -> Arc { + Arc::new(GenerationSessionWrapper { + session: session, + session_id: session_id, + cluster: cluster, + }) + } +} + +impl GenerationSession for GenerationSessionWrapper { + fn state(&self) -> GenerationSessionState { + self.session.state() + } + + fn wait(&self, timeout: Option) -> Result { + self.session.wait(timeout) + } + + fn joint_public_and_secret(&self) -> Option> { + self.session.joint_public_and_secret() + } +} + +impl Drop for GenerationSessionWrapper { + fn drop(&mut self) { + if let Some(cluster) = self.cluster.upgrade() { + cluster.sessions().generation_sessions.remove(&self.session_id); + } + } +} + +impl EncryptionSessionWrapper { + pub fn new(cluster: Weak, session_id: SessionId, session: Arc) -> Arc { + Arc::new(EncryptionSessionWrapper { + session: session, + session_id: session_id, + cluster: cluster, + }) + } +} + +impl EncryptionSession for EncryptionSessionWrapper { + fn state(&self) -> EncryptionSessionState { + self.session.state() + } + + fn wait(&self, timeout: Option) -> Result<(), Error> { + self.session.wait(timeout) + } +} + +impl Drop for EncryptionSessionWrapper { + fn drop(&mut self) { + if let Some(cluster) = self.cluster.upgrade() { + cluster.sessions().encryption_sessions.remove(&self.session_id); + } + } +} + +impl DecryptionSessionWrapper { + pub fn new(cluster: Weak, session_id: DecryptionSessionId, session: Arc) -> Arc { + Arc::new(DecryptionSessionWrapper { + session: session, + session_id: session_id, + cluster: cluster, + }) + } +} + +impl DecryptionSession for DecryptionSessionWrapper { + fn wait(&self) -> Result { + self.session.wait() + } +} + +impl Drop for DecryptionSessionWrapper { + fn drop(&mut self) { + if let Some(cluster) = self.cluster.upgrade() { + cluster.sessions().decryption_sessions.remove(&self.session_id); + } + } +} + +impl SigningSessionWrapper { + pub fn new(cluster: Weak, session_id: SigningSessionId, session: Arc) -> Arc { + Arc::new(SigningSessionWrapper { + session: session, + session_id: session_id, + cluster: cluster, + }) + } +} + +impl SigningSession for SigningSessionWrapper { + fn state(&self) -> SigningSessionState { + self.session.state() + } + + fn wait(&self) -> Result<(Secret, Secret), Error> { + self.session.wait() + } +} + +impl Drop for SigningSessionWrapper { + fn drop(&mut self) { + if let Some(cluster) = self.cluster.upgrade() { + cluster.sessions().signing_sessions.remove(&self.session_id); + } + } +} diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index c916c58f89c..56c7c31a186 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -132,7 +132,7 @@ impl Consensus where T: Debug + Clone { Consensus::Established(ref mut consensus) => return consensus.accept_offer(node), Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => return consensus.core.accept_offer(node), - Consensus::Unreachable => { println!("=== a"); return Err(Error::InvalidStateForRequest) }, + Consensus::Unreachable => return Err(Error::InvalidStateForRequest), }; *self = Consensus::Established(established_consensus); @@ -153,7 +153,7 @@ impl Consensus where T: Debug + Clone { Consensus::Established(ref mut consensus) => return consensus.reject_offer(node), Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => return consensus.core.reject_offer(node), - _ => {println!("=== b"); return Err(Error::InvalidStateForRequest) }, + _ => return Err(Error::InvalidStateForRequest), } *self = Consensus::Unreachable; @@ -165,7 +165,7 @@ impl Consensus where T: Debug + Clone { let active_consensus = match *self { Consensus::Established(ref established_consensus) => ActiveConsensus::new(established_consensus.clone()), Consensus::Active(ref active_consensus) => ActiveConsensus::new(active_consensus.core.clone()), - _ => { panic!("=== c"); println!("=== c: {:?}", self); return Err(Error::InvalidStateForRequest) }, + _ => return Err(Error::InvalidStateForRequest), }; *self = Consensus::Active(active_consensus); @@ -176,7 +176,7 @@ impl Consensus where T: Debug + Clone { pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { match *self { Consensus::Active(ref mut consensus) => consensus.select_nodes(), - _ => {println!("=== d"); Err(Error::InvalidStateForRequest) }, + _ => Err(Error::InvalidStateForRequest), } } @@ -184,7 +184,7 @@ impl Consensus where T: Debug + Clone { pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { match *self { Consensus::Active(ref consensus) => consensus.selected_nodes(), - _ => {println!("=== e"); Err(Error::InvalidStateForRequest) }, + _ => Err(Error::InvalidStateForRequest), } } @@ -192,7 +192,7 @@ impl Consensus where T: Debug + Clone { pub fn job_request_sent(&mut self, node: &NodeId) -> Result<(), Error> { match *self { Consensus::Active(ref mut consensus) => consensus.job_request_sent(node), - _ => {println!("=== f"); Err(Error::InvalidStateForRequest) }, + _ => Err(Error::InvalidStateForRequest), } } @@ -209,7 +209,7 @@ impl Consensus where T: Debug + Clone { // else fall through consensus.clone() }, - _ => {println!("=== g"); return Err(Error::InvalidStateForRequest) }, + _ => return Err(Error::InvalidStateForRequest), }; *self = Consensus::Completed(completed_consensus); @@ -220,7 +220,7 @@ impl Consensus where T: Debug + Clone { pub fn job_requests(&self) -> Result<&BTreeSet, Error> { match *self { Consensus::Active(ref consensus) => consensus.job_requests(), - _ => {println!("=== h"); Err(Error::InvalidStateForRequest) }, + _ => Err(Error::InvalidStateForRequest), } } @@ -229,7 +229,7 @@ impl Consensus where T: Debug + Clone { match *self { Consensus::Active(ref consensus) => consensus.job_responses(), Consensus::Completed(ref consensus) => consensus.job_responses(), - _ => {println!("=== i"); Err(Error::InvalidStateForRequest)}, + _ => Err(Error::InvalidStateForRequest), } } @@ -267,7 +267,7 @@ impl Consensus where T: Debug + Clone { // else fall through }, Consensus::Completed(_) => return Ok(false), - _ => {println!("=== j"); return Err(Error::InvalidStateForRequest) }, + _ => return Err(Error::InvalidStateForRequest), } *self = Consensus::Unreachable; @@ -304,7 +304,6 @@ impl ConsensusCore { /// When node has accepted join offer. pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { if !self.requested_nodes.remove(node) { -println!("=== k"); return Err(Error::InvalidStateForRequest); } @@ -315,7 +314,6 @@ println!("=== k"); /// When node has rejected join offer. pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { if !self.requested_nodes.remove(node) { -println!("=== l"); return Err(Error::InvalidStateForRequest); } @@ -346,7 +344,6 @@ impl ActiveConsensus where T: Debug + Clone { /// Select nodes to make job. pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { if !self.selected_nodes.is_empty() { -println!("=== m"); return Err(Error::InvalidStateForRequest); } @@ -357,7 +354,6 @@ println!("=== m"); /// Get nodes, selected nodes to make their job. pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { if self.selected_nodes.is_empty() { -println!("=== n"); return Err(Error::InvalidStateForRequest); } @@ -383,7 +379,6 @@ println!("=== n"); /// When job response is received from the node. pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { if !self.active_requests.remove(node) { -println!("=== o"); return Err(Error::InvalidStateForRequest); } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 7bcc092b2d4..f1671e24e75 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -200,7 +200,6 @@ impl SessionImpl { // check state if data.state != SessionState::WaitingForInitialization { -println!("=== 0"); return Err(Error::InvalidStateForRequest); } @@ -269,7 +268,7 @@ println!("=== 0"); // process message let consensus_action = { let mut data = data.deref_mut(); - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest).map_err(|e| { println!("=== 1");e })?; + let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest)?; match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => { let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; @@ -277,7 +276,7 @@ println!("=== 0"); consensus_session.on_initialize_session(sender, &requestor)? }, ConsensusMessage::ConfirmConsensusInitialization(ref message) => { - let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest).map_err(|e| { println!("=== 2"); e })?; + let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? }, } @@ -285,11 +284,9 @@ println!("=== 0"); SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; // if consensus is established and we are on master node => ask for partial decryption -println!("====================== before state check: {:?}", data.state); if data.state != SessionState::EstablishedConsensus { return Ok(()); } -println!("====================== before start_waiting_for_partial_decryption"); SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) } @@ -314,7 +311,6 @@ println!("====================== before start_waiting_for_partial_decryption"); data.state = SessionState::WaitingForPartialDecryptionRequest; } if data.state != SessionState::WaitingForPartialDecryptionRequest { -println!("=== 3: {:?}", data.state); return Err(Error::InvalidStateForRequest); } @@ -347,13 +343,12 @@ println!("=== 3: {:?}", data.state); // check state if data.state != SessionState::WaitingForPartialDecryption { -println!("=== 4"); return Err(Error::InvalidStateForRequest); } // remember partial signature { - let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest).map_err(|e| { println!("=== 5"); e })?; + let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; consensus.job_response_received(&sender, PartialDecryptionResult { shadow_point: message.shadow_point.clone().into(), decrypt_shadow: message.decrypt_shadow.clone(), diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index fcee3607a29..5e197fda01e 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -312,7 +312,8 @@ pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> // map hash to EC finite field value let hash: U256 = hash.into(); let hash: H256 = (hash % math::curve_order()).into(); - let hash = Secret::from_slice(&*hash)?; + let hash = Secret::from_slice(&*hash); + hash.check_validity()?; Ok(hash) } From a792b3015dd464d65b0419b32146d912fcf9bea8 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 13:17:22 +0300 Subject: [PATCH 22/45] enabled warnings --- .../src/key_server_cluster/cluster.rs | 7 ++--- .../src/key_server_cluster/consensus.rs | 7 +++-- .../key_server_cluster/consensus_session.rs | 18 ++--------- .../key_server_cluster/decryption_session.rs | 2 +- .../key_server_cluster/generation_session.rs | 11 ++----- .../src/key_server_cluster/io/message.rs | 2 -- secret_store/src/key_server_cluster/math.rs | 23 +++++++------- .../src/key_server_cluster/message.rs | 14 ++++++--- secret_store/src/key_server_cluster/mod.rs | 6 ---- .../src/key_server_cluster/signing_session.rs | 31 +++++-------------- 10 files changed, 43 insertions(+), 78 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 030065ed6f6..3c2fec2c689 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -16,7 +16,7 @@ use std::io; use std::time; -use std::sync::{Arc, Weak}; +use std::sync::Arc; use std::collections::{BTreeMap, BTreeSet}; use std::collections::btree_map::Entry; use std::net::{SocketAddr, IpAddr}; @@ -26,9 +26,9 @@ use parking_lot::{RwLock, Mutex}; use tokio_io::IoFuture; use tokio_core::reactor::{Handle, Remote, Interval}; use tokio_core::net::{TcpListener, TcpStream}; -use ethkey::{Public, Secret, KeyPair, Signature, Random, Generator}; +use ethkey::{Public, KeyPair, Signature, Random, Generator}; use util::H256; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, EncryptedDocumentKeyShadow}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; use key_server_cluster::cluster_sessions::{ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, DecryptionSessionWrapper, SigningSessionWrapper}; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, @@ -395,7 +395,6 @@ impl ClusterCore { Message::Decryption(message) => ClusterCore::process_decryption_message(data, connection, message), Message::Signing(message) => ClusterCore::process_signing_message(data, connection, message), Message::Cluster(message) => ClusterCore::process_cluster_message(data, connection, message), - Message::Consensus(_) => (), // consensus messages are always wrapped into other messages } } diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 56c7c31a186..a3b8aa0b656 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -17,7 +17,7 @@ // TODO: when job requests sent && we restart, we could receive previous responses => there should be some kind of control (random secret in each group of jobs) use std::fmt::Debug; -use std::collections::{BTreeSet, BTreeMap, VecDeque}; +use std::collections::{BTreeSet, BTreeMap}; use key_server_cluster::{Error, NodeId}; #[derive(Debug, Clone)] @@ -76,6 +76,7 @@ impl Consensus where T: Debug + Clone { })) } + #[cfg(test)] /// Return consensus core reference. pub fn core(&self) -> Option<&ConsensusCore> { match *self { @@ -216,6 +217,7 @@ impl Consensus where T: Debug + Clone { Ok(()) } + #[cfg(test)] /// Return job reqeuests. pub fn job_requests(&self) -> Result<&BTreeSet, Error> { match *self { @@ -296,6 +298,7 @@ impl Consensus where T: Debug + Clone { } impl ConsensusCore { + #[cfg(test)] /// Return rejected nodes list. pub fn rejected_nodes(&self) -> &BTreeSet { &self.rejected_nodes @@ -386,6 +389,7 @@ impl ActiveConsensus where T: Debug + Clone { Ok(()) } + #[cfg(test)] /// Return job requests. pub fn job_requests(&self) -> Result<&BTreeSet, Error> { Ok(&self.active_requests) @@ -421,7 +425,6 @@ impl ActiveConsensus where T: Debug + Clone { #[cfg(test)] mod tests { - use std::collections::BTreeSet; use key_server_cluster::Error; use super::Consensus; diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs index 37a4a46ae66..188917b21a5 100644 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ b/secret_store/src/key_server_cluster/consensus_session.rs @@ -16,13 +16,8 @@ use std::sync::Arc; use std::fmt::Debug; -use std::collections::BTreeSet; -use parking_lot::Mutex; -use ethkey::{self, Public, Secret, Signature}; -use util; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; -use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::ClusterSession; +use ethkey::{self, Public, Signature}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage}; use key_server_cluster::consensus::Consensus; use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; @@ -52,9 +47,6 @@ pub struct AclConsensusChecker { acl_storage: Arc, } -/// Always accept checker for consensus establishing session. -pub struct TrueConsensusChecker; - /// SessionImpl creation parameters pub struct SessionParams { /// Key generation session id. @@ -229,9 +221,3 @@ impl ConsensusChecker for AclConsensusChecker { self.acl_storage.check(requestor, key).unwrap_or(false) } } - -impl ConsensusChecker for TrueConsensusChecker { - fn check_offer(&self, _key: &SessionId, _requestor: &Public) -> bool { - true - } -} diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index f1671e24e75..5006594cd6c 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -15,7 +15,7 @@ // along with Parity. If not, see . use std::cmp::{Ord, PartialOrd, Ordering}; -use std::collections::{BTreeSet, BTreeMap}; +use std::collections::BTreeSet; use std::ops::DerefMut; use std::sync::Arc; use parking_lot::{Mutex, Condvar}; diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index bedfd4671e7..dbbdea185a1 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -215,11 +215,6 @@ impl SessionImpl { self.data.lock().derived_point.clone() } - /// Get key share. - pub fn key_share(&self) -> Option> { - self.data.lock().key_share.clone() - } - /// Simulate faulty generation session behaviour. pub fn simulate_faulty_behaviour(&self) { self.data.lock().simulate_faulty_behaviour = true; @@ -966,7 +961,7 @@ pub mod tests { #[test] fn fails_to_accept_initialization_when_already_initialized() { - let (sid, m, _, mut l) = make_simple_cluster(0, 2).unwrap(); + let (_, _, _, mut l) = make_simple_cluster(0, 2).unwrap(); let message = l.take_message().unwrap(); l.process_message(message.clone()).unwrap(); assert_eq!(l.process_message(message.clone()).unwrap_err(), Error::InvalidStateForRequest); @@ -1061,7 +1056,7 @@ pub mod tests { #[test] fn fails_to_complete_initialization_if_not_waiting_for_it() { - let (sid, m, s, l) = make_simple_cluster(0, 2).unwrap(); + let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap(); assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { session: sid.into(), derived_point: math::generate_random_point().unwrap().into(), @@ -1070,7 +1065,7 @@ pub mod tests { #[test] fn fails_to_complete_initialization_from_non_master_node() { - let (sid, m, s, mut l) = make_simple_cluster(0, 3).unwrap(); + let (sid, _, _, mut l) = make_simple_cluster(0, 3).unwrap(); l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap(); diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index d235a6f0fec..49b71e39d8d 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -92,8 +92,6 @@ pub fn serialize_message(message: Message) -> Result { Message::Signing(SigningMessage::PartialSignature(payload)) => (203, serde_json::to_vec(&payload)), Message::Signing(SigningMessage::SigningSessionError(payload)) => (204, serde_json::to_vec(&payload)), Message::Signing(SigningMessage::SigningSessionCompleted(payload)) => (205, serde_json::to_vec(&payload)), - - Message::Consensus(_) => unreachable!("always wrapped"), }; let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index 5e197fda01e..01b956b531d 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -14,9 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::iter::once; -use ethkey::{Public, Secret, Random, Generator, Signature, recover, math}; -use util::{U256, H256, Bytes, sha3, Hashable}; +use ethkey::{Public, Secret, Random, Generator, math}; +use util::{U256, H256, Hashable}; use key_server_cluster::Error; #[derive(Debug)] @@ -166,7 +165,7 @@ pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &S } /// Compute secret share. -pub fn compute_secret_share<'a, I>(mut secret_values: I) -> Result where I: Iterator { +pub fn compute_secret_share<'a, I>(secret_values: I) -> Result where I: Iterator { compute_secret_sum(secret_values) } @@ -178,13 +177,13 @@ pub fn compute_public_share(self_secret_value: &Secret) -> Result } /// Compute joint public key. -pub fn compute_joint_public<'a, I>(mut public_shares: I) -> Result where I: Iterator { +pub fn compute_joint_public<'a, I>(public_shares: I) -> Result where I: Iterator { compute_public_sum(public_shares) } #[cfg(test)] /// Compute joint secret key. -pub fn compute_joint_secret<'a, I>(mut secret_coeffs: I) -> Result where I: Iterator { +pub fn compute_joint_secret<'a, I>(secret_coeffs: I) -> Result where I: Iterator { compute_secret_sum(secret_coeffs) } @@ -209,7 +208,7 @@ pub fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result(node_secret_share: &Secret, node_number: &Secret, mut other_nodes_numbers: I) -> Result where I: Iterator { +pub fn compute_node_shadow<'a, I>(node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) -> Result where I: Iterator { compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers) } @@ -235,13 +234,13 @@ pub fn compute_node_shadow_point(access_key: &Secret, common_point: &Public, nod } /// Compute joint shadow point. -pub fn compute_joint_shadow_point<'a, I>(mut nodes_shadow_points: I) -> Result where I: Iterator { +pub fn compute_joint_shadow_point<'a, I>(nodes_shadow_points: I) -> Result where I: Iterator { compute_public_sum(nodes_shadow_points) } #[cfg(test)] /// Compute joint shadow point (version for tests). -pub fn compute_joint_shadow_point_test<'a, I>(access_key: &Secret, common_point: &Public, mut nodes_shadows: I) -> Result where I: Iterator { +pub fn compute_joint_shadow_point_test<'a, I>(access_key: &Secret, common_point: &Public, nodes_shadows: I) -> Result where I: Iterator { let mut joint_shadow = compute_secret_sum(nodes_shadows)?; joint_shadow.mul(access_key)?; @@ -319,7 +318,7 @@ pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> } /// Compute signature share. -pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, mut other_nodes_numbers: I) +pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) -> Result where I: Iterator { let mut sum = one_time_secret_coeff.clone(); let mut addendum = compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers)?; @@ -329,7 +328,7 @@ pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_co } /// Check signature share. -pub fn check_signature_share<'a, I>(combined_hash: &Secret, signature_share: &Secret, public_share: &Public, one_time_public_share: &Public, node_number: &Secret, mut other_nodes_numbers: I) +pub fn check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_number: &Secret, _other_nodes_numbers: I) -> Result where I: Iterator { Ok(true) /* TODO: fix with odd-of-N @@ -366,6 +365,7 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S Ok((combined_hash, sig)) } +#[cfg(test)] /// Verify signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { let mut addendum = math::generation_point(); @@ -380,6 +380,7 @@ pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_h #[cfg(test)] pub mod tests { + use std::iter::once; use ethkey::KeyPair; use super::*; diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index 9e41d0b3009..e4ae27b2ee5 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -32,8 +32,6 @@ pub enum Message { Generation(GenerationMessage), /// Encryption message. Encryption(EncryptionMessage), - /// Consensus message. - Consensus(ConsensusMessage), /// Decryption message. Decryption(DecryptionMessage), /// Signing message. @@ -469,7 +467,6 @@ impl fmt::Display for Message { Message::Cluster(ref message) => write!(f, "Cluster.{}", message), Message::Generation(ref message) => write!(f, "Generation.{}", message), Message::Encryption(ref message) => write!(f, "Encryption.{}", message), - Message::Consensus(ref message) => write!(f, "Consensus.{}", message), Message::Decryption(ref message) => write!(f, "Decryption.{}", message), Message::Signing(ref message) => write!(f, "Signing.{}", message), } @@ -523,7 +520,7 @@ impl fmt::Display for ConsensusMessage { impl fmt::Display for DecryptionMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { - DecryptionMessage::DecryptionConsensusMessage(_) => write!(f, "DecryptionConsensusMessage"), + DecryptionMessage::DecryptionConsensusMessage(ref m) => write!(f, "DecryptionConsensusMessage.{}", m.message), DecryptionMessage::RequestPartialDecryption(_) => write!(f, "RequestPartialDecryption"), DecryptionMessage::PartialDecryption(_) => write!(f, "PartialDecryption"), DecryptionMessage::DecryptionSessionError(_) => write!(f, "DecryptionSessionError"), @@ -534,6 +531,13 @@ impl fmt::Display for DecryptionMessage { impl fmt::Display for SigningMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - unimplemented!() + match *self { + SigningMessage::SigningConsensusMessage(ref m) => write!(f, "SigningConsensusMessage.{}", m.message), + SigningMessage::SigningGenerationMessage(ref m) => write!(f, "SigningGenerationMessage.{}", m.message), + SigningMessage::RequestPartialSignature(_) => write!(f, "RequestPartialSignature"), + SigningMessage::PartialSignature(_) => write!(f, "PartialSignature"), + SigningMessage::SigningSessionError(_) => write!(f, "SigningSessionError"), + SigningMessage::SigningSessionCompleted(_) => write!(f, "SigningSessionCompleted"), + } } } diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 7341c976ac3..6b0e54e43a4 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -14,12 +14,6 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -#![allow(unused_mut)] // TODO: remove me -#![allow(dead_code)] // TODO: remove me -#![allow(unused_imports)] // TODO: remove me -#![allow(unused_variables)] // TODO: remove me -#![allow(unreachable_code)] // TODO: remove me - use std::fmt; use std::io::Error as IoError; use ethkey; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 2d0dd947a80..45797a3fa99 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -16,13 +16,11 @@ use std::collections::{BTreeSet, VecDeque}; use std::mem::swap; -use std::cmp::{Ordering, Ord, PartialOrd}; use std::ops::DerefMut; use std::sync::Arc; -use std::time; use parking_lot::{Mutex, Condvar}; use ethkey::{self, Public, Secret, Signature}; -use util::{self, H256}; +use util::H256; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::ClusterSession; @@ -30,7 +28,7 @@ use key_server_cluster::consensus::Consensus; use key_server_cluster::consensus_session::{ConsensusSession, AclConsensusChecker, SessionParams as ConsensusSessionParams, SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - SessionState as GenerationSessionState, Session as GenerationSessionApi}; + Session as GenerationSessionApi}; use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage, SigningSessionError}; @@ -395,7 +393,7 @@ impl SessionImpl { // calculate partial signature let session_joint_public = data.session_joint_public.as_ref().expect("we are in SessionKeyGenerated state; public is generated during SessionKeyGenerating; qed"); let session_secret_coeff = data.session_secret_coeff.as_ref().expect("we are in SessionKeyGenerated state; coeff is generated during SessionKeyGenerating; qed"); - let mut nodes: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).filter(|n| n != &self.self_node_id).collect(); + let nodes: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).filter(|n| n != &self.self_node_id).collect(); let partial_signature = SessionImpl::do_partial_signing(&self.self_node_id, &message.message_hash.clone().into(), &self.encrypted_data, &nodes, session_joint_public, session_secret_coeff)?; self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { @@ -538,7 +536,7 @@ impl SessionImpl { }); // start generation session - let result = generation_session.initialize(Public::default(), // doesn't matter + generation_session.initialize(Public::default(), // doesn't matter encrypted_data.threshold, selected_nodes.clone())?; data.generation_cluster = Some(generation_cluster); data.generation_session = Some(generation_session); @@ -798,16 +796,16 @@ fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare mod tests { use std::sync::Arc; use std::collections::{BTreeMap, VecDeque}; - use ethkey::{Random, Generator, Public, Signature, sign}; + use ethkey::{Random, Generator, Public, sign}; use util::H256; use super::super::super::acl_storage::tests::DummyAclStorage; - use key_server_cluster::{NodeId, SessionId, Error, DummyKeyStorage, KeyStorage}; - use key_server_cluster::cluster::tests::{DummyCluster, make_clusters, run_clusters, loop_until, all_connections_established}; + use key_server_cluster::{NodeId, SessionId, Error, KeyStorage}; + use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::generation_session::{Session as GenerationSession}; use key_server_cluster::generation_session::tests::MessageLoop as KeyGenerationMessageLoop; use key_server_cluster::math; use key_server_cluster::message::{Message, SigningMessage}; - use key_server_cluster::signing_session::{Session, SessionImpl, SessionState, SessionParams}; + use key_server_cluster::signing_session::{Session, SessionImpl, SessionParams}; struct Node { pub cluster: Arc, @@ -856,14 +854,6 @@ mod tests { &self.nodes.values().nth(0).unwrap().session } - pub fn first_slave(&self) -> &SessionImpl { - &self.nodes.values().nth(1).unwrap().session - } - - pub fn second_slave(&self) -> &SessionImpl { - &self.nodes.values().nth(2).unwrap().session - } - pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { self.nodes.values() .filter_map(|n| n.cluster.take_message().map(|m| (n.session.node().clone(), m.0, m.1))) @@ -890,11 +880,6 @@ mod tests { Err(err) => Err(err), } } - - pub fn take_and_process_message(&mut self) -> Result<(), Error> { - let msg = self.take_message().unwrap(); - self.process_message(msg) - } } #[test] From 6999957869a14b22fe3b22d797a232ab8976caf5 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 13:37:34 +0300 Subject: [PATCH 23/45] fixed possible race --- .../key_server_cluster/cluster_sessions.rs | 70 ++++++++----------- .../key_server_cluster/decryption_session.rs | 6 +- .../src/key_server_cluster/message.rs | 2 +- secret_store/src/key_server_cluster/mod.rs | 4 +- 4 files changed, 36 insertions(+), 46 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 2e6b02943b9..66b0d41e971 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -153,32 +153,30 @@ impl ClusterSessions { /// Create new generation session. pub fn new_generation_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - // check that there's no active encryption session with the same id - if self.generation_sessions.contains(&session_id) { // TODO: possible race here and below - return Err(Error::DuplicateSessionId); - } // check that there's no finished encryption session with the same id if self.key_storage.contains(&session_id) { return Err(Error::DuplicateSessionId); } - // communicating to all other nodes is crucial for encryption session // => check that we have connections to all cluster nodes if self.nodes.iter().any(|n| !cluster.is_connected(n)) { return Err(Error::NodeDisconnected); } - let session = self.generation_sessions.insert(master, session_id, cluster.clone(), GenerationSessionImpl::new(GenerationSessionParams { - id: session_id.clone(), - self_node_id: self.self_node_id.clone(), - key_storage: Some(self.key_storage.clone()), - cluster: cluster, - })); - if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { - session.simulate_faulty_behaviour(); - } - - Ok(session) + // check that there's no active encryption session with the same id + self.generation_sessions.insert(master, session_id, cluster.clone(), move || + Ok(GenerationSessionImpl::new(GenerationSessionParams { + id: session_id.clone(), + self_node_id: self.self_node_id.clone(), + key_storage: Some(self.key_storage.clone()), + cluster: cluster, + }))) + .map(|session| { + if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { + session.simulate_faulty_behaviour(); + } + session + }) } /// Send generation session error. @@ -195,10 +193,6 @@ impl ClusterSessions { /// Create new encryption session. pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - if self.encryption_sessions.contains(&session_id) { - return Err(Error::DuplicateSessionId); - } - // some of nodes, which were generating the key may be down // => do not use these in encryption session let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; @@ -208,13 +202,13 @@ impl ClusterSessions { encrypted_data.id_numbers.remove(&disconnected_node); } - Ok(self.encryption_sessions.insert(master, session_id, cluster.clone(), EncryptionSessionImpl::new(EncryptionSessionParams { + self.encryption_sessions.insert(master, session_id, cluster.clone(), move || EncryptionSessionImpl::new(EncryptionSessionParams { id: session_id.clone(), self_node_id: self.self_node_id.clone(), encrypted_data: encrypted_data, key_storage: self.key_storage.clone(), cluster: cluster, - })?)) + })) } /// Send encryption session error. @@ -232,9 +226,6 @@ impl ClusterSessions { /// Create new decryption session. pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { let session_id = DecryptionSessionId::new(session_id, sub_session_id); - if self.decryption_sessions.contains(&session_id) { - return Err(Error::DuplicateSessionId); - } // some of nodes, which were encrypting secret may be down // => do not use these in decryption session @@ -245,14 +236,14 @@ impl ClusterSessions { encrypted_data.id_numbers.remove(&disconnected_node); } - Ok(self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), DecryptionSessionImpl::new(DecryptionSessionParams { + self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), move || DecryptionSessionImpl::new(DecryptionSessionParams { id: session_id.id, access_key: session_id.access_key, self_node_id: self.self_node_id.clone(), encrypted_data: encrypted_data, acl_storage: self.acl_storage.clone(), cluster: cluster, - })?)) + })) } /// Send decryption session error. @@ -276,10 +267,6 @@ impl ClusterSessions { /// Create new signing session. pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { let session_id = SigningSessionId::new(session_id, sub_session_id); - if self.signing_sessions.contains(&session_id) { - return Err(Error::DuplicateSessionId); - } - // some of nodes, which were encrypting secret may be down // => do not use these in signing session let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; @@ -289,14 +276,14 @@ impl ClusterSessions { encrypted_data.id_numbers.remove(&disconnected_node); } - Ok(self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), SigningSessionImpl::new(SigningSessionParams { + self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), move || SigningSessionImpl::new(SigningSessionParams { id: session_id.id, access_key: session_id.access_key, self_node_id: self.self_node_id.clone(), encrypted_data: encrypted_data, acl_storage: self.acl_storage.clone(), cluster: cluster, - })?)) + })) } /// Send signing session error. @@ -341,16 +328,17 @@ impl ClusterSessionsContainer where K: Clone + Ord, V: Cluster } } - pub fn contains(&self, session_id: &K) -> bool { - self.sessions.read().contains_key(session_id) - } - pub fn get(&self, session_id: &K) -> Option> { self.sessions.read().get(session_id).map(|s| s.session.clone()) } - pub fn insert(&self, master: NodeId, session_id: K, cluster: Arc, session: V) -> Arc { - let session = Arc::new(session); + pub fn insert Result>(&self, master: NodeId, session_id: K, cluster: Arc, session: F) -> Result, Error> { + let mut sessions = self.sessions.write(); + if sessions.contains_key(&session_id) { + return Err(Error::DuplicateSessionId); + } + + let session = Arc::new(session()?); let queued_session = QueuedSession { master: master, cluster_view: cluster, @@ -358,8 +346,8 @@ impl ClusterSessionsContainer where K: Clone + Ord, V: Cluster session: session.clone(), queue: VecDeque::new(), }; - self.sessions.write().insert(session_id, queued_session); - session + sessions.insert(session_id, queued_session); + Ok(session) } pub fn remove(&self, session_id: &K) { diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 5006594cd6c..8a2ca404136 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -465,7 +465,7 @@ impl SessionImpl { cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { session: session_id.clone().into(), sub_session: access_key.clone().into(), - is_shadow_decryption: data.is_shadow_decryption.expect("TODO"), + is_shadow_decryption: data.is_shadow_decryption.expect("is_shadow_decryption on master node is filled in initialization; we are on master node; qed"), nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), })))?; } @@ -487,7 +487,9 @@ impl SessionImpl { fn do_decryption(access_key: Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // decrypt the secret using shadow points - let job_responses = data.consensus.as_ref().expect("TODO").job_responses()?; + let job_responses = data.consensus.as_ref() + .expect("consesus is filled in initialization phase; decryption phase follows initialization phase") + .job_responses()?; let joint_shadow_point = math::compute_joint_shadow_point(job_responses.values().map(|s| &s.shadow_point))?; let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index e4ae27b2ee5..ba033b2c443 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -298,7 +298,7 @@ pub struct RequestPartialSignature { pub sub_session: SerializableSecret, /// Message hash. pub message_hash: SerializableMessageHash, - /// Selected nodes. TODO: this information is known from generation session - reuse + /// Selected nodes. pub nodes: BTreeSet, } diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 6b0e54e43a4..3ba285946ec 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -66,7 +66,7 @@ pub enum Error { /// Current state of encryption/decryption session does not allow to proceed request. /// This means that either there is some comm-failure or node is misbehaving/cheating. InvalidStateForRequest, - /// TODO + /// Request cannot be sent/received from this node. InvalidNodeForRequest, /// Message or some data in the message was recognized as invalid. /// This means that node is misbehaving/cheating. @@ -119,7 +119,7 @@ impl fmt::Display for Error { Error::InvalidThreshold => write!(f, "invalid threshold value has been passed"), Error::TooEarlyForRequest => write!(f, "session is not yet ready to process this request"), Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), - Error::InvalidNodeForRequest => write!(f, "node cannot respond to this request"), + Error::InvalidNodeForRequest => write!(f, "invalid node for this request"), Error::InvalidMessage => write!(f, "invalid message is received"), Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), From 7497654d51adb5079301f89ed0060792c571d1db Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 14:03:39 +0300 Subject: [PATCH 24/45] ignore previous jobs responses --- .../src/key_server_cluster/consensus.rs | 33 ++++++++++++------- .../key_server_cluster/decryption_session.rs | 11 +++++-- .../src/key_server_cluster/message.rs | 8 +++++ .../src/key_server_cluster/signing_session.rs | 10 +++--- 4 files changed, 43 insertions(+), 19 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index a3b8aa0b656..8e0cd374e84 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -14,11 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -// TODO: when job requests sent && we restart, we could receive previous responses => there should be some kind of control (random secret in each group of jobs) - use std::fmt::Debug; use std::collections::{BTreeSet, BTreeMap}; +use ethkey::Secret; use key_server_cluster::{Error, NodeId}; +use key_server_cluster::math; #[derive(Debug, Clone)] /// Consensus. @@ -53,6 +53,8 @@ pub struct ConsensusCore { pub struct ActiveConsensus { /// Consensus core data. core: ConsensusCore, + /// Selection key. + selection_key: Option, /// Selected nodes. selected_nodes: BTreeSet, /// Active job requests to confirmed nodes. @@ -174,7 +176,7 @@ impl Consensus where T: Debug + Clone { } /// Select nodes for completing their jobs. - pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { + pub fn select_nodes(&mut self) -> Result<(&Secret, &BTreeSet), Error> { match *self { Consensus::Active(ref mut consensus) => consensus.select_nodes(), _ => Err(Error::InvalidStateForRequest), @@ -182,7 +184,7 @@ impl Consensus where T: Debug + Clone { } /// Get nodes, select nodes for completing their jobs. - pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { + pub fn selected_nodes(&self) -> Result<(&Secret, &BTreeSet), Error> { match *self { Consensus::Active(ref consensus) => consensus.selected_nodes(), _ => Err(Error::InvalidStateForRequest), @@ -198,10 +200,10 @@ impl Consensus where T: Debug + Clone { } /// When job response is received from the node. - pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { + pub fn job_response_received(&mut self, node: &NodeId, selection_key: &Secret, response: T) -> Result<(), Error> { let completed_consensus = match *self { Consensus::Active(ref mut consensus) => { - consensus.job_response_received(node, response)?; + consensus.job_response_received(node, selection_key, response)?; if consensus.responses.len() != consensus.core.threshold + 1 { return Ok(()); } @@ -338,6 +340,7 @@ impl ActiveConsensus where T: Debug + Clone { pub fn new(core: ConsensusCore) -> Self { ActiveConsensus { core: core, + selection_key: None, selected_nodes: BTreeSet::new(), active_requests: BTreeSet::new(), responses: BTreeMap::new(), @@ -345,22 +348,24 @@ impl ActiveConsensus where T: Debug + Clone { } /// Select nodes to make job. - pub fn select_nodes(&mut self) -> Result<&BTreeSet, Error> { + pub fn select_nodes(&mut self) -> Result<(&Secret, &BTreeSet), Error> { if !self.selected_nodes.is_empty() { return Err(Error::InvalidStateForRequest); } + // TODO: possibly optimize by including this node in selected_nodes list + self.selection_key = Some(math::generate_random_scalar()?); self.selected_nodes = self.core.confirmed_nodes.iter().cloned().take(self.core.threshold + 1).collect(); - Ok(&self.selected_nodes) + Ok((self.selection_key.as_ref().expect("filled couple of lines above"), &self.selected_nodes)) } /// Get nodes, selected nodes to make their job. - pub fn selected_nodes(&self) -> Result<&BTreeSet, Error> { - if self.selected_nodes.is_empty() { + pub fn selected_nodes(&self) -> Result<(&Secret, &BTreeSet), Error> { + if self.selection_key.is_none() || self.selected_nodes.is_empty() { return Err(Error::InvalidStateForRequest); } - Ok(&self.selected_nodes) + Ok((self.selection_key.as_ref().expect("checked couple of lines above"), &self.selected_nodes)) } /// When job request is sent to the node. @@ -380,7 +385,11 @@ impl ActiveConsensus where T: Debug + Clone { } /// When job response is received from the node. - pub fn job_response_received(&mut self, node: &NodeId, response: T) -> Result<(), Error> { + pub fn job_response_received(&mut self, node: &NodeId, selection_key: &Secret, response: T) -> Result<(), Error> { + if self.selection_key.as_ref() != Some(selection_key) { + // response from previous request => ignore + return Ok(()); + } if !self.active_requests.remove(node) { return Err(Error::InvalidStateForRequest); } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 8a2ca404136..8a7ece9fa39 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -323,6 +323,7 @@ impl SessionImpl { self.cluster.send(&sender, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), + request_id: message.request_id.clone(), shadow_point: decryption_result.shadow_point.into(), decrypt_shadow: decryption_result.decrypt_shadow, })))?; @@ -349,7 +350,7 @@ impl SessionImpl { // remember partial signature { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; - consensus.job_response_received(&sender, PartialDecryptionResult { + consensus.job_response_received(&sender, &message.request_id.clone().into(), PartialDecryptionResult { shadow_point: message.shadow_point.clone().into(), decrypt_shadow: message.decrypt_shadow.clone(), })?; @@ -457,7 +458,7 @@ impl SessionImpl { // send jobs to all selected nodes let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial decryption phase follows initialization; qed"); consensus.activate()?; - let mut confirmed_nodes = consensus.select_nodes()?.clone(); + let (request_id, mut confirmed_nodes) = consensus.select_nodes().map(|(r, n)| (r.clone(), n.clone()))?; // send requests for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { @@ -465,6 +466,7 @@ impl SessionImpl { cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { session: session_id.clone().into(), sub_session: access_key.clone().into(), + request_id: request_id.clone().into(), is_shadow_decryption: data.is_shadow_decryption.expect("is_shadow_decryption on master node is filled in initialization; we are on master node; qed"), nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), })))?; @@ -479,7 +481,7 @@ impl SessionImpl { }; consensus.job_request_sent(self_node_id)?; - consensus.job_response_received(self_node_id, decryption_result)?; + consensus.job_response_received(self_node_id, &request_id, decryption_result)?; } Ok(()) @@ -858,6 +860,7 @@ mod tests { assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node().clone(), &message::RequestPartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), + request_id: Random.generate().unwrap().secret().clone().into(), is_shadow_decryption: false, nodes: sessions.iter().map(|s| s.node().clone().into()).take(4).collect(), }).unwrap_err(), Error::InvalidMessage); @@ -876,6 +879,7 @@ mod tests { assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node().clone(), &message::RequestPartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), + request_id: Random.generate().unwrap().secret().clone().into(), is_shadow_decryption: false, nodes: sessions.iter().map(|s| s.node().clone().into()).take(2).collect(), }).unwrap_err(), Error::InvalidMessage); @@ -887,6 +891,7 @@ mod tests { assert_eq!(sessions[0].on_partial_decryption(sessions[1].node().clone(), &message::PartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), + request_id: Random.generate().unwrap().secret().clone().into(), shadow_point: Random.generate().unwrap().public().clone().into(), decrypt_shadow: None, }).unwrap_err(), Error::InvalidStateForRequest); diff --git a/secret_store/src/key_server_cluster/message.rs b/secret_store/src/key_server_cluster/message.rs index ba033b2c443..b18cf512fed 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -296,6 +296,8 @@ pub struct RequestPartialSignature { pub session: MessageSessionId, /// Signing session Id. pub sub_session: SerializableSecret, + /// Request id. + pub request_id: SerializableSecret, /// Message hash. pub message_hash: SerializableMessageHash, /// Selected nodes. @@ -309,6 +311,8 @@ pub struct PartialSignature { pub session: MessageSessionId, /// Signing session Id. pub sub_session: SerializableSecret, + /// Request id. + pub request_id: SerializableSecret, /// S part of signature. pub partial_signature: SerializableSecret, } @@ -351,6 +355,8 @@ pub struct RequestPartialDecryption { pub session: MessageSessionId, /// Decryption session Id. pub sub_session: SerializableSecret, + /// Request id. + pub request_id: SerializableSecret, /// Is shadow decryption requested? When true, decryption result /// will be visible to the owner of requestor public key only. pub is_shadow_decryption: bool, @@ -365,6 +371,8 @@ pub struct PartialDecryption { pub session: MessageSessionId, /// Decryption session Id. pub sub_session: SerializableSecret, + /// Request id. + pub request_id: SerializableSecret, /// Partially decrypted secret. pub shadow_point: SerializablePublic, /// Decrypt shadow coefficient (if requested), encrypted with requestor public. diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 45797a3fa99..ee8278c86c9 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -399,6 +399,7 @@ impl SessionImpl { self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), + request_id: message.request_id.clone(), partial_signature: partial_signature.into(), })))?; @@ -424,7 +425,7 @@ impl SessionImpl { // remember partial signature { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; - consensus.job_response_received(&sender, message.partial_signature.clone().into())?; + consensus.job_response_received(&sender, &message.request_id.clone().into(), message.partial_signature.clone().into())?; // check if we have enough shadow points to decrypt the secret if !consensus.is_completed() { @@ -524,7 +525,7 @@ impl SessionImpl { // select nodes to make signature let mut consensus = data.consensus.as_mut().expect("consensus is filled during initialization phase; key generation phase follows initialization; qed"); consensus.activate()?; - let selected_nodes = consensus.select_nodes()?; + let (_, selected_nodes) = consensus.select_nodes()?; // create generation session let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); @@ -600,7 +601,7 @@ impl SessionImpl { // send jobs to all selected nodes let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial signing phase follows initialization; qed"); - let mut confirmed_nodes = consensus.selected_nodes()?.clone(); + let (request_id, mut confirmed_nodes) = consensus.selected_nodes().map(|(r, n)| (r.clone(), n.clone()))?; // send requests let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); @@ -609,6 +610,7 @@ impl SessionImpl { cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { session: session_id.clone().into(), sub_session: access_key.clone().into(), + request_id: request_id.clone().into(), message_hash: message_hash.clone().into(), nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), })))?; @@ -623,7 +625,7 @@ impl SessionImpl { }; consensus.job_request_sent(&self_node_id)?; - consensus.job_response_received(&self_node_id, signing_result)?; + consensus.job_response_received(&self_node_id, &request_id, signing_result)?; } Ok(()) From 340c357c43c9023b61a4590b6da634a35d6eccfa Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 14:18:04 +0300 Subject: [PATCH 25/45] include sef node in consensus when confirmed --- .../src/key_server_cluster/consensus.rs | 18 +++++++++++++----- .../key_server_cluster/decryption_session.rs | 2 +- .../src/key_server_cluster/signing_session.rs | 2 +- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index 8e0cd374e84..f5d89e0a0de 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -176,9 +176,9 @@ impl Consensus where T: Debug + Clone { } /// Select nodes for completing their jobs. - pub fn select_nodes(&mut self) -> Result<(&Secret, &BTreeSet), Error> { + pub fn select_nodes(&mut self, self_node_id: &NodeId) -> Result<(&Secret, &BTreeSet), Error> { match *self { - Consensus::Active(ref mut consensus) => consensus.select_nodes(), + Consensus::Active(ref mut consensus) => consensus.select_nodes(self_node_id), _ => Err(Error::InvalidStateForRequest), } } @@ -348,14 +348,22 @@ impl ActiveConsensus where T: Debug + Clone { } /// Select nodes to make job. - pub fn select_nodes(&mut self) -> Result<(&Secret, &BTreeSet), Error> { + pub fn select_nodes(&mut self, self_node_id: &NodeId) -> Result<(&Secret, &BTreeSet), Error> { if !self.selected_nodes.is_empty() { return Err(Error::InvalidStateForRequest); } - // TODO: possibly optimize by including this node in selected_nodes list self.selection_key = Some(math::generate_random_scalar()?); - self.selected_nodes = self.core.confirmed_nodes.iter().cloned().take(self.core.threshold + 1).collect(); + self.selected_nodes.clear(); + if self.core.confirmed_nodes.contains(self_node_id) { + self.selected_nodes.insert(self_node_id.clone()); + } + for confirmed_node in &self.core.confirmed_nodes { + self.selected_nodes.insert(confirmed_node.clone()); + if self.selected_nodes.len() == self.core.threshold + 1 { + break; + } + } Ok((self.selection_key.as_ref().expect("filled couple of lines above"), &self.selected_nodes)) } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 8a7ece9fa39..a1cfcf891d6 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -458,7 +458,7 @@ impl SessionImpl { // send jobs to all selected nodes let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial decryption phase follows initialization; qed"); consensus.activate()?; - let (request_id, mut confirmed_nodes) = consensus.select_nodes().map(|(r, n)| (r.clone(), n.clone()))?; + let (request_id, mut confirmed_nodes) = consensus.select_nodes(self_node_id).map(|(r, n)| (r.clone(), n.clone()))?; // send requests for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index ee8278c86c9..24b88aa4c3e 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -525,7 +525,7 @@ impl SessionImpl { // select nodes to make signature let mut consensus = data.consensus.as_mut().expect("consensus is filled during initialization phase; key generation phase follows initialization; qed"); consensus.activate()?; - let (_, selected_nodes) = consensus.select_nodes()?; + let (_, selected_nodes) = consensus.select_nodes(&self_node_id)?; // create generation session let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); From e2e89b65a7c4a9a0a0d78944c36775e5b235c346 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 14:24:20 +0300 Subject: [PATCH 26/45] fixed warning --- secret_store/src/key_server_cluster/math.rs | 4 ++-- secret_store/src/key_server_cluster/signing_session.rs | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index 01b956b531d..aa78956e447 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -328,7 +328,7 @@ pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_co } /// Check signature share. -pub fn check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_number: &Secret, _other_nodes_numbers: I) +pub fn _check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_number: &Secret, _other_nodes_numbers: I) -> Result where I: Iterator { Ok(true) /* TODO: fix with odd-of-N @@ -546,7 +546,7 @@ pub mod tests { .filter(|j| i != *j) .map(|j| { let signature_share = partial_signatures[j].clone(); - assert!(check_signature_share(&combined_hash, + assert!(_check_signature_share(&combined_hash, &signature_share, &artifacts.public_shares[j], &one_time_artifacts.public_shares[j], diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 24b88aa4c3e..b49b19319b9 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -422,6 +422,9 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } + // check partial signature + // TODO: check_signature_share() + // remember partial signature { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; From 1b26bd9dfafba0b0f1fef0e229da834b6cebd286 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 15:06:39 +0300 Subject: [PATCH 27/45] removed extra clones --- .../key_server_cluster/cluster_sessions.rs | 10 +-- .../key_server_cluster/decryption_session.rs | 58 +++++++++-------- .../src/key_server_cluster/signing_session.rs | 64 +++++++++---------- 3 files changed, 63 insertions(+), 69 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 66b0d41e971..19248dbfe80 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -30,7 +30,7 @@ use key_server_cluster::decryption_session::{Session as DecryptionSession, Sessi use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams, SessionState as EncryptionSessionState}; use key_server_cluster::signing_session::{Session as SigningSession, SessionImpl as SigningSessionImpl, - SigningSessionId, SessionParams as SigningSessionParams, SessionState as SigningSessionState}; + SigningSessionId, SessionParams as SigningSessionParams}; /// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, /// we must treat this session as stalled && finish it with an error. @@ -256,7 +256,7 @@ impl ClusterSessions { // => or broadcast error // do not bother processing send error, as we already processing error - if &s.master == s.session.node() { + if s.master == self.self_node_id { let _ = s.cluster_view.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); } else { let _ = s.cluster_view.send(to, Message::Decryption(DecryptionMessage::DecryptionSessionError(error))); @@ -296,7 +296,7 @@ impl ClusterSessions { // => or broadcast error // do not bother processing send error, as we already processing error - if &s.master == s.session.node() { + if s.master == self.self_node_id { let _ = s.cluster_view.broadcast(Message::Signing(SigningMessage::SigningSessionError(error))); } else { let _ = s.cluster_view.send(to, Message::Signing(SigningMessage::SigningSessionError(error))); @@ -494,10 +494,6 @@ impl SigningSessionWrapper { } impl SigningSession for SigningSessionWrapper { - fn state(&self) -> SigningSessionState { - self.session.state() - } - fn wait(&self) -> Result<(Secret, Secret), Error> { self.session.wait() } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index a1cfcf891d6..18a7060a280 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -172,16 +172,17 @@ impl SessionImpl { }) } - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id - } - /// Get current session state. pub fn state(&self) -> SessionState { self.data.lock().state.clone() } + #[cfg(test)] + /// Get this node id. + pub fn node(&self) -> &NodeId { + &self.self_node_id + } + #[cfg(test)] /// Get this session access key. pub fn access_key(&self) -> &Secret { @@ -207,9 +208,9 @@ impl SessionImpl { let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; // update state - data.master = Some(self.node().clone()); + data.master = Some(self.self_node_id.clone()); data.state = SessionState::EstablishingConsensus; - data.requestor = Some(requestor_public.clone()); + data.requestor = Some(requestor_public); data.is_shadow_decryption = Some(is_shadow_decryption); // create consensus session @@ -231,8 +232,8 @@ impl SessionImpl { // if single node is required to sign message, proceed if data.state == SessionState::EstablishedConsensus { - SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; - SessionImpl::do_decryption(self.access_key.clone(), &self.encrypted_data, &mut *data)?; + SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data)?; + SessionImpl::do_decryption(&self.access_key, &self.encrypted_data, &mut *data)?; self.completed.notify_all(); } @@ -272,8 +273,9 @@ impl SessionImpl { match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => { let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; - data.requestor = Some(requestor.clone()); - consensus_session.on_initialize_session(sender, &requestor)? + let consensus_action = consensus_session.on_initialize_session(sender, &requestor)?; + data.requestor = Some(requestor); + consensus_action }, ConsensusMessage::ConfirmConsensusInitialization(ref message) => { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; @@ -287,13 +289,13 @@ impl SessionImpl { if data.state != SessionState::EstablishedConsensus { return Ok(()); } - SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) + SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data) } pub fn on_partial_decryption_requested(&self, sender: NodeId, message: &RequestPartialDecryption) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); + debug_assert!(sender != self.self_node_id); // check message if message.nodes.len() != self.encrypted_data.threshold + 1 { @@ -318,7 +320,7 @@ impl SessionImpl { let decryption_result = { let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryptionRequest follows initialization; qed"); let nodes = message.nodes.iter().cloned().map(Into::into).collect(); - do_partial_decryption(self.node(), &requestor, message.is_shadow_decryption, &nodes, &self.access_key, &self.encrypted_data)? + do_partial_decryption(&self.self_node_id, &requestor, message.is_shadow_decryption, &nodes, &self.access_key, &self.encrypted_data)? }; self.cluster.send(&sender, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { session: self.id.clone().into(), @@ -338,7 +340,7 @@ impl SessionImpl { pub fn on_partial_decryption(&self, sender: NodeId, message: &PartialDecryption) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); + debug_assert!(sender != self.self_node_id); let mut data = self.data.lock(); @@ -368,7 +370,7 @@ impl SessionImpl { })))?; // do decryption - SessionImpl::do_decryption(self.access_key.clone(), &self.encrypted_data, &mut *data)?; + SessionImpl::do_decryption(&self.access_key, &self.encrypted_data, &mut *data)?; self.completed.notify_all(); Ok(()) @@ -378,7 +380,7 @@ impl SessionImpl { pub fn on_session_completed(&self, sender: NodeId, message: &DecryptionSessionCompleted) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); + debug_assert!(sender != self.self_node_id); let mut data = self.data.lock(); @@ -401,7 +403,7 @@ impl SessionImpl { pub fn on_session_error(&self, sender: NodeId, message: &DecryptionSessionError) -> Result<(), Error> { let mut data = self.data.lock(); - warn!("{}: decryption session failed with error: {:?} from {}", self.node(), message.error, sender); + warn!("{}: decryption session failed with error: {:?} from {}", &self.self_node_id, message.error, sender); data.state = SessionState::Failed; data.decrypted_secret = Some(Err(Error::Io(message.error.clone()))); @@ -445,7 +447,7 @@ impl SessionImpl { Ok(()) } - fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: &SessionId, access_key: &Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { if data.master.as_ref() != Some(self_node_id) { // if we are on the slave node, wait for partial decryption requests data.state = SessionState::WaitingForPartialDecryptionRequest; @@ -477,7 +479,7 @@ impl SessionImpl { let decryption_result = { let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); - do_partial_decryption(self_node_id, &requestor, is_shadow_decryption, &confirmed_nodes, &access_key, &encrypted_data)? + do_partial_decryption(self_node_id, &requestor, is_shadow_decryption, &confirmed_nodes, access_key, &encrypted_data)? }; consensus.job_request_sent(self_node_id)?; @@ -487,7 +489,7 @@ impl SessionImpl { Ok(()) } - fn do_decryption(access_key: Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + fn do_decryption(access_key: &Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { // decrypt the secret using shadow points let job_responses = data.consensus.as_ref() .expect("consesus is filled in initialization phase; decryption phase follows initialization phase") @@ -495,7 +497,7 @@ impl SessionImpl { let joint_shadow_point = math::compute_joint_shadow_point(job_responses.values().map(|s| &s.shadow_point))?; let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); - let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, &access_key, encrypted_point, &joint_shadow_point)?; + let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, access_key, encrypted_point, &joint_shadow_point)?; let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; decryption follows initialization; qed"); let (common_point, decrypt_shadows) = if is_shadow_decryption { ( @@ -530,7 +532,7 @@ impl ClusterSession for SessionImpl { fn on_node_timeout(&self, node: &NodeId) { let mut data = self.data.lock(); - let is_self_master = data.master.as_ref() == Some(self.node()); + let is_self_master = data.master.as_ref() == Some(&self.self_node_id); let is_other_master = data.master.as_ref() == Some(node); // if this is master node, we might have to restart if is_self_master { @@ -543,7 +545,7 @@ impl ClusterSession for SessionImpl { }, }; if is_restart_required { - if SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + if SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { return; } } @@ -554,7 +556,7 @@ impl ClusterSession for SessionImpl { } // else: disconnecting from master node means failure - warn!("{}: decryption session failed because {} connection has timeouted", self.node(), node); + warn!("{}: decryption session failed because {} connection has timeouted", &self.self_node_id, node); data.state = SessionState::Failed; data.decrypted_secret = Some(Err(Error::NodeDisconnected)); @@ -564,7 +566,7 @@ impl ClusterSession for SessionImpl { fn on_session_timeout(&self) { let mut data = self.data.lock(); - let is_self_master = data.master.as_ref() == Some(self.node()); + let is_self_master = data.master.as_ref() == Some(&self.self_node_id); // if this is master node, we might have to restart if is_self_master { let is_restart_required = match data.consensus.as_mut() { @@ -575,13 +577,13 @@ impl ClusterSession for SessionImpl { }, }; if is_restart_required { - if SessionImpl::start_waiting_for_partial_decryption(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + if SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { return; } } } - warn!("{}: decryption session failed with timeout", self.node()); + warn!("{}: decryption session failed with timeout", &self.self_node_id); data.state = SessionState::Failed; data.decrypted_secret = Some(Err(Error::NodeDisconnected)); diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index b49b19319b9..e520bbf3d6c 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -37,8 +37,6 @@ pub use key_server_cluster::decryption_session::DecryptionSessionId as SigningSe /// Signing session API. pub trait Session: Send + Sync + 'static { - /// Get session state. - fn state(&self) -> SessionState; /// Wait until session is completed. Returns signed message. fn wait(&self) -> Result<(Secret, Secret), Error>; } @@ -190,9 +188,9 @@ impl SessionImpl { }) } - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id + /// Get current session state. + pub fn state(&self) -> SessionState { + self.data.lock().state.clone() } /// Initialize signing session. @@ -209,8 +207,8 @@ impl SessionImpl { // update state data.state = SessionState::EstablishingConsensus; - data.master = Some(self.node().clone()); - data.requestor = Some(requestor_public.clone()); + data.master = Some(self.self_node_id.clone()); + data.requestor = Some(requestor_public); data.message_hash = Some(message_hash); // create consensus session @@ -232,9 +230,9 @@ impl SessionImpl { // if single node is required to sign message, proceed if data.state == SessionState::EstablishedConsensus { - SessionImpl::start_generating_session_key(self.self_node_id.clone(), &self.encrypted_data, &self.cluster, &mut *data)?; + SessionImpl::start_generating_session_key(&self.self_node_id, &self.encrypted_data, &self.cluster, &mut *data)?; SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; - SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data)?; + SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data)?; SessionImpl::do_signing(&mut *data)?; self.completed.notify_all(); } @@ -275,8 +273,9 @@ impl SessionImpl { match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => { let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; - data.requestor = Some(requestor.clone()); - consensus_session.on_initialize_session(sender, &requestor)? + let consensus_action = consensus_session.on_initialize_session(sender, &requestor)?; + data.requestor = Some(requestor); + consensus_action }, ConsensusMessage::ConfirmConsensusInitialization(ref message) => { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; @@ -291,7 +290,7 @@ impl SessionImpl { return Ok(()); } - SessionImpl::start_generating_session_key(self.self_node_id.clone(), &self.encrypted_data, &self.cluster, &mut *data)?; + SessionImpl::start_generating_session_key(&self.self_node_id, &self.encrypted_data, &self.cluster, &mut *data)?; SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data) } @@ -368,14 +367,14 @@ impl SessionImpl { return Ok(()); } - SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data) + SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data) } /// When partial signature is requested. pub fn on_partial_signature_requested(&self, sender: NodeId, message: &RequestPartialSignature) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); + debug_assert!(sender != self.self_node_id); let mut data = self.data.lock(); @@ -399,7 +398,7 @@ impl SessionImpl { self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), - request_id: message.request_id.clone(), + request_id: message.request_id.clone().into(), partial_signature: partial_signature.into(), })))?; @@ -413,7 +412,7 @@ impl SessionImpl { pub fn on_partial_signature(&self, sender: NodeId, message: &PartialSignature) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); + debug_assert!(sender != self.self_node_id); let mut data = self.data.lock(); @@ -453,7 +452,7 @@ impl SessionImpl { pub fn on_session_completed(&self, sender: NodeId, message: &SigningSessionCompleted) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(self.access_key == *message.sub_session); - debug_assert!(&sender != self.node()); + debug_assert!(sender != self.self_node_id); let mut data = self.data.lock(); @@ -476,7 +475,7 @@ impl SessionImpl { pub fn on_session_error(&self, sender: NodeId, message: &SigningSessionError) -> Result<(), Error> { let mut data = self.data.lock(); - warn!("{}: signing session failed with error: {:?} from {}", self.node(), message.error, sender); + warn!("{}: signing session failed with error: {:?} from {}", self.self_node_id, message.error, sender); data.state = SessionState::Failed; data.signed_message = Some(Err(Error::Io(message.error.clone()))); @@ -521,14 +520,14 @@ impl SessionImpl { } /// Start generating one-time session key. - fn start_generating_session_key(self_node_id: NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { + fn start_generating_session_key(self_node_id: &NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { // update state data.state = SessionState::SessionKeyGeneration; // select nodes to make signature let mut consensus = data.consensus.as_mut().expect("consensus is filled during initialization phase; key generation phase follows initialization; qed"); consensus.activate()?; - let (_, selected_nodes) = consensus.select_nodes(&self_node_id)?; + let (_, selected_nodes) = consensus.select_nodes(self_node_id)?; // create generation session let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); @@ -592,7 +591,7 @@ impl SessionImpl { } /// Start waiting for partial signatures/partial signatures requests. - fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: SessionId, access_key: Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: &SessionId, access_key: &Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { if data.master.as_ref() != Some(self_node_id) { // if we are on the slave node, wait for partial signature requests data.state = SessionState::WaitingForPartialSignatureRequest; @@ -674,7 +673,7 @@ impl ClusterSession for SessionImpl { fn on_node_timeout(&self, node: &NodeId) { let mut data = self.data.lock(); - let is_self_master = data.master.as_ref() == Some(self.node()); + let is_self_master = data.master.as_ref() == Some(&self.self_node_id); let is_other_master = data.master.as_ref() == Some(node); // if this is master node, we might have to restart if is_self_master { @@ -687,7 +686,7 @@ impl ClusterSession for SessionImpl { }, }; if is_restart_required { - if SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + if SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { return; } } @@ -698,7 +697,7 @@ impl ClusterSession for SessionImpl { } // else: disconnecting from master node means failure - warn!("{}: signing session failed because {} connection has timeouted", self.node(), node); + warn!("{}: signing session failed because {} connection has timeouted", self.self_node_id, node); data.state = SessionState::Failed; data.signed_message = Some(Err(Error::NodeDisconnected)); @@ -708,7 +707,7 @@ impl ClusterSession for SessionImpl { fn on_session_timeout(&self) { let mut data = self.data.lock(); - let is_self_master = data.master.as_ref() == Some(self.node()); + let is_self_master = data.master.as_ref() == Some(&self.self_node_id); // if this is master node, we might have to restart if is_self_master { let is_restart_required = match data.consensus.as_mut() { @@ -719,13 +718,13 @@ impl ClusterSession for SessionImpl { }, }; if is_restart_required { - if SessionImpl::start_waiting_for_partial_signing(self.node(), self.id.clone(), self.access_key.clone(), &self.cluster, &self.encrypted_data, &mut *data).is_ok() { + if SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { return; } } } - warn!("{}: signing session failed with timeout", self.node()); + warn!("{}: signing session failed with timeout", self.self_node_id); data.state = SessionState::Failed; data.signed_message = Some(Err(Error::NodeDisconnected)); @@ -767,16 +766,12 @@ impl Cluster for SigningCluster { } fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - self.messages.lock().push_back((to.clone(), message.clone())); + self.messages.lock().push_back((to.clone(), message)); Ok(()) } } impl Session for SessionImpl { - fn state(&self) -> SessionState { - self.data.lock().state.clone() - } - fn wait(&self) -> Result<(Secret, Secret), Error> { let mut data = self.data.lock(); if !data.signed_message.is_some() { @@ -813,6 +808,7 @@ mod tests { use key_server_cluster::signing_session::{Session, SessionImpl, SessionParams}; struct Node { + pub node_id: NodeId, pub cluster: Arc, pub session: SessionImpl, } @@ -838,7 +834,7 @@ mod tests { acl_storage: acl_storage, cluster: cluster.clone(), }).unwrap(); - nodes.insert(gl_node_id.clone(), Node { cluster: cluster, session: session }); + nodes.insert(gl_node_id.clone(), Node { node_id: gl_node_id.clone(), cluster: cluster, session: session }); } let nodes_ids: Vec<_> = nodes.keys().cloned().collect(); @@ -861,7 +857,7 @@ mod tests { pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { self.nodes.values() - .filter_map(|n| n.cluster.take_message().map(|m| (n.session.node().clone(), m.0, m.1))) + .filter_map(|n| n.cluster.take_message().map(|m| (n.node_id.clone(), m.0, m.1))) .nth(0) .or_else(|| self.queue.pop_front()) } From acdabc59fc9ed57cd098f9524f12415ccd0ab290 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 24 May 2017 16:19:43 +0300 Subject: [PATCH 28/45] consensus_restarts_after_node_timeout --- .../src/key_server_cluster/consensus.rs | 85 ++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs index f5d89e0a0de..48afec6f228 100644 --- a/secret_store/src/key_server_cluster/consensus.rs +++ b/secret_store/src/key_server_cluster/consensus.rs @@ -309,7 +309,7 @@ impl ConsensusCore { /// When node has accepted join offer. pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { if !self.requested_nodes.remove(node) { - return Err(Error::InvalidStateForRequest); + return Err(Error::InvalidNodeForRequest); } self.confirmed_nodes.insert(node.clone()); @@ -319,7 +319,7 @@ impl ConsensusCore { /// When node has rejected join offer. pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { if !self.requested_nodes.remove(node) { - return Err(Error::InvalidStateForRequest); + return Err(Error::InvalidNodeForRequest); } self.rejected_nodes.insert(node.clone()); @@ -443,10 +443,91 @@ impl ActiveConsensus where T: Debug + Clone { #[cfg(test)] mod tests { use key_server_cluster::Error; + use key_server_cluster::math; use super::Consensus; #[test] fn consensus_is_not_created_when_not_enough_nodes() { assert_eq!(Consensus::::new(0, vec![].into_iter().collect()).unwrap_err(), Error::InvalidThreshold); } + + #[test] + fn consensus_establishing_state() { + let consensus = Consensus::::new(0, vec![math::generate_random_point().unwrap()].into_iter().collect()).unwrap(); + assert!(consensus.core().is_some()); + assert!(!consensus.is_established()); + assert!(!consensus.is_completed()); + assert!(!consensus.is_unreachable()); + } + + #[test] + fn consensus_establishing_2_of_3_succeeded() { + let nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); + let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); + consensus.offer_response(&nodes[0], true).unwrap(); + assert!(!consensus.is_established()); + consensus.offer_response(&nodes[1], true).unwrap(); + assert!(consensus.is_established()); + consensus.offer_response(&nodes[2], true).unwrap(); + assert!(consensus.is_established()); + } + + #[test] + fn consensus_establishing_2_of_3_failed() { + let nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); + let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); + consensus.offer_response(&nodes[0], false).unwrap(); + assert!(!consensus.is_established()); + assert!(!consensus.is_unreachable()); + consensus.offer_response(&nodes[1], false).unwrap(); + assert!(!consensus.is_established()); + assert!(consensus.is_unreachable()); + } + + #[test] + fn consensus_completion_2_of_3() { + let nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); + let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); + nodes.iter().map(|n| consensus.accept_offer(n).unwrap()).collect::>(); + assert!(consensus.is_established()); + + consensus.activate().unwrap(); + let (key, selected_nodes) = consensus.select_nodes(&nodes[0]).map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); + + assert!(selected_nodes.contains(&nodes[0])); + consensus.job_request_sent(&selected_nodes[0]).unwrap(); + consensus.job_request_sent(&selected_nodes[1]).unwrap(); + + consensus.job_response_received(&selected_nodes[0], &key, 1).unwrap(); + consensus.job_response_received(&selected_nodes[1], &key, 2).unwrap(); + + assert_eq!(consensus.job_responses().unwrap().values().fold(0, |i1, i2| i1 + i2), 3); + } + + #[test] + fn consensus_restarts_after_node_timeout() { + let nodes: Vec<_> = (0..4).map(|_| math::generate_random_point().unwrap()).collect(); + let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); + nodes.iter().map(|n| consensus.accept_offer(n).unwrap()).collect::>(); + assert!(consensus.is_established()); + + consensus.activate().unwrap(); + let (_, selected_nodes) = consensus.select_nodes(&nodes[0]).map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); + let unselected_nodes: Vec<_> = nodes.iter().filter(|n| !selected_nodes.contains(n)).collect(); + assert!(selected_nodes.contains(&nodes[0])); + + // when non-selected node timeouted => nothing happens + selected_nodes.iter().map(|n| consensus.job_request_sent(n)).collect::>(); + assert_eq!(consensus.node_timeouted(&unselected_nodes[0]), Ok(false)); + let (_, selected_nodes) = consensus.selected_nodes().map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); + + // when selected node timeouted => restarts + selected_nodes.iter().map(|n| consensus.job_request_sent(n)).collect::>(); + assert_eq!(consensus.node_timeouted(&selected_nodes[0]), Ok(true)); + let (_, selected_nodes) = consensus.select_nodes(&nodes[0]).map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); + + // when selected node timeouted && there are not enough nodes for consensus => fail + selected_nodes.iter().map(|n| consensus.job_request_sent(n)).collect::>(); + assert_eq!(consensus.node_timeouted(&selected_nodes[0]), Err(Error::ConsensusUnreachable)); + } } From f8646ab23ce8aca9d715b0bc9a1612fae0592458 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 25 May 2017 09:41:43 +0300 Subject: [PATCH 29/45] encrypt signature before return --- secret_store/src/http_listener.rs | 11 +++++++---- secret_store/src/key_server.rs | 19 ++++++++++++++----- secret_store/src/traits.rs | 6 +++--- secret_store/src/types/all.rs | 2 +- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 66e1f7beea5..0e356f23dab 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -27,7 +27,7 @@ use url::percent_encoding::percent_decode; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; -use types::all::{Error, Public, MessageHash, MessageSignature, NodeAddress, RequestSignature, ServerKeyId, +use types::all::{Error, Public, MessageHash, EncryptedMessageSignature, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow}; /// Key server http-requests listener. Available requests: @@ -120,7 +120,7 @@ impl DocumentKeyServer for KeyServerHttpListener where T: KeyServer + 'sta } impl MessageSigner for KeyServerHttpListener where T: KeyServer + 'static { - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { self.handler.key_server.sign_message(key_id, signature, message) } } @@ -180,8 +180,7 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { })); }, Request::SignMessage(document, signature, message_hash) => { - return_bytes(req, res, self.handler.key_server.sign_message(&document, &signature, message_hash) - .map(|s| Some(SerializableBytes(s))) + return_message_signature(req, res, self.handler.key_server.sign_message(&document, &signature, message_hash) .map_err(|err| { warn!(target: "secretstore", "SignMessage request {} has failed with: {}", req_uri, err); err @@ -208,6 +207,10 @@ fn return_server_pubic_key(req: HttpRequest, res: HttpResponse, server_public: R return_bytes(req, res, server_public.map(|k| Some(SerializablePublic(k)))) } +fn return_message_signature(req: HttpRequest, res: HttpResponse, signature: Result) { + return_bytes(req, res, signature.map(|s| Some(SerializableBytes(s)))) +} + fn return_document_key(req: HttpRequest, res: HttpResponse, document_key: Result) { return_bytes(req, res, document_key.map(|k| Some(SerializableBytes(k)))) } diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 47704d23503..1740de6b7f1 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -27,7 +27,7 @@ use super::key_storage::KeyStorage; use key_server_cluster::{math, ClusterCore}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use types::all::{Error, Public, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, - ClusterConfiguration, MessageHash, MessageSignature}; + ClusterConfiguration, MessageHash, EncryptedMessageSignature}; use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration}; /// Secret store key server implementation @@ -121,7 +121,11 @@ impl DocumentKeyServer for KeyServerImpl { } impl MessageSigner for KeyServerImpl { - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + // recover requestor' public key from signature + let public = ethkey::recover(signature, key_id) + .map_err(|_| Error::BadSignature)?; + // sign message let signing_session = self.data.lock().cluster.new_signing_session(key_id.clone(), signature.clone(), message)?; let message_signature = signing_session.wait()?; @@ -130,7 +134,11 @@ impl MessageSigner for KeyServerImpl { let mut combined_signature: Vec = Vec::with_capacity(64); combined_signature.extend_from_slice(&**message_signature.0); combined_signature.extend_from_slice(&**message_signature.1); - Ok(combined_signature.into()) + + // encrypt combined signature with requestor public key + let message_signature = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &combined_signature) + .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?; + Ok(message_signature) } } @@ -192,7 +200,7 @@ pub mod tests { use key_server_cluster::math; use util::H256; use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, MessageSignature}; + EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature}; use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; use super::KeyServerImpl; @@ -225,7 +233,7 @@ pub mod tests { } impl MessageSigner for DummyKeyServer { - fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { + fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { unimplemented!() } } @@ -371,6 +379,7 @@ pub mod tests { // sign message let message_hash = H256::from(42); let combined_signature = key_servers[0].sign_message(&server_key_id, &signature, message_hash.clone()).unwrap(); + let combined_signature = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &combined_signature).unwrap(); let signature_c = Secret::from_slice(&combined_signature[..32]); let signature_s = Secret::from_slice(&combined_signature[32..]); diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 4a5dea8fb82..33a4eff3c7a 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use types::all::{Error, Public, ServerKeyId, MessageHash, MessageSignature, RequestSignature, EncryptedDocumentKey, +use types::all::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, EncryptedDocumentKey, EncryptedDocumentKeyShadow}; /// Server key (SK) generator. @@ -65,8 +65,8 @@ pub trait MessageSigner: ServerKeyGenerator { /// `key_id` is the caller-provided identifier of generated SK. /// `signature` is `key_id`, signed with caller public key. /// `message` is the message to be signed. - /// Result is a signed message. - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; + /// Result is a signed message, encrypted with caller public key. + fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; } diff --git a/secret_store/src/types/all.rs b/secret_store/src/types/all.rs index fad2282f7c3..54fc8acaed6 100644 --- a/secret_store/src/types/all.rs +++ b/secret_store/src/types/all.rs @@ -31,7 +31,7 @@ pub type EncryptedDocumentKey = util::Bytes; /// Message hash. pub type MessageHash = util::H256; /// Message signature. -pub type MessageSignature = util::Bytes; +pub type EncryptedMessageSignature = util::Bytes; /// Request signature type. pub type RequestSignature = ethkey::Signature; /// Public key type. From 4f181c7d4244d80f2bd8d0673c23e4ce2eb55f0f Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 25 May 2017 14:15:37 +0300 Subject: [PATCH 30/45] return error text along with HTTP status --- secret_store/src/http_listener.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 0e356f23dab..8ffdd628cef 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -251,6 +251,13 @@ fn return_error(mut res: HttpResponse, err: Error) { Error::Database(_) => *res.status_mut() = HttpStatusCode::InternalServerError, Error::Internal(_) => *res.status_mut() = HttpStatusCode::InternalServerError, } + + // return error text. ignore errors when returning error + let error_text = format!("\"{}\"", err); + if let Ok(error_text) = serde_json::to_vec(&error_text) { + res.headers_mut().set(header::ContentType::json()); + let _ = res.send(&error_text); + } } fn parse_request(method: &HttpMethod, uri_path: &str) -> Request { From 92c1c20e8001f60ebf46c2a1c2f957e90ed1968d Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 29 May 2017 13:11:56 +0300 Subject: [PATCH 31/45] fix for odd-of-N (share check fails + not equal to local sign) --- ethkey/src/secret.rs | 9 +++ secret_store/src/key_server.rs | 2 +- secret_store/src/key_server_cluster/math.rs | 69 +++++++++++-------- .../src/key_server_cluster/signing_session.rs | 12 ++-- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/ethkey/src/secret.rs b/ethkey/src/secret.rs index de35d6b0451..98296268467 100644 --- a/ethkey/src/secret.rs +++ b/ethkey/src/secret.rs @@ -92,6 +92,15 @@ impl Secret { Ok(()) } + /// Inplace negate secret key (-scalar) + pub fn neg(&mut self) -> Result<(), Error> { + let mut key_secret = self.to_secp256k1_secret()?; + key_secret.mul_assign(&SECP256K1, &key::MINUS_ONE_KEY)?; + + *self = key_secret.into(); + Ok(()) + } + /// Inplace inverse secret key (1 / scalar) pub fn inv(&mut self) -> Result<(), Error> { let mut key_secret = self.to_secp256k1_secret()?; diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 1740de6b7f1..76cb4131855 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -384,7 +384,7 @@ pub mod tests { let signature_s = Secret::from_slice(&combined_signature[32..]); // check signature - assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); + assert_eq!(math::verify_signature(*threshold, &server_public, &(signature_c, signature_s), &message_hash), Ok(true)); } } } diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index aa78956e447..dd7edbbf87f 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -299,7 +299,7 @@ pub fn decrypt_with_joint_secret(encrypted_point: &Public, common_point: &Public } /// Combine message hash with public key X coordinate. -pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> Result { +pub fn combine_message_hash_with_public(threshold: usize, message_hash: &H256, public: &Public) -> Result { // buffer is just [message_hash | public.x] let mut buffer = [0; 64]; buffer[0..32].copy_from_slice(&message_hash[0..32]); @@ -311,9 +311,14 @@ pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> // map hash to EC finite field value let hash: U256 = hash.into(); let hash: H256 = (hash % math::curve_order()).into(); - let hash = Secret::from_slice(&*hash); + let mut hash = Secret::from_slice(&*hash); hash.check_validity()?; + // fix for even threshold + if threshold % 2 == 0 { + hash.neg()?; + } + Ok(hash) } @@ -321,27 +326,29 @@ pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) -> Result where I: Iterator { let mut sum = one_time_secret_coeff.clone(); - let mut addendum = compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers)?; - addendum.mul(combined_hash)?; - sum.add(&addendum)?; + let mut subtrahend = compute_shadow_mul(combined_hash, node_number, other_nodes_numbers)?; + subtrahend.mul(node_secret_share)?; + sum.sub(&subtrahend)?; Ok(sum) } /// Check signature share. -pub fn _check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_number: &Secret, _other_nodes_numbers: I) +pub fn check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_number: &Secret, _other_nodes_numbers: I) -> Result where I: Iterator { + // TODO: in paper partial signature is checked using comparison: + // sig[i] * T = r[i] - c * lagrange_coeff(i) * y[i] + // => (k[i] - c * lagrange_coeff(i) * s[i]) * T = r[i] - c * lagrange_coeff(i) * y[i] + // => k[i] * T - c * lagrange_coeff(i) * s[i] * T = k[i] * T - c * lagrange_coeff(i) * y[i] + // => this means that y[i] = s[i] * T + // but when verifying signature (for t = 1), nonce public (r) is restored using following expression: + // r = (sig[0] + sig[1]) * T - c * y + // r = (k[0] - c * lagrange_coeff(0) * s[0] + k[1] - c * lagrange_coeff(1) * s[1]) * T - c * y + // r = (k[0] + k[1]) * T - c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - c * y + // r = r - c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - c * y + // => -c * y = c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T + // => -y = (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T + // => y[i] != s[i] * T Ok(true) - /* TODO: fix with odd-of-N - let mut left = math::generation_point(); - math::public_mul_secret(&mut left, signature_share)?; - - let right_coeff = compute_shadow_mul(hash, node_number, other_nodes_numbers)?; - let mut right_subtrahend = public_share.clone(); - math::public_mul_secret(&mut right_subtrahend, &right_coeff)?; - let mut right = one_time_public_share.clone(); - math::public_sub(&mut right, &right_subtrahend)?; - - Ok(left == right)*/ } /// Compute signature. @@ -355,7 +362,7 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S let mut nonce_public = math::generation_point(); math::public_mul_secret(&mut nonce_public, &nonce).unwrap(); - let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; + let combined_hash = combine_message_hash_with_public(0, message_hash, &nonce_public)?; let mut sig_subtrahend = combined_hash.clone(); sig_subtrahend.mul(secret)?; @@ -367,14 +374,19 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S #[cfg(test)] /// Verify signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. -pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { +pub fn verify_signature(threshold: usize, public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { let mut addendum = math::generation_point(); math::public_mul_secret(&mut addendum, &signature.1)?; let mut nonce_public = public.clone(); - math::public_mul_secret(&mut nonce_public, &signature.0)?; + let mut nonce_public_coeff = signature.0.clone(); + // fix for even threshold + if threshold % 2 != 0 { + nonce_public_coeff.neg()?; + } + math::public_mul_secret(&mut nonce_public, &nonce_public_coeff)?; math::public_add(&mut nonce_public, &addendum)?; - let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; + let combined_hash = combine_message_hash_with_public(threshold, message_hash, &nonce_public)?; Ok(combined_hash == signature.0) } @@ -498,12 +510,12 @@ pub mod tests { let message_hash = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); let nonce = generate_random_scalar().unwrap(); let signature = local_compute_signature(&nonce, key_pair.secret(), &message_hash).unwrap(); - assert_eq!(verify_signature(key_pair.public(), &signature, &message_hash), Ok(true)); + assert_eq!(verify_signature(0, key_pair.public(), &signature, &message_hash), Ok(true)); } #[test] fn full_signature_math_session() { - let test_cases = [(1, 4)]; + let test_cases = [(1, 4), (2, 4)]; for &(t, n) in &test_cases { // hash of the message to be signed let message_hash: Secret = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); @@ -520,10 +532,11 @@ pub mod tests { let n = t + 1; // step 1: run DKG to generate one-time secret key (nonce) - let one_time_artifacts = run_key_generation(t, n, Some(artifacts.id_numbers.clone())); + let id_numbers = artifacts.id_numbers.iter().cloned().take(n).collect(); + let one_time_artifacts = run_key_generation(t, n, Some(id_numbers)); // step 2: message hash && x coordinate of one-time public value are combined - let combined_hash = combine_message_hash_with_public(&message_hash, &one_time_artifacts.joint_public).unwrap(); + let combined_hash = combine_message_hash_with_public(t, &message_hash, &one_time_artifacts.joint_public).unwrap(); // step 3: compute signature shares let partial_signatures: Vec<_> = (0..n) @@ -546,7 +559,7 @@ pub mod tests { .filter(|j| i != *j) .map(|j| { let signature_share = partial_signatures[j].clone(); - assert!(_check_signature_share(&combined_hash, + assert!(check_signature_share(&combined_hash, &signature_share, &artifacts.public_shares[j], &one_time_artifacts.public_shares[j], @@ -571,8 +584,8 @@ pub mod tests { let nonce = compute_joint_secret(one_time_artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); let local_signature = local_compute_signature(&nonce, &master_secret, &message_hash).unwrap(); for signature in &signatures { - assert_eq!(signature, &local_signature); - assert_eq!(verify_signature(&artifacts.joint_public, signature, &message_hash), Ok(true)); + //assert_eq!(signature, &local_signature); + assert_eq!(verify_signature(t, &artifacts.joint_public, signature, &message_hash), Ok(true)); } } } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index e520bbf3d6c..019f55675a5 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -233,7 +233,7 @@ impl SessionImpl { SessionImpl::start_generating_session_key(&self.self_node_id, &self.encrypted_data, &self.cluster, &mut *data)?; SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data)?; - SessionImpl::do_signing(&mut *data)?; + SessionImpl::do_signing(&self.encrypted_data, &mut *data)?; self.completed.notify_all(); } @@ -442,7 +442,7 @@ impl SessionImpl { })))?; // do signing - SessionImpl::do_signing(&mut *data)?; + SessionImpl::do_signing(&self.encrypted_data, &mut *data)?; self.completed.notify_all(); Ok(()) @@ -638,7 +638,7 @@ impl SessionImpl { debug_assert!(!session_nodes.contains(self_node_id)); debug_assert!(session_nodes.len() == encrypted_data.threshold); - let combined_hash = math::combine_message_hash_with_public(&message_hash, &session_joint_public)?; + let combined_hash = math::combine_message_hash_with_public(encrypted_data.threshold, &message_hash, &session_joint_public)?; math::compute_signature_share( &combined_hash, &session_secret_coeff, @@ -649,12 +649,12 @@ impl SessionImpl { } /// Compute signature - fn do_signing(data: &mut SessionData) -> Result<(), Error> { + fn do_signing(encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); let session_joint_public = data.session_joint_public.as_ref().expect("session key is generated on key generation phase; signing phase follows initialization; qed"); let partial_signatures = data.consensus.as_ref().expect("consensus on master is filled in initialization phase; this is master node; qed").job_responses()?.values(); - let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; + let signature_c = math::combine_message_hash_with_public(encrypted_data.threshold, message_hash, session_joint_public)?; let signature_s = math::compute_signature(partial_signatures)?; data.signed_message = Some(Ok((signature_c, signature_s))); @@ -908,7 +908,7 @@ mod tests { // verify signature let public = gl.master().joint_public_and_secret().unwrap().unwrap().0; let signature = sl.master().wait().unwrap(); - assert!(math::verify_signature(&public, &signature, &message_hash).unwrap()); + assert!(math::verify_signature(threshold, &public, &signature, &message_hash).unwrap()); } } } From 4d1b7506b4c6cfe075892ec6360909c796b981dc Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 29 May 2017 16:57:34 +0300 Subject: [PATCH 32/45] fixed t-of-N for odd t --- secret_store/src/key_server.rs | 5 +- .../src/key_server_cluster/cluster.rs | 6 ++ secret_store/src/key_server_cluster/math.rs | 54 +++++++-------- .../src/key_server_cluster/signing_session.rs | 68 ++++++++++++------- 4 files changed, 74 insertions(+), 59 deletions(-) diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 76cb4131855..3f502ef9da5 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -367,8 +367,7 @@ pub mod tests { //::logger::init_log(); let key_servers = make_key_servers(6100, 3); - //let test_cases = [0, 1, 2]; - let test_cases = [1]; + let test_cases = [0, 1, 2]; for threshold in &test_cases { // generate server key let server_key_id = Random.generate().unwrap().secret().clone(); @@ -384,7 +383,7 @@ pub mod tests { let signature_s = Secret::from_slice(&combined_signature[32..]); // check signature - assert_eq!(math::verify_signature(*threshold, &server_public, &(signature_c, signature_s), &message_hash), Ok(true)); + assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); } } } diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 3c2fec2c689..5074daf1f12 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -640,6 +640,7 @@ impl ClusterCore { }, }; + let mut is_queued_message = false; loop { match session.clone().and_then(|session| match message { SigningMessage::SigningConsensusMessage(ref message) => @@ -670,12 +671,17 @@ impl ClusterCore { // try to dequeue message match data.sessions.signing_sessions.dequeue_message(&signing_session_id) { Some((msg_sender, msg)) => { + is_queued_message = true; sender = msg_sender; message = msg; }, None => break, } }, + Err(Error::TooEarlyForRequest) => { + data.sessions.signing_sessions.enqueue_message(&signing_session_id, sender, message, is_queued_message); + break; + }, Err(err) => { warn!(target: "secretstore_net", "{}: signing session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); data.sessions.respond_with_signing_error(&session_id, &sub_session_id, &sender, message::SigningSessionError { diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index dd7edbbf87f..047a4556c4f 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -299,7 +299,7 @@ pub fn decrypt_with_joint_secret(encrypted_point: &Public, common_point: &Public } /// Combine message hash with public key X coordinate. -pub fn combine_message_hash_with_public(threshold: usize, message_hash: &H256, public: &Public) -> Result { +pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> Result { // buffer is just [message_hash | public.x] let mut buffer = [0; 64]; buffer[0..32].copy_from_slice(&message_hash[0..32]); @@ -311,29 +311,28 @@ pub fn combine_message_hash_with_public(threshold: usize, message_hash: &H256, p // map hash to EC finite field value let hash: U256 = hash.into(); let hash: H256 = (hash % math::curve_order()).into(); - let mut hash = Secret::from_slice(&*hash); + let hash = Secret::from_slice(&*hash); hash.check_validity()?; - // fix for even threshold - if threshold % 2 == 0 { - hash.neg()?; - } - Ok(hash) } /// Compute signature share. -pub fn compute_signature_share<'a, I>(combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) +pub fn compute_signature_share<'a, I>(threshold: usize, combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) -> Result where I: Iterator { let mut sum = one_time_secret_coeff.clone(); let mut subtrahend = compute_shadow_mul(combined_hash, node_number, other_nodes_numbers)?; subtrahend.mul(node_secret_share)?; - sum.sub(&subtrahend)?; + if threshold % 2 == 0 { + sum.sub(&subtrahend)?; + } else { + sum.add(&subtrahend)?; + } Ok(sum) } /// Check signature share. -pub fn check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_number: &Secret, _other_nodes_numbers: I) +pub fn _check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_numbers: I) -> Result where I: Iterator { // TODO: in paper partial signature is checked using comparison: // sig[i] * T = r[i] - c * lagrange_coeff(i) * y[i] @@ -348,6 +347,7 @@ pub fn check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: & // => -c * y = c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T // => -y = (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T // => y[i] != s[i] * T + // => some other way is required Ok(true) } @@ -362,7 +362,7 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S let mut nonce_public = math::generation_point(); math::public_mul_secret(&mut nonce_public, &nonce).unwrap(); - let combined_hash = combine_message_hash_with_public(0, message_hash, &nonce_public)?; + let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; let mut sig_subtrahend = combined_hash.clone(); sig_subtrahend.mul(secret)?; @@ -374,19 +374,14 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S #[cfg(test)] /// Verify signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. -pub fn verify_signature(threshold: usize, public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { +pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { let mut addendum = math::generation_point(); math::public_mul_secret(&mut addendum, &signature.1)?; let mut nonce_public = public.clone(); - let mut nonce_public_coeff = signature.0.clone(); - // fix for even threshold - if threshold % 2 != 0 { - nonce_public_coeff.neg()?; - } - math::public_mul_secret(&mut nonce_public, &nonce_public_coeff)?; + math::public_mul_secret(&mut nonce_public, &signature.0)?; math::public_add(&mut nonce_public, &addendum)?; - let combined_hash = combine_message_hash_with_public(threshold, message_hash, &nonce_public)?; + let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; Ok(combined_hash == signature.0) } @@ -510,12 +505,13 @@ pub mod tests { let message_hash = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); let nonce = generate_random_scalar().unwrap(); let signature = local_compute_signature(&nonce, key_pair.secret(), &message_hash).unwrap(); - assert_eq!(verify_signature(0, key_pair.public(), &signature, &message_hash), Ok(true)); + assert_eq!(verify_signature(key_pair.public(), &signature, &message_hash), Ok(true)); } #[test] fn full_signature_math_session() { - let test_cases = [(1, 4), (2, 4)]; + let test_cases = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5), (4, 5), + (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10)]; for &(t, n) in &test_cases { // hash of the message to be signed let message_hash: Secret = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); @@ -536,11 +532,12 @@ pub mod tests { let one_time_artifacts = run_key_generation(t, n, Some(id_numbers)); // step 2: message hash && x coordinate of one-time public value are combined - let combined_hash = combine_message_hash_with_public(t, &message_hash, &one_time_artifacts.joint_public).unwrap(); + let combined_hash = combine_message_hash_with_public(&message_hash, &one_time_artifacts.joint_public).unwrap(); // step 3: compute signature shares let partial_signatures: Vec<_> = (0..n) .map(|i| compute_signature_share( + t, &combined_hash, &one_time_artifacts.polynoms1[i][0], &artifacts.secret_shares[i], @@ -559,16 +556,11 @@ pub mod tests { .filter(|j| i != *j) .map(|j| { let signature_share = partial_signatures[j].clone(); - assert!(check_signature_share(&combined_hash, + assert!(_check_signature_share(&combined_hash, &signature_share, &artifacts.public_shares[j], &one_time_artifacts.public_shares[j], - &artifacts.id_numbers[j], - artifacts.id_numbers.iter() - .enumerate() - .filter(|&(k, _)| j != k) - .map(|(_, n)| n) - .take(t)).unwrap_or(false)); + artifacts.id_numbers.iter().take(t)).unwrap_or(false)); signature_share }) .collect()) @@ -584,8 +576,8 @@ pub mod tests { let nonce = compute_joint_secret(one_time_artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); let local_signature = local_compute_signature(&nonce, &master_secret, &message_hash).unwrap(); for signature in &signatures { - //assert_eq!(signature, &local_signature); - assert_eq!(verify_signature(t, &artifacts.joint_public, signature, &message_hash), Ok(true)); + assert_eq!(signature, &local_signature); + assert_eq!(verify_signature(&artifacts.joint_public, signature, &message_hash), Ok(true)); } } } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 019f55675a5..4addd46e831 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -233,7 +233,7 @@ impl SessionImpl { SessionImpl::start_generating_session_key(&self.self_node_id, &self.encrypted_data, &self.cluster, &mut *data)?; SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data)?; - SessionImpl::do_signing(&self.encrypted_data, &mut *data)?; + SessionImpl::do_signing(&mut *data)?; self.completed.notify_all(); } @@ -383,7 +383,10 @@ impl SessionImpl { return Err(Error::InvalidMessage); } if data.state != SessionState::WaitingForPartialSignatureRequest { - return Err(Error::InvalidStateForRequest); + match data.state { + SessionState::SessionKeyGeneration => return Err(Error::TooEarlyForRequest), + _ => return Err(Error::InvalidStateForRequest), + } } // update data @@ -442,7 +445,7 @@ impl SessionImpl { })))?; // do signing - SessionImpl::do_signing(&self.encrypted_data, &mut *data)?; + SessionImpl::do_signing(&mut *data)?; self.completed.notify_all(); Ok(()) @@ -638,8 +641,9 @@ impl SessionImpl { debug_assert!(!session_nodes.contains(self_node_id)); debug_assert!(session_nodes.len() == encrypted_data.threshold); - let combined_hash = math::combine_message_hash_with_public(encrypted_data.threshold, &message_hash, &session_joint_public)?; + let combined_hash = math::combine_message_hash_with_public(&message_hash, &session_joint_public)?; math::compute_signature_share( + encrypted_data.threshold, &combined_hash, &session_secret_coeff, &encrypted_data.secret_share, @@ -649,12 +653,12 @@ impl SessionImpl { } /// Compute signature - fn do_signing(encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { + fn do_signing(data: &mut SessionData) -> Result<(), Error> { let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); let session_joint_public = data.session_joint_public.as_ref().expect("session key is generated on key generation phase; signing phase follows initialization; qed"); let partial_signatures = data.consensus.as_ref().expect("consensus on master is filled in initialization phase; this is master node; qed").job_responses()?.values(); - let signature_c = math::combine_message_hash_with_public(encrypted_data.threshold, message_hash, session_joint_public)?; + let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; let signature_s = math::compute_signature(partial_signatures)?; data.signed_message = Some(Ok((signature_c, signature_s))); @@ -862,31 +866,45 @@ mod tests { .or_else(|| self.queue.pop_front()) } - pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { - match { - match msg.2 { - Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => self.nodes[&msg.1].session.on_consensus_message(msg.0.clone(), &message), - Message::Signing(SigningMessage::SigningGenerationMessage(ref message)) => self.nodes[&msg.1].session.on_generation_message(msg.0.clone(), &message), - Message::Signing(SigningMessage::RequestPartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature_requested(msg.0.clone(), &message), - Message::Signing(SigningMessage::PartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature(msg.0.clone(), &message), - Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), - _ => panic!("unexpected"), + pub fn process_message(&mut self, mut msg: (NodeId, NodeId, Message)) -> Result<(), Error> { + let mut is_queued_message = false; + loop { + match { + match msg.2 { + Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => self.nodes[&msg.1].session.on_consensus_message(msg.0.clone(), &message), + Message::Signing(SigningMessage::SigningGenerationMessage(ref message)) => self.nodes[&msg.1].session.on_generation_message(msg.0.clone(), &message), + Message::Signing(SigningMessage::RequestPartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature_requested(msg.0.clone(), &message), + Message::Signing(SigningMessage::PartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature(msg.0.clone(), &message), + Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), + _ => panic!("unexpected"), + } + } { + Ok(_) => { + if let Some(message) = self.queue.pop_front() { + msg = message; + is_queued_message = true; + continue; + } + return Ok(()); + }, + Err(Error::TooEarlyForRequest) => { + if is_queued_message { + self.queue.push_front(msg); + } else { + self.queue.push_back(msg); + } + return Ok(()); + }, + Err(err) => return Err(err), } - } { - Ok(_) => Ok(()), - Err(Error::TooEarlyForRequest) => { - self.queue.push_back(msg); - Ok(()) - }, - Err(err) => Err(err), } } } #[test] fn complete_gen_sign_session() { - //let test_cases = [(0, 1)]; - let test_cases = [(1, 3)]; + //let test_cases = [(0, 1), (0, 5), (2, 5), (3, 5)]; + let test_cases = [/*(0, 1), (0, 5), */(2, 3)]; for &(threshold, num_nodes) in &test_cases { // run key generation sessions let mut gl = KeyGenerationMessageLoop::new(num_nodes); @@ -908,7 +926,7 @@ mod tests { // verify signature let public = gl.master().joint_public_and_secret().unwrap().unwrap().0; let signature = sl.master().wait().unwrap(); - assert!(math::verify_signature(threshold, &public, &signature, &message_hash).unwrap()); + assert!(math::verify_signature(&public, &signature, &message_hash).unwrap()); } } } From a0e6735d455b541fe4c8c7dcf51b8253822d2797 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 30 May 2017 09:51:18 +0300 Subject: [PATCH 33/45] fixed test cases in complete_gen_sign_session --- secret_store/src/key_server_cluster/signing_session.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 4addd46e831..59895b951a6 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -903,8 +903,7 @@ mod tests { #[test] fn complete_gen_sign_session() { - //let test_cases = [(0, 1), (0, 5), (2, 5), (3, 5)]; - let test_cases = [/*(0, 1), (0, 5), */(2, 3)]; + let test_cases = [(0, 1), (0, 5), (2, 5), (3, 5)]; for &(threshold, num_nodes) in &test_cases { // run key generation sessions let mut gl = KeyGenerationMessageLoop::new(num_nodes); From 810e72584f9df76f2493e8fda0638bf5d7a8ba7f Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 30 May 2017 11:41:12 +0300 Subject: [PATCH 34/45] fixed mistimed response reaction --- .../src/key_server_cluster/decryption_session.rs | 8 ++++++++ .../src/key_server_cluster/signing_session.rs | 14 ++++++++------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 18a7060a280..d815f0ab20c 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -272,6 +272,10 @@ impl SessionImpl { let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest)?; match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => { + // check state + if data.state != SessionState::EstablishingConsensus { + return Err(Error::InvalidStateForRequest); + } let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; let consensus_action = consensus_session.on_initialize_session(sender, &requestor)?; data.requestor = Some(requestor); @@ -279,6 +283,10 @@ impl SessionImpl { }, ConsensusMessage::ConfirmConsensusInitialization(ref message) => { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; + if data.state != SessionState::EstablishingConsensus { + // consensus is already established => mark node as confirmed (for restart case) and ignore + return consensus.offer_response(&sender, message.is_confirmed); + } consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? }, } diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index e520bbf3d6c..bb5fc559815 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -260,18 +260,16 @@ impl SessionImpl { })?); } - // check state - if data.state != SessionState::EstablishingConsensus { - // consensus is already established => mark node as confirmed (for restart case) and ignore - return data.consensus.as_mut().map(|consensus| consensus.accept_offer(&sender)).unwrap_or(Ok(())); - } - // process message let consensus_action = { let mut data = data.deref_mut(); let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest)?; match message.message { ConsensusMessage::InitializeConsensusSession(ref message) => { + // check state + if data.state != SessionState::EstablishingConsensus { + return Err(Error::InvalidStateForRequest); + } let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; let consensus_action = consensus_session.on_initialize_session(sender, &requestor)?; data.requestor = Some(requestor); @@ -279,6 +277,10 @@ impl SessionImpl { }, ConsensusMessage::ConfirmConsensusInitialization(ref message) => { let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; + if data.state != SessionState::EstablishingConsensus { + // consensus is already established => mark node as confirmed (for restart case) and ignore + return consensus.offer_response(&sender, message.is_confirmed); + } consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? }, } From 48693cae8430d17e1b72bb87a15728d14394d145 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 1 Jun 2017 15:39:49 +0300 Subject: [PATCH 35/45] jobs draft --- .../jobs/consensus_session.rs | 187 +++++++ .../key_server_cluster/jobs/decryption_job.rs | 0 .../key_server_cluster/jobs/job_session.rs | 468 ++++++++++++++++++ .../key_server_cluster/jobs/key_access_job.rs | 55 ++ .../src/key_server_cluster/jobs/mod.rs | 5 + .../key_server_cluster/jobs/signing_job.rs | 0 secret_store/src/key_server_cluster/mod.rs | 13 + 7 files changed, 728 insertions(+) create mode 100644 secret_store/src/key_server_cluster/jobs/consensus_session.rs create mode 100644 secret_store/src/key_server_cluster/jobs/decryption_job.rs create mode 100644 secret_store/src/key_server_cluster/jobs/job_session.rs create mode 100644 secret_store/src/key_server_cluster/jobs/key_access_job.rs create mode 100644 secret_store/src/key_server_cluster/jobs/mod.rs create mode 100644 secret_store/src/key_server_cluster/jobs/signing_job.rs diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs new file mode 100644 index 00000000000..50236278eaa --- /dev/null +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -0,0 +1,187 @@ +use std::marker::PhantomData; +use std::collections::BTreeSet; +use std::sync::Arc; +use ethkey::Signature; +use key_server_cluster::{Error, NodeId, SessionMeta, AclStorage}; +use key_server_cluster::message::ConsensusMessage; +use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor}; +use key_server_cluster::jobs::key_access_job::KeyAccessJob; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ConsensusSessionState { + WaitingForInitialization, + EstablishingConsensus, + ConsensusEstablished, + WaitingForPartialResults, + Finished, + Failed, +} + +pub struct ConsensusSession<'a, ConsensusTransport: JobTransport, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport> { + state: ConsensusSessionState, + meta: &'a SessionMeta, + consensus_job: JobSession<'a, KeyAccessJob, ConsensusTransport>, + computation_job: Option>, + dummy: PhantomData<(ComputationTransport, ComputationExecutor)>, +} + +pub struct ConsensusSessionParams<'a, ConsensusTransport: JobTransport> { + meta: &'a SessionMeta, + acl_storage: Arc, + consensus_transport: ConsensusTransport, +} + +impl<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> ConsensusSession<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> where ConsensusTransport: JobTransport, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport { + pub fn new_on_slave(params: ConsensusSessionParams<'a, ConsensusTransport>) -> Result { + let consensus_job_executor = KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage); + let consensus_job = JobSession::new(params.meta, consensus_job_executor, params.consensus_transport); + debug_assert!(consensus_job.state() == JobSessionState::Inactive); + + Ok(ConsensusSession { + state: ConsensusSessionState::WaitingForInitialization, + meta: params.meta, + consensus_job: consensus_job, + computation_job: None, + dummy: PhantomData, + }) + } + + pub fn new_on_master(params: ConsensusSessionParams<'a, ConsensusTransport>, signature: Signature) -> Result { + let consensus_job_executor = KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage, signature); + let consensus_job = JobSession::new(params.meta, consensus_job_executor, params.consensus_transport); + + Ok(ConsensusSession { + state: ConsensusSessionState::WaitingForInitialization, + meta: params.meta, + consensus_job: consensus_job, + computation_job: None, + dummy: PhantomData, + }) + } + + pub fn initialize(&mut self, nodes: BTreeSet) -> Result<(), Error> { + let initialization_result = self.consensus_job.initialize(nodes); + self.process_result(initialization_result) + } + + pub fn on_consensus_message(&mut self, sender: &NodeId, message: &ConsensusMessage) -> Result<(), Error> { + let consensus_result = match message { + &ConsensusMessage::InitializeConsensusSession(ref message) => + self.consensus_job.on_partial_request(sender, message.requestor_signature.clone().into()), + &ConsensusMessage::ConfirmConsensusInitialization(ref message) => + self.consensus_job.on_partial_response(sender, message.is_confirmed), + }; + self.process_result(consensus_result) + } + + pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { + if self.meta.self_node_id != self.meta.master_node_id { + return Err(Error::InvalidMessage); + } + if self.state != ConsensusSessionState::ConsensusEstablished { + return Err(Error::InvalidStateForRequest); + } + + let consensus_nodes = self.consensus_job.result().expect("disseminate_jobs is only called on master node when consensus is established; qed"); + let is_self_in_consensus = consensus_nodes.contains(&self.meta.self_node_id); + let mut consensus_nodes: BTreeSet<_> = consensus_nodes.into_iter().take(self.meta.threshold + 1).collect(); + if is_self_in_consensus { + consensus_nodes.remove(&self.meta.master_node_id); + consensus_nodes.insert(self.meta.master_node_id.clone()); + } + + let mut computation_job = JobSession::new(self.meta, executor, transport); + computation_job.initialize(consensus_nodes)?; + + self.computation_job = Some(computation_job); + self.state = ConsensusSessionState::WaitingForPartialResults; + + Ok(()) + } + + pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { + if &self.meta.master_node_id != node { + return Err(Error::InvalidMessage); + } + if self.state != ConsensusSessionState::ConsensusEstablished { + return Err(Error::InvalidStateForRequest); + } + + let mut computation_job = JobSession::new(self.meta, executor, transport); + let computation_result = computation_job.on_partial_request(node, request); + self.process_result(computation_result) + } + + pub fn on_job_response(&mut self, node: &NodeId, response: ComputationExecutor::PartialJobResponse) -> Result<(), Error> { + if self.state != ConsensusSessionState::WaitingForPartialResults { + return Err(Error::InvalidStateForRequest); + } + + let computation_result = self.computation_job.as_mut() + .expect("WaitingForPartialResults is only set when computation_job is created; qed") + .on_partial_response(node, response); + self.process_result(computation_result) + } + + pub fn on_node_timeout(&mut self, node: &NodeId) -> Result<(), Error> { + let timeout_result = match self.state { + ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Finished | ConsensusSessionState::Failed => + Err(Error::ConsensusUnreachable), + ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => + self.consensus_job.on_node_timeout(node), + ConsensusSessionState::WaitingForPartialResults => { + let is_computation_node = self.computation_job.as_mut() + .expect("WaitingForPartialResults state is only set when computation_job is created; qed") + .on_node_timeout(node) + .is_err(); + if !is_computation_node { + Ok(()) + } else { + self.consensus_job.on_node_timeout(node) + } + }, + }; + self.process_result(timeout_result) + } + + pub fn on_session_timeout(&mut self) -> Result<(), Error> { + match self.state { + ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Finished | ConsensusSessionState::Failed | + ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => + return self.process_result(Err(Error::ConsensusUnreachable)), + ConsensusSessionState::WaitingForPartialResults => () + }; + + let timeouted_nodes = self.computation_job.as_ref() + .expect("WaitingForPartialResults state is only set when computation_job is created; qed") + .requests() + .clone(); + for timeouted_node in timeouted_nodes { + let timeout_result = self.consensus_job.on_node_timeout(&timeouted_node); + self.process_result(timeout_result); + } + + self.state = ConsensusSessionState::ConsensusEstablished; + Ok(()) + } + + fn process_result(&mut self, result: Result<(), Error>) -> Result<(), Error> { + match self.state { + ConsensusSessionState::EstablishingConsensus => match self.consensus_job.state() { + JobSessionState::Finished => self.state = ConsensusSessionState::ConsensusEstablished, + JobSessionState::Failed => self.state = ConsensusSessionState::Failed, + _ => (), + }, + ConsensusSessionState::WaitingForPartialResults => match self.computation_job.as_ref() + .expect("WaitingForPartialResults state is only set when computation_job is created; qed") + .state() { + JobSessionState::Finished => self.state = ConsensusSessionState::Finished, + JobSessionState::Failed => self.state = ConsensusSessionState::Failed, + _ => (), + }, + _ => (), + } + + result + } +} diff --git a/secret_store/src/key_server_cluster/jobs/decryption_job.rs b/secret_store/src/key_server_cluster/jobs/decryption_job.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs new file mode 100644 index 00000000000..0062fb91d20 --- /dev/null +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -0,0 +1,468 @@ +use std::marker::PhantomData; +use std::collections::{BTreeSet, BTreeMap}; +use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; + +/// Job executor. +pub trait JobExecutor { + type PartialJobRequest; + type PartialJobResponse; + type JobResponse; + + /// Prepare job request for given node. + fn prepare_partial_request(&self) -> Result; + /// Process partial request. + fn process_partial_request(&self, partial_request: Self::PartialJobRequest) -> Result; + /// Check partial response of given node. + fn check_partial_response(&self, partial_response: &Self::PartialJobResponse) -> Result; + /// Compute final job response. + fn compute_response(&self, partial_responses: &BTreeMap) -> Result; +} + +/// Jobs transport. +pub trait JobTransport { + type PartialJobRequest; + type PartialJobResponse; + + /// Send partial request to given node. + fn send_partial_request(&self, node: &NodeId, request: Self::PartialJobRequest) -> Result<(), Error>; + /// Send partial request to given node. + fn send_partial_response(&self, node: &NodeId, response: Self::PartialJobResponse) -> Result<(), Error>; +} + +#[derive(Debug, Clone, Copy, PartialEq)] +/// Current state of job session. +pub enum JobSessionState { + /// Session is inactive. + Inactive, + /// Session is active. + Active, + /// Session is finished. + Finished, + /// Session has failed. + Failed, +} + +/// Basic request-response session on a set of nodes. +pub struct JobSession<'a, Executor: JobExecutor, Transport> where Transport: JobTransport { + /// Session meta. + meta: &'a SessionMeta, + /// Job executor. + executor: Executor, + /// Jobs transport. + transport: Transport, + /// Session data. + data: JobSessionData, + //// PartialJobRequest dummy. + // dummy: PhantomData, +} + +/// Data of job session. +struct JobSessionData { + /// Session state. + state: JobSessionState, + /// Mutable session data. + active_data: Option>, +} + +/// Active job session data. +struct ActiveJobSessionData { + /// Active partial requests. + requests: BTreeSet, + /// Rejects to partial requests. + rejects: BTreeSet, + /// Received partial responses. + responses: BTreeMap, +} + +impl<'a, Executor, Transport> JobSession<'a, Executor, Transport> where Executor: JobExecutor, Transport: JobTransport { + /// Create new session. + pub fn new(meta: &'a SessionMeta, executor: Executor, transport: Transport) -> Self { + JobSession { + meta: meta, + executor: executor, + transport: transport, + data: JobSessionData { + state: JobSessionState::Inactive, + active_data: None, + }, + } + } + + #[cfg(test)] + /// Get transport reference. + pub fn transport(&self) -> &Transport { + &self.transport + } + + /// Get job state. + pub fn state(&self) -> JobSessionState { + self.data.state + } + + /// Get active requests. + pub fn requests(&self) -> &BTreeSet { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); + + &self.data.active_data.as_ref() + .expect("requests is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") + .requests + } + + /// Get job result. + pub fn result(&self) -> Result { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); + + if self.data.state != JobSessionState::Finished { + return Err(Error::InvalidStateForRequest); + } + + self.executor.compute_response(&self.data.active_data.as_ref() + .expect("requests is only called on master nodes; on master nodes active_data is filled during initialization; qed") + .responses) + } + + /// Initialize. + pub fn initialize(&mut self, mut nodes: BTreeSet) -> Result<(), Error> { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); + debug_assert!(nodes.len() >= self.meta.threshold + 1); + + if self.data.state != JobSessionState::Inactive { + return Err(Error::InvalidStateForRequest); + } + + // send requests to slave nodes + let mut waits_for_self = false; + let active_data = ActiveJobSessionData { + requests: nodes, + rejects: BTreeSet::new(), + responses: BTreeMap::new(), + }; + for node in &active_data.requests { + if node != &self.meta.self_node_id { + self.transport.send_partial_request(&node, self.executor.prepare_partial_request()?)?; + } else { + waits_for_self = true; + } + } + + // update state + self.data.active_data = Some(active_data); + self.data.state = JobSessionState::Active; + + // if we are waiting for response from self => do it + if waits_for_self { + let partial_response = self.executor.process_partial_request(self.executor.prepare_partial_request()?)?; + self.on_partial_response(&self.meta.self_node_id, partial_response)?; + } + + Ok(()) + } + + /// When partial request is received by slave node. + pub fn on_partial_request(&mut self, node: &NodeId, request: Executor::PartialJobRequest) -> Result<(), Error> { + if node != &self.meta.master_node_id { + return Err(Error::InvalidMessage); + } + if self.meta.self_node_id == self.meta.master_node_id { + return Err(Error::InvalidMessage); + } + if self.data.state != JobSessionState::Inactive && self.data.state != JobSessionState::Finished { + return Err(Error::InvalidStateForRequest); + } + + self.data.state = JobSessionState::Finished; + self.transport.send_partial_response(node, self.executor.process_partial_request(request)?) + } + + /// When partial request is received by master node. + pub fn on_partial_response(&mut self, node: &NodeId, response: Executor::PartialJobResponse) -> Result<(), Error> { + if self.meta.self_node_id != self.meta.master_node_id { + return Err(Error::InvalidMessage); + } + if self.data.state != JobSessionState::Active && self.data.state != JobSessionState::Finished { + return Err(Error::InvalidStateForRequest); + } + + let active_data = self.data.active_data.as_mut() + .expect("on_partial_response is only called on master nodes; on master nodes active_data is filled during initialization; qed"); + if !active_data.requests.remove(node) { + return Err(Error::InvalidNodeForRequest); + } + + if !self.executor.check_partial_response(&response).unwrap_or(false) { + active_data.rejects.insert(node.clone()); + if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { + return Ok(()); + } + + self.data.state = JobSessionState::Failed; + Err(Error::ConsensusUnreachable) + } else { + active_data.responses.insert(node.clone(), response); + + if active_data.responses.len() < self.meta.threshold + 1 { + return Ok(()); + } + + self.data.state = JobSessionState::Finished; + Ok(()) + } + } + + /// When node is timeouted. + pub fn on_node_timeout(&mut self, node: &NodeId) -> Result<(), Error> { + if self.meta.self_node_id != self.meta.master_node_id { + if node != &self.meta.master_node_id { + return Ok(()); + } + + self.data.state = JobSessionState::Failed; + return Err(Error::NodeDisconnected); + } + + let active_data = self.data.active_data.as_mut() + .expect("we have checked that we are on master node; on master nodes active_data is filled during initialization; qed"); + if active_data.rejects.contains(node) { + return Ok(()); + } + if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { + active_data.rejects.insert(node.clone()); + if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { + return Ok(()); + } + + self.data.state = JobSessionState::Failed; + return Err(Error::NodeDisconnected); + } + + Ok(()) + } + + /// When session timeouted. + pub fn on_session_timeout(&mut self) { + self.data.state = JobSessionState::Failed; + } +} + + +#[cfg(test)] +mod tests { + use std::collections::{VecDeque, BTreeMap}; + use parking_lot::Mutex; + use ethkey::Public; + use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, DocumentKeyShare}; + use super::{JobExecutor, JobTransport, JobSession, JobSessionState}; + + struct SquaredSumJobExecutor; + + impl JobExecutor for SquaredSumJobExecutor { + type PartialJobRequest = u32; + type PartialJobResponse = u32; + type JobResponse = u32; + + fn prepare_partial_request(&self) -> Result { Ok(2) } + fn process_partial_request(&self, r: u32) -> Result { Ok(r * r) } + fn check_partial_response(&self, r: &u32) -> Result { Ok(r % 2 == 0) } + fn compute_response(&self, r: &BTreeMap) -> Result { Ok(r.values().fold(0, |v1, v2| v1 + v2)) } + } + + #[derive(Default)] + struct DummyJobTransport { + pub requests: Mutex>, + pub responses: Mutex>, + } + + impl DummyJobTransport { + pub fn response(&self) -> (NodeId, u32) { + self.responses.lock().pop_front().unwrap() + } + } + + impl JobTransport for DummyJobTransport { + type PartialJobRequest = u32; + type PartialJobResponse = u32; + + fn send_partial_request(&self, node: &NodeId, request: u32) -> Result<(), Error> { self.requests.lock().push_back((node.clone(), request)); Ok(()) } + fn send_partial_response(&self, node: &NodeId, response: u32) -> Result<(), Error> { self.responses.lock().push_back((node.clone(), response)); Ok(()) } + } + + fn make_master_session_meta(threshold: usize) -> SessionMeta { + SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(1), threshold: threshold } + } + + fn make_slave_session_meta(threshold: usize) -> SessionMeta { + SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(2), threshold: threshold } + } + + #[test] + fn job_initialize_fails_if_not_inactive() { + let meta = make_master_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1)].into_iter().collect()).unwrap(); + assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect()).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn job_initialization_leads_to_finish_if_single_node_is_required() { + let meta = make_master_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Finished); + assert_eq!(job.result(), Ok(4)); + } + + #[test] + fn job_initialization_does_not_leads_to_finish_if_single_other_node_is_required() { + let meta = make_master_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(2)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + } + + #[test] + fn job_request_fails_if_comes_from_non_master_node() { + let meta = make_slave_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + assert_eq!(job.on_partial_request(&NodeId::from(3), 2).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn job_request_fails_if_comes_to_master_node() { + let meta = make_master_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + assert_eq!(job.on_partial_request(&NodeId::from(1), 2).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn job_request_fails_if_comes_to_failed_state() { + let meta = make_slave_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.on_session_timeout(); + assert_eq!(job.on_partial_request(&NodeId::from(1), 2).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn job_request_succeeds_if_comes_to_finished_state() { + let meta = make_slave_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.on_partial_request(&NodeId::from(1), 2).unwrap(); + assert_eq!(job.transport().response(), (NodeId::from(1), 4)); + assert_eq!(job.state(), JobSessionState::Finished); + job.on_partial_request(&NodeId::from(1), 3).unwrap(); + assert_eq!(job.transport().response(), (NodeId::from(1), 9)); + assert_eq!(job.state(), JobSessionState::Finished); + } + + #[test] + fn job_response_fails_if_comes_to_slave_node() { + let meta = make_slave_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + assert_eq!(job.on_partial_response(&NodeId::from(1), 2).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn job_response_fails_if_comes_to_failed_state() { + let meta = make_master_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(2)].into_iter().collect()).unwrap(); + job.on_session_timeout(); + assert_eq!(job.on_partial_response(&NodeId::from(2), 2).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn job_response_fails_if_comes_from_unknown_node() { + let meta = make_master_session_meta(0); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(2)].into_iter().collect()).unwrap(); + assert_eq!(job.on_partial_response(&NodeId::from(3), 2).unwrap_err(), Error::InvalidNodeForRequest); + } + + #[test] + fn job_response_leads_to_failure_if_too_few_nodes_left() { + let meta = make_master_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + assert_eq!(job.on_partial_response(&NodeId::from(2), 3).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(job.state(), JobSessionState::Failed); + } + + #[test] + fn job_response_succeeds() { + let meta = make_master_session_meta(2); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + job.on_partial_response(&NodeId::from(2), 2).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + } + + #[test] + fn job_response_leads_to_finish() { + let meta = make_master_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + job.on_partial_response(&NodeId::from(2), 2).unwrap(); + assert_eq!(job.state(), JobSessionState::Finished); + } + + #[test] + fn job_node_timeout_ignored_when_slave_disconnects_from_slave() { + let meta = make_slave_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + assert_eq!(job.state(), JobSessionState::Inactive); + job.on_node_timeout(&NodeId::from(3)).unwrap(); + assert_eq!(job.state(), JobSessionState::Inactive); + } + + #[test] + fn job_node_timeout_leads_to_fail_when_slave_disconnects_from_master() { + let meta = make_slave_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + assert_eq!(job.state(), JobSessionState::Inactive); + assert_eq!(job.on_node_timeout(&NodeId::from(1)).unwrap_err(), Error::NodeDisconnected); + assert_eq!(job.state(), JobSessionState::Failed); + } + + #[test] + fn job_node_timeout_ignored_when_disconnects_from_rejected() { + let meta = make_master_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + job.on_partial_response(&NodeId::from(2), 3).unwrap(); + job.on_node_timeout(&NodeId::from(2)).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + } + + #[test] + fn job_node_timeout_ignored_when_disconnects_from_unknown() { + let meta = make_master_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + job.on_node_timeout(&NodeId::from(3)).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + } + + #[test] + fn job_node_timeout_ignored_when_disconnects_from_requested_and_enough_nodes_left() { + let meta = make_master_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + job.on_node_timeout(&NodeId::from(3)).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + } + + #[test] + fn job_node_timeout_leads_to_fail_when_disconnects_from_requested_and_not_enough_nodes_left() { + let meta = make_master_session_meta(1); + let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); + assert_eq!(job.state(), JobSessionState::Active); + assert_eq!(job.on_node_timeout(&NodeId::from(2)).unwrap_err(), Error::NodeDisconnected); + assert_eq!(job.state(), JobSessionState::Failed); + } +} diff --git a/secret_store/src/key_server_cluster/jobs/key_access_job.rs b/secret_store/src/key_server_cluster/jobs/key_access_job.rs new file mode 100644 index 00000000000..87daf934074 --- /dev/null +++ b/secret_store/src/key_server_cluster/jobs/key_access_job.rs @@ -0,0 +1,55 @@ +use std::sync::Arc; +use std::collections::{BTreeSet, BTreeMap}; +use ethkey::{Signature, recover}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage}; +use key_server_cluster::jobs::job_session::JobExecutor; + +/// Purpose of this job is to construct set of nodes, which have agreed to provide access to the given key for the given requestor. +pub struct KeyAccessJob { + /// Key id. + id: SessionId, + /// ACL storage. + acl_storage: Arc, + /// Requester signature. + signature: Option, +} + +impl KeyAccessJob { + pub fn new_on_slave(id: SessionId, acl_storage: Arc) -> Self { + KeyAccessJob { + id: id, + acl_storage: acl_storage, + signature: None, + } + } + + pub fn new_on_master(id: SessionId, acl_storage: Arc, signature: Signature) -> Self { + KeyAccessJob { + id: id, + acl_storage: acl_storage, + signature: Some(signature), + } + } +} + +impl JobExecutor for KeyAccessJob { + type PartialJobRequest = Signature; + type PartialJobResponse = bool; + type JobResponse = BTreeSet; + + fn prepare_partial_request(&self) -> Result { + Ok(self.signature.as_ref().expect("prepare_partial_request is only called on master nodes; new_on_master fills the signature; qed").clone()) + } + + fn process_partial_request(&self, partial_request: Signature) -> Result { + self.acl_storage.check(&recover(&partial_request, &self.id)?, &self.id).map_err(|_| Error::AccessDenied) + } + + fn check_partial_response(&self, partial_response: &bool) -> Result { + Ok(*partial_response) + } + + fn compute_response(&self, partial_responses: &BTreeMap) -> Result, Error> { + Ok(partial_responses.keys().cloned().collect()) + } +} diff --git a/secret_store/src/key_server_cluster/jobs/mod.rs b/secret_store/src/key_server_cluster/jobs/mod.rs new file mode 100644 index 00000000000..ed3ca38409f --- /dev/null +++ b/secret_store/src/key_server_cluster/jobs/mod.rs @@ -0,0 +1,5 @@ +pub mod consensus_session; +pub mod decryption_job; +pub mod job_session; +pub mod key_access_job; +pub mod signing_job; diff --git a/secret_store/src/key_server_cluster/jobs/signing_job.rs b/secret_store/src/key_server_cluster/jobs/signing_job.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 3ba285946ec..ae5de0cb1c8 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -36,6 +36,18 @@ pub use super::acl_storage::tests::DummyAclStorage; pub type SessionId = ServerKeyId; +/// Session metadata. +pub struct SessionMeta { + /// Key id. + pub id: SessionId, + /// Id of node, which has started this session. + pub master_node_id: NodeId, + /// Id of node, on which this session is running. + pub self_node_id: NodeId, + /// Session threshold. + pub threshold: usize, +} + #[derive(Clone, Debug, PartialEq)] /// Errors which can occur during encryption/decryption session pub enum Error { @@ -146,6 +158,7 @@ mod decryption_session; mod encryption_session; mod generation_session; mod io; +mod jobs; pub mod math; mod message; mod signing_session; From 7b4f591bf6d876e072d1ec5005cebf9f535cd820 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 1 Jun 2017 16:56:48 +0300 Subject: [PATCH 36/45] DecryptionJob --- .../src/key_server_cluster/cluster.rs | 26 +- .../key_server_cluster/cluster_sessions.rs | 18 +- .../key_server_cluster/decryption_session.rs | 743 +++++++----------- .../jobs/consensus_session.rs | 82 +- .../key_server_cluster/jobs/decryption_job.rs | 146 ++++ .../key_server_cluster/jobs/job_session.rs | 68 +- .../key_server_cluster/jobs/key_access_job.rs | 8 +- secret_store/src/key_server_cluster/mod.rs | 1 + 8 files changed, 537 insertions(+), 555 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 5074daf1f12..2c9da8f11b6 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -29,14 +29,14 @@ use tokio_core::net::{TcpListener, TcpStream}; use ethkey::{Public, KeyPair, Signature, Random, Generator}; use util::H256; use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage}; -use key_server_cluster::cluster_sessions::{ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, +use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, GenerationSessionWrapper, EncryptionSessionWrapper, DecryptionSessionWrapper, SigningSessionWrapper}; use key_server_cluster::message::{self, Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage, ConsensusMessage}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionState as GenerationSessionState}; #[cfg(test)] use key_server_cluster::generation_session::SessionImpl as GenerationSessionImpl; -use key_server_cluster::decryption_session::{Session as DecryptionSession, SessionState as DecryptionSessionState, DecryptionSessionId}; +use key_server_cluster::decryption_session::{Session as DecryptionSession, DecryptionSessionId}; use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionState as EncryptionSessionState}; use key_server_cluster::signing_session::{Session as SigningSession, SessionState as SigningSessionState, SigningSessionId}; use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, read_encrypted_message, WriteMessage, write_encrypted_message}; @@ -559,7 +559,7 @@ impl ClusterCore { connected_nodes.insert(data.self_key_pair.public().clone()); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); - data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster) + data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster, None) }, _ => { data.sessions.decryption_sessions.get(&decryption_session_id) @@ -568,26 +568,12 @@ impl ClusterCore { }; loop { - match session.clone().and_then(|session| match message { - DecryptionMessage::DecryptionConsensusMessage(ref message) => - session.on_consensus_message(sender.clone(), message), - DecryptionMessage::RequestPartialDecryption(ref message) => - session.on_partial_decryption_requested(sender.clone(), message), - DecryptionMessage::PartialDecryption(ref message) => - session.on_partial_decryption(sender.clone(), message), - DecryptionMessage::DecryptionSessionError(ref message) => - session.on_session_error(sender.clone(), message), - DecryptionMessage::DecryptionSessionCompleted(ref message) => - session.on_session_completed(sender.clone(), message), - }) { + match session.clone().and_then(|session| session.process_message(&sender, &message)) { Ok(_) => { // if session is completed => stop let session = session.clone().expect("session.method() call finished with success; session exists; qed"); - let session_state = session.state(); - if session_state == DecryptionSessionState::Finished { + if session.is_finished() { info!(target: "secretstore_net", "{}: decryption session completed", data.self_key_pair.public()); - } - if session_state == DecryptionSessionState::Finished || session_state == DecryptionSessionState::Failed { data.sessions.decryption_sessions.remove(&decryption_session_id); break; } @@ -929,7 +915,7 @@ impl ClusterClient for ClusterClientImpl { let access_key = Random.generate()?.secret().clone(); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); - let session = self.data.sessions.new_decryption_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster)?; + let session = self.data.sessions.new_decryption_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster, Some(requestor_signature))?; session.initialize(requestor_signature, is_shadow_decryption)?; Ok(DecryptionSessionWrapper::new(Arc::downgrade(&self.data), DecryptionSessionId::new(session_id, access_key), session)) } diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 19248dbfe80..5fc8406e326 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -19,8 +19,8 @@ use std::sync::{Arc, Weak}; use std::sync::atomic::{AtomicBool, Ordering}; use std::collections::{VecDeque, BTreeSet, BTreeMap}; use parking_lot::RwLock; -use ethkey::{Public, Secret}; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, EncryptedDocumentKeyShadow}; +use ethkey::{Public, Secret, Signature}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::{Cluster, ClusterData, ClusterView, ClusterConfiguration}; use key_server_cluster::message::{self, Message, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionImpl as GenerationSessionImpl, @@ -224,7 +224,7 @@ impl ClusterSessions { } /// Create new decryption session. - pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { + pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc, requester_signature: Option) -> Result, Error> { let session_id = DecryptionSessionId::new(session_id, sub_session_id); // some of nodes, which were encrypting secret may be down @@ -237,13 +237,17 @@ impl ClusterSessions { } self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), move || DecryptionSessionImpl::new(DecryptionSessionParams { - id: session_id.id, + meta: SessionMeta { + id: session_id.id, + self_node_id: self.self_node_id.clone(), + master_node_id: master, + threshold: encrypted_data.threshold, + }, access_key: session_id.access_key, - self_node_id: self.self_node_id.clone(), - encrypted_data: encrypted_data, + key_share: encrypted_data, acl_storage: self.acl_storage.clone(), cluster: cluster, - })) + }, requester_signature)) } /// Send decryption session error. diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index d815f0ab20c..9071a063d8d 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -22,15 +22,18 @@ use parking_lot::{Mutex, Condvar}; use ethcrypto::ecies::encrypt; use ethcrypto::DEFAULT_MAC; use ethkey::{self, Secret, Public, Signature}; -use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, EncryptedDocumentKeyShadow}; +use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::consensus::Consensus; -use key_server_cluster::consensus_session::{ConsensusSession, AclConsensusChecker, SessionParams as ConsensusSessionParams, - SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; use key_server_cluster::math; use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, - PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage}; + PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage, InitializeConsensusSession, + ConfirmConsensusInitialization}; +use key_server_cluster::jobs::job_session::{JobExecutor, JobTransport}; +use key_server_cluster::jobs::key_access_job::KeyAccessJob; +use key_server_cluster::jobs::decryption_job::{PartialDecryptionRequest, PartialDecryptionResponse, DecryptionJob}; +use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; /// Decryption session API. pub trait Session: Send + Sync + 'static { @@ -47,22 +50,34 @@ pub trait Session: Send + Sync + 'static { /// 3) partial decryption: every node which has succussfully checked access for the requestor do a partial decryption /// 4) decryption: master node receives all partial decryptions of the secret and restores the secret pub struct SessionImpl { - /// Encryption session id. - id: SessionId, + /// Session core. + core: SessionCore, + /// Session data. + data: Mutex, +} + +/// Immutable session data. +struct SessionCore { + /// Session metadata. + pub meta: SessionMeta, /// Decryption session access key. - access_key: Secret, - /// Public identifier of this node. - self_node_id: NodeId, - /// Encrypted data. - encrypted_data: DocumentKeyShare, - /// ACL storate to check access to the resource. - acl_storage: Arc, + pub access_key: Secret, + /// Key share. + pub key_share: DocumentKeyShare, /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, + pub cluster: Arc, /// SessionImpl completion condvar. - completed: Condvar, - /// Mutable session data. - data: Mutex, + pub completed: Condvar, +} + +/// Mutable session data. +struct SessionData { + /// Consensus-based decryption session. + pub consensus_session: DecryptionConsensusSession, + /// Is shadow decryption requested? + pub is_shadow_decryption: Option, + /// Decryption result. + pub result: Option>, } /// Decryption session Id. @@ -76,542 +91,348 @@ pub struct DecryptionSessionId { /// SessionImpl creation parameters pub struct SessionParams { - /// SessionImpl identifier. - pub id: SessionId, - /// SessionImpl access key. + /// Session metadata. + pub meta: SessionMeta, + /// Session access key. pub access_key: Secret, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Encrypted data (result of running encryption_session::SessionImpl). - pub encrypted_data: DocumentKeyShare, + /// Key share. + pub key_share: DocumentKeyShare, /// ACL storage. pub acl_storage: Arc, /// Cluster pub cluster: Arc, } -#[derive(Debug, Clone)] -/// Partial decryption result. -struct PartialDecryptionResult { - /// Shadow point. - pub shadow_point: Public, - /// Decryption shadow coefficient, if requested. - pub decrypt_shadow: Option>, +type DecryptionConsensusSession = ConsensusSession; + +/// Decryption consensus transport. +struct DecryptionConsensusTransport { + /// Session id. + id: SessionId, + /// Session access key. + access_key: Secret, + /// Cluster. + cluster: Arc, } -/// Mutable data of encryption (distributed key generation) session. -struct SessionData { - /// Current state of the session. - state: SessionState, - - // === Values, filled when session initialization just starts === - /// Reference to the node, which has started this session. - master: Option, - /// Public key of requestor. - requestor: Option, - /// Is shadow decryption requested? - is_shadow_decryption: Option, - /// Decryption consensus group. - consensus: Option>, +impl JobTransport for DecryptionConsensusTransport { + type PartialJobRequest=Signature; + type PartialJobResponse=bool; - // === Values, filled when consensus is establishing === - /// Consensus session. - consensus_session: Option>, + fn send_partial_request(&self, node: &NodeId, request: Signature) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: request.into(), + }) + }))) + } - /// === Values, filled during final decryption === - /// Decrypted secret - decrypted_secret: Option>, + fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: response, + }) + }))) + } } -#[derive(Debug, Clone, PartialEq)] -/// Decryption session data. -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Establishing consensus. - EstablishingConsensus, - /// Consensus established - EstablishedConsensus, - - // === Decryption states === - /// Waiting for partial decrypion request. - WaitingForPartialDecryptionRequest, - /// Waiting for partial decryption responses. - WaitingForPartialDecryption, - - // === Final states of the session === - /// Decryption session is finished for this node. - Finished, - /// Decryption session is failed for this node. - Failed, +/// Decryption job transport +struct DecryptionJobTransport { + /// Session id. + id: SessionId, + //// Session access key. + access_key: Secret, + /// Cluster. + cluster: Arc, +} + +impl JobTransport for DecryptionJobTransport { + type PartialJobRequest=PartialDecryptionRequest; + type PartialJobResponse=PartialDecryptionResponse; + + fn send_partial_request(&self, node: &NodeId, request: PartialDecryptionRequest) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + request_id: request.id.into(), + is_shadow_decryption: request.is_shadow_decryption, + nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: PartialDecryptionResponse) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + request_id: response.request_id.into(), + shadow_point: response.shadow_point.into(), + decrypt_shadow: response.decrypt_shadow, + }))) + } } impl SessionImpl { /// Create new decryption session. - pub fn new(params: SessionParams) -> Result { - check_encrypted_data(¶ms.self_node_id, ¶ms.encrypted_data)?; + pub fn new(params: SessionParams, requester_signature: Option) -> Result { + debug_assert!((params.meta.self_node_id == params.meta.master_node_id) == requester_signature.is_some()); + + use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; + + // check that common_point and encrypted_point are already set + if params.key_share.common_point.is_none() || params.key_share.encrypted_point.is_none() { + return Err(Error::NotStartedSessionId); + } + + // check nodes and threshold + let nodes = params.key_share.id_numbers.keys().cloned().collect(); + check_cluster_nodes(¶ms.meta.self_node_id, &nodes)?; + check_threshold(params.key_share.threshold, &nodes)?; + + let consensus_transport = DecryptionConsensusTransport { + id: params.meta.id.clone(), + access_key: params.access_key.clone(), + cluster: params.cluster.clone(), + }; Ok(SessionImpl { - id: params.id, - access_key: params.access_key, - self_node_id: params.self_node_id, - encrypted_data: params.encrypted_data, - acl_storage: params.acl_storage, - cluster: params.cluster, - completed: Condvar::new(), + core: SessionCore { + meta: params.meta, + access_key: params.access_key, + key_share: params.key_share, + cluster: params.cluster, + completed: Condvar::new(), + }, data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - master: None, - requestor: None, + consensus_session: match requester_signature { + Some(requester_signature) => ConsensusSession::new_on_master(ConsensusSessionParams { + meta: params.meta.clone(), + acl_storage: params.acl_storage.clone(), + consensus_transport: consensus_transport, + }, requester_signature)?, + None => ConsensusSession::new_on_slave(ConsensusSessionParams { + meta: params.meta.clone(), + acl_storage: params.acl_storage.clone(), + consensus_transport: consensus_transport, + })?, + }, is_shadow_decryption: None, - consensus: None, - consensus_session: None, - decrypted_secret: None, - }) + result: None, + }), }) } - /// Get current session state. - pub fn state(&self) -> SessionState { - self.data.lock().state.clone() - } - #[cfg(test)] /// Get this node id. pub fn node(&self) -> &NodeId { - &self.self_node_id + &self.core.meta.self_node_id } #[cfg(test)] /// Get this session access key. pub fn access_key(&self) -> &Secret { - &self.access_key + &self.core.access_key } #[cfg(test)] /// Get decrypted secret pub fn decrypted_secret(&self) -> Option> { - self.data.lock().decrypted_secret.clone() + self.data.lock().result.clone() } - /// Initialize decryption session. + /// Initialize decryption session on master node. pub fn initialize(&self, requestor_signature: Signature, is_shadow_decryption: bool) -> Result<(), Error> { let mut data = self.data.lock(); + data.consensus_session.initialize(self.core.key_share.id_numbers.keys().cloned().collect())?; - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // recover requestor signature - let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; - - // update state - data.master = Some(self.self_node_id.clone()); - data.state = SessionState::EstablishingConsensus; - data.requestor = Some(requestor_public); - data.is_shadow_decryption = Some(is_shadow_decryption); - - // create consensus session - let mut consensus = Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?; - let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { - id: self.id.clone(), - self_node_id: self.self_node_id.clone(), - master_node_id: self.self_node_id.clone(), - consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), - })?; - - // start consensus session - let consensus_action = consensus_session.initialize(requestor_signature, &mut consensus)?; - data.consensus = Some(consensus); - data.consensus_session = Some(consensus_session); - - // process consensus action - SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; + if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { + self.core.disseminate_jobs(&mut data.consensus_session, is_shadow_decryption)?; - // if single node is required to sign message, proceed - if data.state == SessionState::EstablishedConsensus { - SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data)?; - SessionImpl::do_decryption(&self.access_key, &self.encrypted_data, &mut *data)?; - self.completed.notify_all(); + debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); + data.is_shadow_decryption = Some(is_shadow_decryption); + data.result = Some(Ok(data.consensus_session.result()?)); + self.core.completed.notify_all(); } Ok(()) } - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: NodeId, message: &DecryptionConsensusMessage) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - - let mut data = self.data.lock(); - - // if we are waiting for initialization - if data.state == SessionState::WaitingForInitialization { - data.master = Some(sender.clone()); - data.state = SessionState::EstablishingConsensus; - data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); - data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { - id: self.id.clone(), - self_node_id: self.self_node_id.clone(), - master_node_id: sender.clone(), - consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), - })?); + /// Process decryption message. + pub fn process_message(&self, sender: &NodeId, message: &DecryptionMessage) -> Result<(), Error> { + match message { + &DecryptionMessage::DecryptionConsensusMessage(ref message) => + self.on_consensus_message(sender, message), + &DecryptionMessage::RequestPartialDecryption(ref message) => + self.on_partial_decryption_requested(sender, message), + &DecryptionMessage::PartialDecryption(ref message) => + self.on_partial_decryption(sender, message), + &DecryptionMessage::DecryptionSessionError(ref message) => + self.on_session_error(sender, message), + &DecryptionMessage::DecryptionSessionCompleted(ref message) => + self.on_session_completed(sender, message), } + } - // check state - if data.state != SessionState::EstablishingConsensus { - // consensus is already established => mark node as confirmed (for restart case) and ignore - return data.consensus.as_mut().map(|consensus| consensus.accept_offer(&sender)).unwrap_or(Ok(())); - } + /// When consensus-related message is received. + pub fn on_consensus_message(&self, sender: &NodeId, message: &DecryptionConsensusMessage) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); - // process message - let consensus_action = { - let mut data = data.deref_mut(); - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest)?; - match message.message { - ConsensusMessage::InitializeConsensusSession(ref message) => { - // check state - if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); - } - let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; - let consensus_action = consensus_session.on_initialize_session(sender, &requestor)?; - data.requestor = Some(requestor); - consensus_action - }, - ConsensusMessage::ConfirmConsensusInitialization(ref message) => { - let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; - if data.state != SessionState::EstablishingConsensus { - // consensus is already established => mark node as confirmed (for restart case) and ignore - return consensus.offer_response(&sender, message.is_confirmed); - } - consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? - }, - } - }; - SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; + let mut data = self.data.lock(); + let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; + data.consensus_session.on_consensus_message(&sender, &message.message)?; - // if consensus is established and we are on master node => ask for partial decryption - if data.state != SessionState::EstablishedConsensus { + let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; + if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { return Ok(()); } - SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data) - } - pub fn on_partial_decryption_requested(&self, sender: NodeId, message: &RequestPartialDecryption) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(sender != self.self_node_id); + let is_shadow_decryption = data.is_shadow_decryption + .expect("we are on master node; on master node is_shadow_decryption is filled in initialize(); on_consensus_message follows initialize (state check in consensus_session); qed"); + self.core.disseminate_jobs(&mut data.consensus_session, is_shadow_decryption) + } - // check message - if message.nodes.len() != self.encrypted_data.threshold + 1 { - return Err(Error::InvalidMessage); - } + /// When partial decryption is requested. + pub fn on_partial_decryption_requested(&self, sender: &NodeId, message: &RequestPartialDecryption) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); + let requester = data.consensus_session.requester()?.clone(); + let decryption_job = DecryptionJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.access_key.clone(), requester, self.core.key_share.clone()); + let decryption_transport = self.core.decryption_transport(); - // check state - if data.master != Some(sender) { - return Err(Error::InvalidMessage); - } - if data.state == SessionState::EstablishingConsensus { - // for slave nodes consensus is established when partial decryption request is received - data.state = SessionState::WaitingForPartialDecryptionRequest; - } - if data.state != SessionState::WaitingForPartialDecryptionRequest { - return Err(Error::InvalidStateForRequest); - } - - // calculate shadow point - let decryption_result = { - let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryptionRequest follows initialization; qed"); - let nodes = message.nodes.iter().cloned().map(Into::into).collect(); - do_partial_decryption(&self.self_node_id, &requestor, message.is_shadow_decryption, &nodes, &self.access_key, &self.encrypted_data)? - }; - self.cluster.send(&sender, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - request_id: message.request_id.clone(), - shadow_point: decryption_result.shadow_point.into(), - decrypt_shadow: decryption_result.decrypt_shadow, - })))?; - - // master could ask us for another partial decryption in case of restart - // => no state change is required - - Ok(()) + data.consensus_session.on_job_request(&sender, PartialDecryptionRequest { + id: message.request_id.clone().into(), + is_shadow_decryption: message.is_shadow_decryption, + other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), + }, decryption_job, decryption_transport) } /// When partial decryption is received. - pub fn on_partial_decryption(&self, sender: NodeId, message: &PartialDecryption) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(sender != self.self_node_id); + pub fn on_partial_decryption(&self, sender: &NodeId, message: &PartialDecryption) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); + data.consensus_session.on_job_response(sender, PartialDecryptionResponse { + request_id: message.request_id.clone().into(), + shadow_point: message.shadow_point.clone().into(), + decrypt_shadow: message.decrypt_shadow.clone(), + })?; - // check state - if data.state != SessionState::WaitingForPartialDecryption { - return Err(Error::InvalidStateForRequest); - } - - // remember partial signature - { - let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; - consensus.job_response_received(&sender, &message.request_id.clone().into(), PartialDecryptionResult { - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - })?; - - // check if we have enough shadow points to decrypt the secret - if !consensus.is_completed() { - return Ok(()); - } + if data.consensus_session.state() != ConsensusSessionState::Finished { + return Ok(()); } - // notify all other nodes about session completion - self.cluster.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), + self.core.cluster.broadcast(Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { + session: self.core.meta.id.clone().into(), + sub_session: self.core.access_key.clone().into(), })))?; - // do decryption - SessionImpl::do_decryption(&self.access_key, &self.encrypted_data, &mut *data)?; - self.completed.notify_all(); + data.result = Some(Ok(data.consensus_session.result()?)); + self.core.completed.notify_all(); Ok(()) } /// When session is completed. - pub fn on_session_completed(&self, sender: NodeId, message: &DecryptionSessionCompleted) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(sender != self.self_node_id); - - let mut data = self.data.lock(); - - if data.master != Some(sender) { - return Err(Error::InvalidMessage); - } - // it is up to master node to decide when to complete session - // => we could only fail if already failed - if data.state == SessionState::Failed { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.state = SessionState::Finished; + pub fn on_session_completed(&self, sender: &NodeId, message: &DecryptionSessionCompleted) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); - Ok(()) + self.data.lock().consensus_session.on_session_completed(sender) } /// When error has occured on another node. - pub fn on_session_error(&self, sender: NodeId, message: &DecryptionSessionError) -> Result<(), Error> { - let mut data = self.data.lock(); - - warn!("{}: decryption session failed with error: {:?} from {}", &self.self_node_id, message.error, sender); - - data.state = SessionState::Failed; - data.decrypted_secret = Some(Err(Error::Io(message.error.clone()))); - self.completed.notify_all(); - - Ok(()) + pub fn on_session_error(&self, sender: &NodeId, message: &DecryptionSessionError) -> Result<(), Error> { + self.process_node_error(Some(&sender), &message.error) } - /// Process nested consensus session action. - fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { - match action { - ConsensusSessionAction::BroadcastMessage(message) => { - cluster.broadcast(Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - })))? - }, - ConsensusSessionAction::SendMessage(to, message) => { - cluster.send(&to, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - })))? + /// Process error from the other node. + fn process_node_error(&self, node: Option<&NodeId>, error: &String) -> Result<(), Error> { + let mut data = self.data.lock(); + match { + match node { + Some(node) => data.consensus_session.on_node_error(node), + None => data.consensus_session.on_session_timeout(), + } + } { + Ok(false) => Ok(()), + Ok(true) => match self.core.disseminate_jobs(&mut data.consensus_session, + data.is_shadow_decryption.expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when is_shadow_decryption.is_some(); qed")) { + Ok(()) => Ok(()), + Err(err) => { + warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); + + data.result = Some(Err(err)); + self.core.completed.notify_all(); + Err(err) + } }, - ConsensusSessionAction::CheckStatus => (), - } + Err(err) => { + warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - match data.consensus_session.as_ref() - .expect("we are processing consensus session action; action is a result of processing message by session; qed") - .state() { - ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, - ConsensusSessionState::Failed => { - data.state = SessionState::Failed; - data.decrypted_secret = Some(Err(Error::ConsensusUnreachable)); - completed.notify_all(); + data.result = Some(Err(err)); + self.core.completed.notify_all(); + Err(err) }, - _ => (), } - - Ok(()) - } - - fn start_waiting_for_partial_decryption(self_node_id: &NodeId, session_id: &SessionId, access_key: &Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - if data.master.as_ref() != Some(self_node_id) { - // if we are on the slave node, wait for partial decryption requests - data.state = SessionState::WaitingForPartialDecryptionRequest; - return Ok(()); - } - - // update state - data.state = SessionState::WaitingForPartialDecryption; - - // send jobs to all selected nodes - let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial decryption phase follows initialization; qed"); - consensus.activate()?; - let (request_id, mut confirmed_nodes) = consensus.select_nodes(self_node_id).map(|(r, n)| (r.clone(), n.clone()))?; - - // send requests - for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { - consensus.job_request_sent(node)?; - cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { - session: session_id.clone().into(), - sub_session: access_key.clone().into(), - request_id: request_id.clone().into(), - is_shadow_decryption: data.is_shadow_decryption.expect("is_shadow_decryption on master node is filled in initialization; we are on master node; qed"), - nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), - })))?; - } - - // confirmation from this node, if this node is in consensus group - if confirmed_nodes.remove(self_node_id) { - let decryption_result = { - let requestor = data.requestor.as_ref().expect("requestor public is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); - let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; WaitingForPartialDecryption follows initialization; qed"); - do_partial_decryption(self_node_id, &requestor, is_shadow_decryption, &confirmed_nodes, access_key, &encrypted_data)? - }; - - consensus.job_request_sent(self_node_id)?; - consensus.job_response_received(self_node_id, &request_id, decryption_result)?; - } - - Ok(()) - } - - fn do_decryption(access_key: &Secret, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - // decrypt the secret using shadow points - let job_responses = data.consensus.as_ref() - .expect("consesus is filled in initialization phase; decryption phase follows initialization phase") - .job_responses()?; - let joint_shadow_point = math::compute_joint_shadow_point(job_responses.values().map(|s| &s.shadow_point))?; - let encrypted_point = encrypted_data.encrypted_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); - let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); - let decrypted_secret = math::decrypt_with_joint_shadow(encrypted_data.threshold, access_key, encrypted_point, &joint_shadow_point)?; - let is_shadow_decryption = data.is_shadow_decryption.expect("is_shadow_decryption is filled during initialization; decryption follows initialization; qed"); - let (common_point, decrypt_shadows) = if is_shadow_decryption { - ( - Some(math::make_common_shadow_point(encrypted_data.threshold, common_point.clone())?), - Some(job_responses.values() - .map(|s| s.decrypt_shadow.as_ref().expect("decrypt_shadow is filled during partial decryption; decryption follows partial decryption; qed").clone()) - .collect()) - ) - } else { - (None, None) - }; - data.decrypted_secret = Some(Ok(EncryptedDocumentKeyShadow { - decrypted_secret: decrypted_secret, - common_point: common_point, - decrypt_shadows: decrypt_shadows, - })); - - // switch to completed state - data.state = SessionState::Finished; - - Ok(()) } } impl ClusterSession for SessionImpl { fn is_finished(&self) -> bool { let data = self.data.lock(); - data.state == SessionState::Failed - || data.state == SessionState::Finished + data.consensus_session.state() == ConsensusSessionState::Failed + || data.consensus_session.state() == ConsensusSessionState::Finished } fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - let is_self_master = data.master.as_ref() == Some(&self.self_node_id); - let is_other_master = data.master.as_ref() == Some(node); - // if this is master node, we might have to restart - if is_self_master { - let is_restart_required = match data.consensus.as_mut() { - None => false, - Some(consensus) => match consensus.node_timeouted(node) { - Ok(false) => return, - Ok(true) => true, - Err(_) => false, //fall through - }, - }; - if is_restart_required { - if SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { - return; - } - } - } else if !is_other_master { - // disconnected from non-master node on non-master node - // => this does not affect this session - return; - } - // else: disconnecting from master node means failure - - warn!("{}: decryption session failed because {} connection has timeouted", &self.self_node_id, node); - - data.state = SessionState::Failed; - data.decrypted_secret = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + // ignore error, only state matters + let _ = self.process_node_error(Some(node), &Error::NodeDisconnected.into()); } fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - let is_self_master = data.master.as_ref() == Some(&self.self_node_id); - // if this is master node, we might have to restart - if is_self_master { - let is_restart_required = match data.consensus.as_mut() { - None => false, - Some(consensus) => match consensus.session_timeouted() { - Ok(_) => true, - Err(_) => false, - }, - }; - if is_restart_required { - if SessionImpl::start_waiting_for_partial_decryption(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { - return; - } - } - } - - warn!("{}: decryption session failed with timeout", &self.self_node_id); - - data.state = SessionState::Failed; - data.decrypted_secret = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + // ignore error, only state matters + let _ = self.process_node_error(None, &Error::NodeDisconnected.into()); } } impl Session for SessionImpl { fn wait(&self) -> Result { let mut data = self.data.lock(); - if !data.decrypted_secret.is_some() { + if !data.result.is_some() { self.completed.wait(&mut data); } - data.decrypted_secret.as_ref() - .expect("checked above or waited for completed; completed is only signaled when decrypted_secret.is_some(); qed") + data.result.as_ref() + .expect("checked above or waited for completed; completed is only signaled when result.is_some(); qed") .clone() } } +impl SessionCore { + pub fn decryption_transport(&self) -> DecryptionJobTransport { + DecryptionJobTransport { + id: self.meta.id.clone(), + access_key: self.access_key.clone(), + cluster: self.cluster.clone() + } + } + + pub fn disseminate_jobs(&self, consensus_session: &mut DecryptionConsensusSession, is_shadow_decryption: bool) -> Result<(), Error> { + let decryption_job = DecryptionJob::new_on_master(self.meta.self_node_id.clone(), self.access_key.clone(), consensus_session.requester().clone(), self.key_share.clone(), is_shadow_decryption); + consensus_session.disseminate_jobs(decryption_job, self.decryption_transport()) + } +} + impl DecryptionSessionId { /// Create new decryption session Id. pub fn new(session_id: SessionId, sub_session_id: Secret) -> Self { @@ -628,7 +449,6 @@ impl PartialOrd for DecryptionSessionId { } } - impl Ord for DecryptionSessionId { fn cmp(&self, other: &Self) -> Ordering { match self.id.cmp(&other.id) { @@ -638,39 +458,6 @@ impl Ord for DecryptionSessionId { } } -fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { - use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; - - // check that common_point and encrypted_point are already set - if encrypted_data.common_point.is_none() || encrypted_data.encrypted_point.is_none() { - return Err(Error::NotStartedSessionId); - } - - let nodes = encrypted_data.id_numbers.keys().cloned().collect(); - check_cluster_nodes(self_node_id, &nodes)?; - check_threshold(encrypted_data.threshold, &nodes) -} - - -fn do_partial_decryption(node: &NodeId, requestor_public: &Public, is_shadow_decryption: bool, participants: &BTreeSet, access_key: &Secret, encrypted_data: &DocumentKeyShare) -> Result { - let node_id_number = &encrypted_data.id_numbers[node]; - let node_secret_share = &encrypted_data.secret_share; - let other_id_numbers = participants.iter() - .filter(|id| *id != node) - .map(|id| &encrypted_data.id_numbers[id]); - let node_shadow = math::compute_node_shadow(node_secret_share, node_id_number, other_id_numbers)?; - let decrypt_shadow = if is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; - let common_point = encrypted_data.common_point.as_ref().expect("checked at the beginning of the session; immutable; qed"); - let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(access_key, common_point, &node_shadow, decrypt_shadow)?; - Ok(PartialDecryptionResult { - shadow_point: shadow_point, - decrypt_shadow: match decrypt_shadow { - None => None, - Some(decrypt_shadow) => Some(encrypt(requestor_public, &DEFAULT_MAC, &**decrypt_shadow)?), - }, - }) -} - #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs index 50236278eaa..7d423fb702c 100644 --- a/secret_store/src/key_server_cluster/jobs/consensus_session.rs +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -1,7 +1,7 @@ use std::marker::PhantomData; use std::collections::BTreeSet; use std::sync::Arc; -use ethkey::Signature; +use ethkey::{Public, Signature, recover}; use key_server_cluster::{Error, NodeId, SessionMeta, AclStorage}; use key_server_cluster::message::ConsensusMessage; use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor}; @@ -17,22 +17,25 @@ pub enum ConsensusSessionState { Failed, } -pub struct ConsensusSession<'a, ConsensusTransport: JobTransport, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport> { +pub struct ConsensusSession, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport> { state: ConsensusSessionState, - meta: &'a SessionMeta, - consensus_job: JobSession<'a, KeyAccessJob, ConsensusTransport>, - computation_job: Option>, - dummy: PhantomData<(ComputationTransport, ComputationExecutor)>, + meta: SessionMeta, + requester: Option, + consensus_job: JobSession, + computation_job: Option>, + //dummy: PhantomData<(ComputationTransport, ComputationExecutor)>, } -pub struct ConsensusSessionParams<'a, ConsensusTransport: JobTransport> { - meta: &'a SessionMeta, +pub struct ConsensusSessionParams> { + meta: SessionMeta, acl_storage: Arc, consensus_transport: ConsensusTransport, } -impl<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> ConsensusSession<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> where ConsensusTransport: JobTransport, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport { - pub fn new_on_slave(params: ConsensusSessionParams<'a, ConsensusTransport>) -> Result { +impl ConsensusSession where ConsensusTransport: JobTransport, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport { + pub fn new_on_slave(params: ConsensusSessionParams) -> Result { + debug_assert!(params.meta.self_node_id != params.meta.master_node_id); + let consensus_job_executor = KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage); let consensus_job = JobSession::new(params.meta, consensus_job_executor, params.consensus_transport); debug_assert!(consensus_job.state() == JobSessionState::Inactive); @@ -40,34 +43,62 @@ impl<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> Consensu Ok(ConsensusSession { state: ConsensusSessionState::WaitingForInitialization, meta: params.meta, + requester: None, consensus_job: consensus_job, computation_job: None, - dummy: PhantomData, + //dummy: PhantomData, }) } - pub fn new_on_master(params: ConsensusSessionParams<'a, ConsensusTransport>, signature: Signature) -> Result { + pub fn new_on_master(params: ConsensusSessionParams, signature: Signature) -> Result { + debug_assert!(params.meta.self_node_id == params.meta.master_node_id); + + let requester = recover(&signature, ¶ms.meta.id)?; let consensus_job_executor = KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage, signature); let consensus_job = JobSession::new(params.meta, consensus_job_executor, params.consensus_transport); Ok(ConsensusSession { state: ConsensusSessionState::WaitingForInitialization, meta: params.meta, + requester: Some(requester), consensus_job: consensus_job, computation_job: None, dummy: PhantomData, }) } + pub fn state(&self) -> ConsensusSessionState { + self.state + } + + pub fn requester(&self) -> Result<&Public, Error> { + self.requester.as_ref().ok_or(Error::InvalidStateForRequest) + } + + pub fn result(&self) -> Result { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); + if self.state != ConsensusSessionState::Finished { + return Err(Error::InvalidStateForRequest); + } + + self.computation_job + .expect("we are on master node in finished state; computation_job is set on master node during initialization; qed") + .result() + } + pub fn initialize(&mut self, nodes: BTreeSet) -> Result<(), Error> { let initialization_result = self.consensus_job.initialize(nodes); + self.state = ConsensusSessionState::EstablishingConsensus; self.process_result(initialization_result) } pub fn on_consensus_message(&mut self, sender: &NodeId, message: &ConsensusMessage) -> Result<(), Error> { let consensus_result = match message { - &ConsensusMessage::InitializeConsensusSession(ref message) => - self.consensus_job.on_partial_request(sender, message.requestor_signature.clone().into()), + &ConsensusMessage::InitializeConsensusSession(ref message) => { + let signature = message.requestor_signature.clone().into(); + self.requester = Some(recover(&signature, &self.meta.id)?); + self.consensus_job.on_partial_request(sender, signature) + }, &ConsensusMessage::ConfirmConsensusInitialization(ref message) => self.consensus_job.on_partial_response(sender, message.is_confirmed), }; @@ -91,12 +122,10 @@ impl<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> Consensu } let mut computation_job = JobSession::new(self.meta, executor, transport); - computation_job.initialize(consensus_nodes)?; - + let computation_result = computation_job.initialize(consensus_nodes); self.computation_job = Some(computation_job); self.state = ConsensusSessionState::WaitingForPartialResults; - - Ok(()) + self.process_result(computation_result) } pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { @@ -123,7 +152,20 @@ impl<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> Consensu self.process_result(computation_result) } - pub fn on_node_timeout(&mut self, node: &NodeId) -> Result<(), Error> { + pub fn on_session_completed(&mut self, node: &NodeId) -> Result<(), Error> { + if node != &self.meta.master_node_id { + return Err(Error::InvalidMessage); + } + if self.state != ConsensusSessionState::ConsensusEstablished { + return Err(Error::InvalidStateForRequest); + } + + self.state = ConsensusSessionState::Finished; + + Ok(()) + } + + pub fn on_node_error(&mut self, node: &NodeId) -> Result { let timeout_result = match self.state { ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Finished | ConsensusSessionState::Failed => Err(Error::ConsensusUnreachable), @@ -144,7 +186,7 @@ impl<'a, ConsensusTransport, ComputationExecutor, ComputationTransport> Consensu self.process_result(timeout_result) } - pub fn on_session_timeout(&mut self) -> Result<(), Error> { + pub fn on_session_timeout(&mut self) -> Result { match self.state { ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Finished | ConsensusSessionState::Failed | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => diff --git a/secret_store/src/key_server_cluster/jobs/decryption_job.rs b/secret_store/src/key_server_cluster/jobs/decryption_job.rs index e69de29bb2d..a6c59d9d8b9 100644 --- a/secret_store/src/key_server_cluster/jobs/decryption_job.rs +++ b/secret_store/src/key_server_cluster/jobs/decryption_job.rs @@ -0,0 +1,146 @@ +use std::sync::Arc; +use std::collections::{BTreeSet, BTreeMap}; +use ethkey::{Public, Secret}; +use ethcrypto::ecies::encrypt; +use ethcrypto::DEFAULT_MAC; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare, EncryptedDocumentKeyShadow}; +use key_server_cluster::math; +use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobExecutor}; + +/// Decryption job. +pub struct DecryptionJob { + /// This node id. + self_node_id: NodeId, + /// Access key. + access_key: Secret, + /// Requester public key. + requester: Public, + /// Key share. + key_share: DocumentKeyShare, + /// Request id. + request_id: Option, + /// Is shadow decryption requested. + is_shadow_decryption: Option, + /// Id of nodes, participating in decryption. + participants: Option>, +} + +/// Decryption job partial request. +pub struct PartialDecryptionRequest { + /// Request id. + pub id: Secret, + /// Is shadow decryption requested. + pub is_shadow_decryption: bool, + /// Id of other nodes, participating in decryption. + pub other_nodes_ids: BTreeSet, +} + +/// Decryption job partial response. +pub struct PartialDecryptionResponse { + /// Request id. + pub request_id: Secret, + /// Shadow point. + pub shadow_point: Public, + /// Decryption shadow coefficient, if requested. + pub decrypt_shadow: Option>, +} + +impl DecryptionJob { + pub fn new_on_slave(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare) -> Self { + debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); + DecryptionJob { + self_node_id: self_node_id, + access_key: access_key, + requester: requester, + key_share: key_share, + request_id: None, + is_shadow_decryption: None, + participants: None, + } + } + + pub fn new_on_master(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, is_shadow_decryption: bool, participants: BTreeSet) -> Self { + debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); + DecryptionJob { + self_node_id: self_node_id, + access_key: access_key, + requester: requester, + key_share: key_share, + is_shadow_decryption: Some(is_shadow_decryption), + participants: Some(participants), + } + } +} + +impl JobExecutor for DecryptionJob { + type PartialJobRequest = PartialDecryptionRequest; + type PartialJobResponse = PartialDecryptionResponse; + type JobResponse = EncryptedDocumentKeyShadow; + + fn prepare_partial_request(&self, node: &NodeId) -> Result { + let is_shadow_decryption = self.is_shadow_decryption + .expect("prepare_partial_request is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); + let mut other_nodes_ids = self.participants.as_ref() + .expect("prepare_partial_request is only called on master nodes; participants is filed in constructor on master nodes; qed") + .clone(); + other_nodes_ids.remove(node); + + Ok(PartialDecryptionRequest { + is_shadow_decryption: is_shadow_decryption, + other_nodes_ids: other_nodes_ids, + }) + } + + fn process_partial_request(&self, partial_request: PartialDecryptionRequest) -> Result { + if partial_request.other_nodes_ids.len() != self.key_share.threshold + || partial_request.other_nodes_ids.contains(&self.self_node_id) + || partial_request.other_nodes_ids.iter().any(|n| !self.key_share.id_numbers.contains_key(n)) { + return Err(Error::InvalidMessage); + } + + let self_id_number = &self.key_share.id_numbers[&self.self_node_id]; + let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &self.key_share.id_numbers[n]); + let node_shadow = math::compute_node_shadow(&self.key_share.secret_share, &self_id_number, other_id_numbers)?; + let decrypt_shadow = if partial_request.is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; + let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); + let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(&self.access_key, &common_point, &node_shadow, decrypt_shadow)?; + Ok(PartialDecryptionResponse { + shadow_point: shadow_point, + decrypt_shadow: match decrypt_shadow { + None => None, + Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, &**decrypt_shadow)?), + }, + }) + } + + fn check_partial_response(&self, partial_response: &PartialDecryptionResponse) -> Result { + if Some(partial_response.request_id) != self.request_id { + return Ok(JobPartialResponseAction::Ignore); + } + if self.is_shadow_decryption != Some(partial_response.decrypt_shadow.is_some()) { + return Ok(JobPartialResponseAction::Reject); + } + Ok(JobPartialResponseAction::Accept) + } + + fn compute_response(&self, partial_responses: &BTreeMap) -> Result { + let is_shadow_decryption = self.is_shadow_decryption + .expect("compute_response is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); + let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); + let encrypted_point = self.key_share.encrypted_point.as_ref().expect("DecryptionJob is only created when encrypted_point is known; qed"); + let joint_shadow_point = math::compute_joint_shadow_point(partial_responses.values().map(|s| &s.shadow_point))?; + let decrypted_secret = math::decrypt_with_joint_shadow(self.key_share.threshold, &self.access_key, encrypted_point, &joint_shadow_point)?; + Ok(EncryptedDocumentKeyShadow { + decrypted_secret: decrypted_secret, + common_point: if is_shadow_decryption { + Some(math::make_common_shadow_point(self.key_share.threshold, common_point.clone())?) + } else { None }, + decrypt_shadows: if is_shadow_decryption { + Some(partial_responses.values().map(|r| r.decrypt_shadow.as_ref() + .expect("is_shadow_decryption == true; decrypt_shadow.is_some() is checked in check_partial_response; qed") + .clone()) + .collect()) + } else { None }, + }) + } +} diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs index 0062fb91d20..877525e32e3 100644 --- a/secret_store/src/key_server_cluster/jobs/job_session.rs +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -2,6 +2,17 @@ use std::marker::PhantomData; use std::collections::{BTreeSet, BTreeMap}; use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; +#[derive(Debug, Clone, Copy, PartialEq)] +/// Partial rsponse action. +pub enum JobPartialResponseAction { + /// Ignore this response. + Ignore, + /// Mark this response as reject. + Reject, + /// Accept this response. + Accept, +} + /// Job executor. pub trait JobExecutor { type PartialJobRequest; @@ -9,11 +20,11 @@ pub trait JobExecutor { type JobResponse; /// Prepare job request for given node. - fn prepare_partial_request(&self) -> Result; + fn prepare_partial_request(&self, node: &NodeId) -> Result; /// Process partial request. fn process_partial_request(&self, partial_request: Self::PartialJobRequest) -> Result; /// Check partial response of given node. - fn check_partial_response(&self, partial_response: &Self::PartialJobResponse) -> Result; + fn check_partial_response(&self, partial_response: &Self::PartialJobResponse) -> Result; /// Compute final job response. fn compute_response(&self, partial_responses: &BTreeMap) -> Result; } @@ -43,9 +54,9 @@ pub enum JobSessionState { } /// Basic request-response session on a set of nodes. -pub struct JobSession<'a, Executor: JobExecutor, Transport> where Transport: JobTransport { +pub struct JobSession where Transport: JobTransport { /// Session meta. - meta: &'a SessionMeta, + meta: SessionMeta, /// Job executor. executor: Executor, /// Jobs transport. @@ -74,9 +85,9 @@ struct ActiveJobSessionData { responses: BTreeMap, } -impl<'a, Executor, Transport> JobSession<'a, Executor, Transport> where Executor: JobExecutor, Transport: JobTransport { +impl JobSession where Executor: JobExecutor, Transport: JobTransport { /// Create new session. - pub fn new(meta: &'a SessionMeta, executor: Executor, transport: Transport) -> Self { + pub fn new(meta: SessionMeta, executor: Executor, transport: Transport) -> Self { JobSession { meta: meta, executor: executor, @@ -139,7 +150,7 @@ impl<'a, Executor, Transport> JobSession<'a, Executor, Transport> where Executor }; for node in &active_data.requests { if node != &self.meta.self_node_id { - self.transport.send_partial_request(&node, self.executor.prepare_partial_request()?)?; + self.transport.send_partial_request(&node, self.executor.prepare_partial_request(node)?)?; } else { waits_for_self = true; } @@ -151,7 +162,8 @@ impl<'a, Executor, Transport> JobSession<'a, Executor, Transport> where Executor // if we are waiting for response from self => do it if waits_for_self { - let partial_response = self.executor.process_partial_request(self.executor.prepare_partial_request()?)?; + let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id)?; + let partial_response = self.executor.process_partial_request(partial_request)?; self.on_partial_response(&self.meta.self_node_id, partial_response)?; } @@ -189,23 +201,27 @@ impl<'a, Executor, Transport> JobSession<'a, Executor, Transport> where Executor return Err(Error::InvalidNodeForRequest); } - if !self.executor.check_partial_response(&response).unwrap_or(false) { - active_data.rejects.insert(node.clone()); - if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - Err(Error::ConsensusUnreachable) - } else { - active_data.responses.insert(node.clone(), response); + match self.executor.check_partial_response(&response)? { + JobPartialResponseAction::Ignore => Ok(()), + JobPartialResponseAction::Reject => { + active_data.rejects.insert(node.clone()); + if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { + return Ok(()); + } + + self.data.state = JobSessionState::Failed; + Err(Error::ConsensusUnreachable) + }, + JobPartialResponseAction::Accept => { + active_data.responses.insert(node.clone(), response); - if active_data.responses.len() < self.meta.threshold + 1 { - return Ok(()); - } + if active_data.responses.len() < self.meta.threshold + 1 { + return Ok(()); + } - self.data.state = JobSessionState::Finished; - Ok(()) + self.data.state = JobSessionState::Finished; + Ok(()) + }, } } @@ -251,7 +267,7 @@ mod tests { use parking_lot::Mutex; use ethkey::Public; use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, DocumentKeyShare}; - use super::{JobExecutor, JobTransport, JobSession, JobSessionState}; + use super::{JobPartialResponseAction, JobExecutor, JobTransport, JobSession, JobSessionState}; struct SquaredSumJobExecutor; @@ -260,9 +276,9 @@ mod tests { type PartialJobResponse = u32; type JobResponse = u32; - fn prepare_partial_request(&self) -> Result { Ok(2) } + fn prepare_partial_request(&self, _n: &NodeId) -> Result { Ok(2) } fn process_partial_request(&self, r: u32) -> Result { Ok(r * r) } - fn check_partial_response(&self, r: &u32) -> Result { Ok(r % 2 == 0) } + fn check_partial_response(&self, r: &u32) -> Result { if r % 2 == 0 { Ok(JobPartialResponseAction::Accept) } else { Ok(JobPartialResponseAction::Reject) } } fn compute_response(&self, r: &BTreeMap) -> Result { Ok(r.values().fold(0, |v1, v2| v1 + v2)) } } diff --git a/secret_store/src/key_server_cluster/jobs/key_access_job.rs b/secret_store/src/key_server_cluster/jobs/key_access_job.rs index 87daf934074..ae125aca654 100644 --- a/secret_store/src/key_server_cluster/jobs/key_access_job.rs +++ b/secret_store/src/key_server_cluster/jobs/key_access_job.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethkey::{Signature, recover}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage}; -use key_server_cluster::jobs::job_session::JobExecutor; +use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobExecutor}; /// Purpose of this job is to construct set of nodes, which have agreed to provide access to the given key for the given requestor. pub struct KeyAccessJob { @@ -37,7 +37,7 @@ impl JobExecutor for KeyAccessJob { type PartialJobResponse = bool; type JobResponse = BTreeSet; - fn prepare_partial_request(&self) -> Result { + fn prepare_partial_request(&self, _node: &NodeId) -> Result { Ok(self.signature.as_ref().expect("prepare_partial_request is only called on master nodes; new_on_master fills the signature; qed").clone()) } @@ -45,8 +45,8 @@ impl JobExecutor for KeyAccessJob { self.acl_storage.check(&recover(&partial_request, &self.id)?, &self.id).map_err(|_| Error::AccessDenied) } - fn check_partial_response(&self, partial_response: &bool) -> Result { - Ok(*partial_response) + fn check_partial_response(&self, partial_response: &bool) -> Result { + Ok(if *partial_response { JobPartialResponseAction::Accept } else { JobPartialResponseAction::Reject }) } fn compute_response(&self, partial_responses: &BTreeMap) -> Result, Error> { diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index ae5de0cb1c8..10acb33c51e 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -36,6 +36,7 @@ pub use super::acl_storage::tests::DummyAclStorage; pub type SessionId = ServerKeyId; +#[derive(Debug, Clone)] /// Session metadata. pub struct SessionMeta { /// Key id. From a596c826b54fd1a67e7b83c316a1461152047c5a Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Fri, 2 Jun 2017 17:54:55 +0300 Subject: [PATCH 37/45] consensus session tets --- .../src/key_server_cluster/cluster.rs | 2 +- .../key_server_cluster/decryption_session.rs | 265 ++++---- .../jobs/consensus_session.rs | 574 ++++++++++++++++-- .../key_server_cluster/jobs/decryption_job.rs | 34 +- .../key_server_cluster/jobs/job_session.rs | 184 +++--- .../key_server_cluster/jobs/key_access_job.rs | 2 +- 6 files changed, 781 insertions(+), 280 deletions(-) diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 2c9da8f11b6..44247c9494f 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -916,7 +916,7 @@ impl ClusterClient for ClusterClientImpl { let access_key = Random.generate()?.secret().clone(); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let session = self.data.sessions.new_decryption_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster, Some(requestor_signature))?; - session.initialize(requestor_signature, is_shadow_decryption)?; + session.initialize(is_shadow_decryption)?; Ok(DecryptionSessionWrapper::new(Arc::downgrade(&self.data), DecryptionSessionId::new(session_id, access_key), session)) } diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 9071a063d8d..b1757060c87 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -15,23 +15,16 @@ // along with Parity. If not, see . use std::cmp::{Ord, PartialOrd, Ordering}; -use std::collections::BTreeSet; -use std::ops::DerefMut; use std::sync::Arc; use parking_lot::{Mutex, Condvar}; -use ethcrypto::ecies::encrypt; -use ethcrypto::DEFAULT_MAC; -use ethkey::{self, Secret, Public, Signature}; +use ethkey::{Secret, Signature}; use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::ClusterSession; -use key_server_cluster::consensus::Consensus; -use key_server_cluster::math; use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; -use key_server_cluster::jobs::job_session::{JobExecutor, JobTransport}; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; +use key_server_cluster::jobs::job_session::JobTransport; use key_server_cluster::jobs::decryption_job::{PartialDecryptionRequest, PartialDecryptionResponse, DecryptionJob}; use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; @@ -178,7 +171,8 @@ impl JobTransport for DecryptionJobTransport { impl SessionImpl { /// Create new decryption session. pub fn new(params: SessionParams, requester_signature: Option) -> Result { - debug_assert!((params.meta.self_node_id == params.meta.master_node_id) == requester_signature.is_some()); + debug_assert_eq!(params.meta.threshold, params.key_share.threshold); + debug_assert_eq!(params.meta.self_node_id == params.meta.master_node_id, requester_signature.is_some()); use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; @@ -200,7 +194,7 @@ impl SessionImpl { Ok(SessionImpl { core: SessionCore { - meta: params.meta, + meta: params.meta.clone(), access_key: params.access_key, key_share: params.key_share, cluster: params.cluster, @@ -209,12 +203,12 @@ impl SessionImpl { data: Mutex::new(SessionData { consensus_session: match requester_signature { Some(requester_signature) => ConsensusSession::new_on_master(ConsensusSessionParams { - meta: params.meta.clone(), + meta: params.meta, acl_storage: params.acl_storage.clone(), consensus_transport: consensus_transport, }, requester_signature)?, None => ConsensusSession::new_on_slave(ConsensusSessionParams { - meta: params.meta.clone(), + meta: params.meta, acl_storage: params.acl_storage.clone(), consensus_transport: consensus_transport, })?, @@ -237,6 +231,12 @@ impl SessionImpl { &self.core.access_key } + #[cfg(test)] + /// Get session state. + pub fn state(&self) -> ConsensusSessionState { + self.data.lock().consensus_session.state() + } + #[cfg(test)] /// Get decrypted secret pub fn decrypted_secret(&self) -> Option> { @@ -244,15 +244,15 @@ impl SessionImpl { } /// Initialize decryption session on master node. - pub fn initialize(&self, requestor_signature: Signature, is_shadow_decryption: bool) -> Result<(), Error> { + pub fn initialize(&self, is_shadow_decryption: bool) -> Result<(), Error> { let mut data = self.data.lock(); + data.is_shadow_decryption = Some(is_shadow_decryption); data.consensus_session.initialize(self.core.key_share.id_numbers.keys().cloned().collect())?; if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { self.core.disseminate_jobs(&mut data.consensus_session, is_shadow_decryption)?; debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); - data.is_shadow_decryption = Some(is_shadow_decryption); data.result = Some(Ok(data.consensus_session.result()?)); self.core.completed.notify_all(); } @@ -303,7 +303,7 @@ impl SessionImpl { let mut data = self.data.lock(); let requester = data.consensus_session.requester()?.clone(); - let decryption_job = DecryptionJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.access_key.clone(), requester, self.core.key_share.clone()); + let decryption_job = DecryptionJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.access_key.clone(), requester, self.core.key_share.clone())?; let decryption_transport = self.core.decryption_transport(); data.consensus_session.on_job_request(&sender, PartialDecryptionRequest { @@ -365,21 +365,24 @@ impl SessionImpl { } } { Ok(false) => Ok(()), - Ok(true) => match self.core.disseminate_jobs(&mut data.consensus_session, - data.is_shadow_decryption.expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when is_shadow_decryption.is_some(); qed")) { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - - data.result = Some(Err(err)); - self.core.completed.notify_all(); - Err(err) + Ok(true) => { + let is_shadow_decryption = data.is_shadow_decryption.expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when is_shadow_decryption.is_some(); qed"); + let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, is_shadow_decryption); + match disseminate_result { + Ok(()) => Ok(()), + Err(err) => { + warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); + + data.result = Some(Err(err.clone())); + self.core.completed.notify_all(); + Err(err) + } } }, Err(err) => { warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - data.result = Some(Err(err)); + data.result = Some(Err(err.clone())); self.core.completed.notify_all(); Err(err) }, @@ -409,7 +412,7 @@ impl Session for SessionImpl { fn wait(&self) -> Result { let mut data = self.data.lock(); if !data.result.is_some() { - self.completed.wait(&mut data); + self.core.completed.wait(&mut data); } data.result.as_ref() @@ -428,7 +431,8 @@ impl SessionCore { } pub fn disseminate_jobs(&self, consensus_session: &mut DecryptionConsensusSession, is_shadow_decryption: bool) -> Result<(), Error> { - let decryption_job = DecryptionJob::new_on_master(self.meta.self_node_id.clone(), self.access_key.clone(), consensus_session.requester().clone(), self.key_share.clone(), is_shadow_decryption); + let requester = consensus_session.requester()?.clone(); + let decryption_job = DecryptionJob::new_on_master(self.meta.self_node_id.clone(), self.access_key.clone(), requester, self.key_share.clone(), is_shadow_decryption)?; consensus_session.disseminate_jobs(decryption_job, self.decryption_transport()) } } @@ -463,17 +467,18 @@ mod tests { use std::sync::Arc; use std::collections::BTreeMap; use super::super::super::acl_storage::tests::DummyAclStorage; - use ethkey::{self, Random, Generator, Public, Secret}; - use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow}; + use ethkey::{self, KeyPair, Random, Generator, Public, Secret}; + use key_server_cluster::{NodeId, DocumentKeyShare, SessionId, Error, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::decryption_session::{SessionImpl, SessionParams, SessionState}; + use key_server_cluster::decryption_session::{SessionImpl, SessionParams}; use key_server_cluster::message::{self, Message, DecryptionMessage}; use key_server_cluster::math; + use key_server_cluster::jobs::consensus_session::ConsensusSessionState; const SECRET_PLAIN: &'static str = "d2b57ae7619e070af0af6bc8c703c0cd27814c54d5d6a999cacac0da34ede279ca0d9216e85991029e54e2f0c92ee0bd30237725fa765cbdbfc4529489864c5f"; - fn prepare_decryption_sessions() -> (Vec>, Vec>, Vec) { + fn prepare_decryption_sessions() -> (KeyPair, Vec>, Vec>, Vec) { // prepare encrypted data + cluster configuration for scheme 4-of-5 let session_id = SessionId::default(); let access_key = Random.generate().unwrap().secret().clone(); @@ -514,16 +519,22 @@ mod tests { } cluster }).collect(); + let requester = Random.generate().unwrap(); + let signature = Some(ethkey::sign(requester.secret(), &SessionId::default()).unwrap()); let sessions: Vec<_> = (0..5).map(|i| SessionImpl::new(SessionParams { - id: session_id.clone(), + meta: SessionMeta { + id: session_id.clone(), + self_node_id: id_numbers.iter().nth(i).clone().unwrap().0, + master_node_id: id_numbers.iter().nth(0).clone().unwrap().0, + threshold: encrypted_datas[i].threshold, + }, access_key: access_key.clone(), - self_node_id: id_numbers.iter().nth(i).clone().unwrap().0, - encrypted_data: encrypted_datas[i].clone(), + key_share: encrypted_datas[i].clone(), acl_storage: acl_storages[i].clone(), cluster: clusters[i].clone() - }).unwrap()).collect(); + }, if i == 0 { signature.clone() } else { None }).unwrap()).collect(); - (clusters, acl_storages, sessions) + (requester, clusters, acl_storages, sessions) } fn do_messages_exchange(clusters: &[Arc], sessions: &[SessionImpl]) { @@ -538,11 +549,8 @@ mod tests { } match message { - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(message)) => session.on_consensus_message(from, &message).unwrap(), - Message::Decryption(DecryptionMessage::RequestPartialDecryption(message)) => session.on_partial_decryption_requested(from, &message).unwrap(), - Message::Decryption(DecryptionMessage::PartialDecryption(message)) => session.on_partial_decryption(from, &message).unwrap(), - Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(message)) => session.on_session_completed(from, &message).unwrap(), - _ => panic!("unexpected"), + Message::Decryption(message) => session.process_message(&from, &message).unwrap(), + _ => unreachable!(), } } } @@ -553,10 +561,14 @@ mod tests { let self_node_id = Random.generate().unwrap().public().clone(); nodes.insert(self_node_id, Random.generate().unwrap().secret().clone()); match SessionImpl::new(SessionParams { - id: SessionId::default(), + meta: SessionMeta { + id: SessionId::default(), + self_node_id: self_node_id.clone(), + master_node_id: self_node_id.clone(), + threshold: 0, + }, access_key: Random.generate().unwrap().secret().clone(), - self_node_id: self_node_id.clone(), - encrypted_data: DocumentKeyShare { + key_share: DocumentKeyShare { author: Public::default(), threshold: 0, id_numbers: nodes, @@ -566,7 +578,7 @@ mod tests { }, acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - }) { + }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { Ok(_) => (), _ => panic!("unexpected"), } @@ -579,10 +591,14 @@ mod tests { nodes.insert(Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()); nodes.insert(Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()); match SessionImpl::new(SessionParams { - id: SessionId::default(), + meta: SessionMeta { + id: SessionId::default(), + self_node_id: self_node_id.clone(), + master_node_id: self_node_id.clone(), + threshold: 0, + }, access_key: Random.generate().unwrap().secret().clone(), - self_node_id: self_node_id.clone(), - encrypted_data: DocumentKeyShare { + key_share: DocumentKeyShare { author: Public::default(), threshold: 0, id_numbers: nodes, @@ -592,7 +608,7 @@ mod tests { }, acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - }) { + }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { Err(Error::InvalidNodesConfiguration) => (), _ => panic!("unexpected"), } @@ -605,10 +621,14 @@ mod tests { nodes.insert(self_node_id.clone(), Random.generate().unwrap().secret().clone()); nodes.insert(Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()); match SessionImpl::new(SessionParams { - id: SessionId::default(), + meta: SessionMeta { + id: SessionId::default(), + self_node_id: self_node_id.clone(), + master_node_id: self_node_id.clone(), + threshold: 2, + }, access_key: Random.generate().unwrap().secret().clone(), - self_node_id: self_node_id.clone(), - encrypted_data: DocumentKeyShare { + key_share: DocumentKeyShare { author: Public::default(), threshold: 2, id_numbers: nodes, @@ -618,7 +638,7 @@ mod tests { }, acl_storage: Arc::new(DummyAclStorage::default()), cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - }) { + }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { Err(Error::InvalidThreshold) => (), _ => panic!("unexpected"), } @@ -626,16 +646,16 @@ mod tests { #[test] fn fails_to_initialize_when_already_initialized() { - let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(), ()); - assert_eq!(sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap_err(), Error::InvalidStateForRequest); + let (_, _, _, sessions) = prepare_decryption_sessions(); + assert_eq!(sessions[0].initialize(false).unwrap(), ()); + assert_eq!(sessions[0].initialize(false).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn fails_to_accept_initialization_when_already_initialized() { - let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(), ()); - assert_eq!(sessions[0].on_consensus_message(sessions[1].node().clone(), &message::DecryptionConsensusMessage { + let (_, _, _, sessions) = prepare_decryption_sessions(); + assert_eq!(sessions[0].initialize(false).unwrap(), ()); + assert_eq!(sessions[0].on_consensus_message(sessions[1].node(), &message::DecryptionConsensusMessage { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { @@ -646,15 +666,15 @@ mod tests { #[test] fn fails_to_partial_decrypt_if_requested_by_slave() { - let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_consensus_message(sessions[0].node().clone(), &message::DecryptionConsensusMessage { + let (_, _, _, sessions) = prepare_decryption_sessions(); + assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), }), }).unwrap(), ()); - assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node().clone(), &message::RequestPartialDecryption { + assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node(), &message::RequestPartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), request_id: Random.generate().unwrap().secret().clone().into(), @@ -665,15 +685,15 @@ mod tests { #[test] fn fails_to_partial_decrypt_if_wrong_number_of_nodes_participating() { - let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_consensus_message(sessions[0].node().clone(), &message::DecryptionConsensusMessage { + let (_, _, _, sessions) = prepare_decryption_sessions(); + assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), }), }).unwrap(), ()); - assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node().clone(), &message::RequestPartialDecryption { + assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node(), &message::RequestPartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), request_id: Random.generate().unwrap().secret().clone().into(), @@ -684,8 +704,8 @@ mod tests { #[test] fn fails_to_accept_partial_decrypt_if_not_waiting() { - let (_, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].on_partial_decryption(sessions[1].node().clone(), &message::PartialDecryption { + let (_, _, _, sessions) = prepare_decryption_sessions(); + assert_eq!(sessions[0].on_partial_decryption(sessions[1].node(), &message::PartialDecryption { session: SessionId::default().into(), sub_session: sessions[0].access_key().clone().into(), request_id: Random.generate().unwrap().secret().clone().into(), @@ -696,8 +716,8 @@ mod tests { #[test] fn fails_to_accept_partial_decrypt_twice() { - let (clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); + let (_, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(false).unwrap(); let mut pd_from = None; let mut pd_msg = None; @@ -710,13 +730,13 @@ mod tests { _ => false, }); - assert_eq!(sessions[0].on_partial_decryption(pd_from.clone().unwrap(), &pd_msg.clone().unwrap()).unwrap(), ()); - assert_eq!(sessions[0].on_partial_decryption(pd_from.unwrap(), &pd_msg.unwrap()).unwrap_err(), Error::InvalidStateForRequest); + assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.clone().unwrap()).unwrap(), ()); + assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.unwrap()).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn decryption_fails_on_session_timeout() { - let (_, _, sessions) = prepare_decryption_sessions(); + let (_, _, _, sessions) = prepare_decryption_sessions(); assert!(sessions[0].decrypted_secret().is_none()); sessions[0].on_session_timeout(); assert!(sessions[0].decrypted_secret().unwrap().unwrap_err() == Error::NodeDisconnected); @@ -724,110 +744,107 @@ mod tests { #[test] fn node_is_marked_rejected_when_timed_out_during_initialization_confirmation() { - let (_, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); + let (_, _, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(false).unwrap(); // 1 node disconnects => we still can recover secret sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().unwrap().rejected_nodes().contains(sessions[1].node())); - assert!(sessions[0].data.lock().state == SessionState::EstablishingConsensus); + assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains(sessions[1].node())); + assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); // 2 node are disconnected => we can not recover secret sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().is_none()); - assert!(sessions[0].data.lock().state == SessionState::Failed); + assert!(sessions[0].state() == ConsensusSessionState::Failed); } #[test] fn session_does_not_fail_if_rejected_node_disconnects() { - let (clusters, acl_storages, sessions) = prepare_decryption_sessions(); + let (_, clusters, acl_storages, sessions) = prepare_decryption_sessions(); let key_pair = Random.generate().unwrap(); acl_storages[1].prohibit(key_pair.public().clone(), SessionId::default()); - sessions[0].initialize(ethkey::sign(key_pair.secret(), &SessionId::default()).unwrap(), false).unwrap(); + sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); // 1st node disconnects => ignore this sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().state == SessionState::WaitingForPartialDecryption); + assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); } #[test] fn session_does_not_fail_if_requested_node_disconnects() { - let (clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); + let (_, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); // 1 node disconnects => we still can recover secret sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().state == SessionState::WaitingForPartialDecryption); + assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); // 2 node are disconnected => we can not recover secret sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].data.lock().state == SessionState::Failed); + assert!(sessions[0].state() == ConsensusSessionState::Failed); } #[test] fn session_does_not_fail_if_node_with_shadow_point_disconnects() { - let (clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); + let (_, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption - && sessions[0].data.lock().consensus.as_ref().unwrap().job_responses().unwrap().len() == 2); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults + && sessions[0].data.lock().consensus_session.consensus_job().responses().len() == 2); // disconnects from the node which has already sent us its own shadow point let disconnected = sessions[0].data.lock(). - consensus.as_ref().unwrap().job_responses().unwrap().keys() + consensus_session.consensus_job().responses().keys() .filter(|n| *n != sessions[0].node()) .cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); - assert!(sessions[0].data.lock().state == SessionState::WaitingForPartialDecryption); + assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); } #[test] fn session_restarts_if_confirmed_node_disconnects() { - let (clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); + let (_, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); // disconnects from the node which has already confirmed its participation - let disconnected = sessions[0].data.lock().consensus.as_ref().unwrap().job_requests().unwrap().iter().cloned().nth(0).unwrap(); + let disconnected = sessions[0].data.lock().consensus_session.consensus_job().requests().iter().cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); - assert!(sessions[0].data.lock().state == SessionState::WaitingForPartialDecryption); - assert!(sessions[0].data.lock().consensus.as_ref().unwrap().core().unwrap().rejected_nodes().contains(&disconnected)); - assert!(!sessions[0].data.lock().consensus.as_ref().unwrap().job_requests().unwrap().contains(&disconnected)); + assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains(&disconnected)); + assert!(!sessions[0].data.lock().consensus_session.consensus_job().requests().contains(&disconnected)); } #[test] fn session_does_not_fail_if_non_master_node_disconnects_from_non_master_node() { - let (clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap(), false).unwrap(); + let (_, clusters, _, sessions) = prepare_decryption_sessions(); + sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == SessionState::WaitingForPartialDecryption); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); // disconnects from the node which has already confirmed its participation sessions[1].on_node_timeout(sessions[2].node()); - assert!(sessions[0].data.lock().state == SessionState::WaitingForPartialDecryption); - assert!(sessions[1].data.lock().state == SessionState::WaitingForPartialDecryptionRequest); + assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + assert!(sessions[1].state() == ConsensusSessionState::ConsensusEstablished); } #[test] fn complete_dec_session() { - let (clusters, _, sessions) = prepare_decryption_sessions(); + let (_, clusters, _, sessions) = prepare_decryption_sessions(); // now let's try to do a decryption - let key_pair = Random.generate().unwrap(); - let signature = ethkey::sign(key_pair.secret(), &SessionId::default()).unwrap(); - sessions[0].initialize(signature, false).unwrap(); + sessions[0].initialize(false).unwrap(); do_messages_exchange(&clusters, &sessions); // now check that: // 1) 5 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == SessionState::Finished).count(), 5); + assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); // 2) 1 session has decrypted key value assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); @@ -840,18 +857,16 @@ mod tests { #[test] fn complete_shadow_dec_session() { - let (clusters, _, sessions) = prepare_decryption_sessions(); + let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); // now let's try to do a decryption - let key_pair = Random.generate().unwrap(); - let signature = ethkey::sign(key_pair.secret(), &SessionId::default()).unwrap(); - sessions[0].initialize(signature, true).unwrap(); + sessions[0].initialize(true).unwrap(); do_messages_exchange(&clusters, &sessions); // now check that: // 1) 5 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == SessionState::Finished).count(), 5); + assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); // 2) 1 session has decrypted key value assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); @@ -873,12 +888,10 @@ mod tests { #[test] fn failed_dec_session() { - let (clusters, acl_storages, sessions) = prepare_decryption_sessions(); + let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); // now let's try to do a decryption - let key_pair = Random.generate().unwrap(); - let signature = ethkey::sign(key_pair.secret(), &SessionId::default()).unwrap(); - sessions[0].initialize(signature, false).unwrap(); + sessions[0].initialize(false).unwrap(); // we need 4 out of 5 nodes to agree to do a decryption // let's say that 2 of these nodes are disagree @@ -890,31 +903,29 @@ mod tests { // now check that: // 1) 3 of 5 sessions are in Failed state - assert_eq!(sessions.iter().filter(|s| s.state() == SessionState::Failed).count(), 3); + assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Failed).count(), 3); // 2) 2 of 5 sessions are in WaitingForPartialDecryptionRequest state - assert_eq!(sessions.iter().filter(|s| s.state() == SessionState::WaitingForPartialDecryptionRequest).count(), 2); + assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::ConsensusEstablished).count(), 2); // 3) 0 sessions have decrypted key value assert!(sessions.iter().all(|s| s.decrypted_secret().is_none() || s.decrypted_secret().unwrap().is_err())); } #[test] fn complete_dec_session_with_acl_check_failed_on_master() { - let (clusters, acl_storages, sessions) = prepare_decryption_sessions(); + let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); // we need 4 out of 5 nodes to agree to do a decryption // let's say that 1 of these nodes (master) is disagree - let key_pair = Random.generate().unwrap(); acl_storages[0].prohibit(key_pair.public().clone(), SessionId::default()); // now let's try to do a decryption - let signature = ethkey::sign(key_pair.secret(), &SessionId::default()).unwrap(); - sessions[0].initialize(signature, false).unwrap(); + sessions[0].initialize(false).unwrap(); do_messages_exchange(&clusters, &sessions); // now check that: // 1) 4 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == SessionState::Finished).count(), 5); + assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); // 2) 1 session has decrypted key value assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs index 7d423fb702c..aa26129c9d6 100644 --- a/secret_store/src/key_server_cluster/jobs/consensus_session.rs +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::collections::BTreeSet; use std::sync::Arc; use ethkey::{Public, Signature, recover}; @@ -7,91 +6,126 @@ use key_server_cluster::message::ConsensusMessage; use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor}; use key_server_cluster::jobs::key_access_job::KeyAccessJob; +/// Consensus session state. #[derive(Debug, Clone, Copy, PartialEq)] pub enum ConsensusSessionState { + /// Every node starts in this state. WaitingForInitialization, + /// Consensus group is establishing. EstablishingConsensus, + /// Consensus group is established. + /// Master node can start jobs dissemination. + /// Slave node waits for partial job requests. ConsensusEstablished, + /// Master node waits for partial jobs responses. WaitingForPartialResults, + /// Consensus session is completed successfully. + /// Master node can call result() to get computation result. Finished, + /// Consensus session has failed with error. Failed, } +/// Consensus session consists of following states: +/// 1) consensus group is established +/// 2) master node sends partial job requests to every member of consensus group +/// 3) slave nodes are computing partial responses +/// 4) master node computes result from partial responses pub struct ConsensusSession, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport> { + /// Current session state. state: ConsensusSessionState, + /// Session metadata. meta: SessionMeta, + /// Requester, for which consensus group has allowed access. requester: Option, + /// Consensus establish job. consensus_job: JobSession, + /// Computation job. computation_job: Option>, - //dummy: PhantomData<(ComputationTransport, ComputationExecutor)>, } +/// Consensus session creation parameters. pub struct ConsensusSessionParams> { - meta: SessionMeta, - acl_storage: Arc, - consensus_transport: ConsensusTransport, + /// Session metadata. + pub meta: SessionMeta, + /// ACL storage for access check. + pub acl_storage: Arc, + /// Transport for consensus establish job. + pub consensus_transport: ConsensusTransport, } impl ConsensusSession where ConsensusTransport: JobTransport, ComputationExecutor: JobExecutor, ComputationTransport: JobTransport { + /// Create new consensus session on slave node. pub fn new_on_slave(params: ConsensusSessionParams) -> Result { debug_assert!(params.meta.self_node_id != params.meta.master_node_id); - - let consensus_job_executor = KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage); - let consensus_job = JobSession::new(params.meta, consensus_job_executor, params.consensus_transport); - debug_assert!(consensus_job.state() == JobSessionState::Inactive); - - Ok(ConsensusSession { - state: ConsensusSessionState::WaitingForInitialization, - meta: params.meta, - requester: None, - consensus_job: consensus_job, - computation_job: None, - //dummy: PhantomData, - }) + Self::new(None, KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), params) } + /// Create new consensus session on master node. pub fn new_on_master(params: ConsensusSessionParams, signature: Signature) -> Result { debug_assert!(params.meta.self_node_id == params.meta.master_node_id); + Self::new(Some(recover(&signature, ¶ms.meta.id)?), + KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), signature), params) + } - let requester = recover(&signature, ¶ms.meta.id)?; - let consensus_job_executor = KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage, signature); - let consensus_job = JobSession::new(params.meta, consensus_job_executor, params.consensus_transport); + /// Create new consensus session. + fn new(requester: Option, consensus_job_executor: KeyAccessJob, params: ConsensusSessionParams) -> Result { + let consensus_job = JobSession::new(params.meta.clone(), consensus_job_executor, params.consensus_transport); + debug_assert!(consensus_job.state() == JobSessionState::Inactive); Ok(ConsensusSession { state: ConsensusSessionState::WaitingForInitialization, meta: params.meta, - requester: Some(requester), + requester: requester, consensus_job: consensus_job, computation_job: None, - dummy: PhantomData, }) } + /// Get consensus job reference. + #[cfg(test)] + pub fn consensus_job(&self) -> &JobSession { + &self.consensus_job + } + + /// Get computation job reference. + #[cfg(test)] + pub fn computation_job(&self) -> &JobSession { + self.computation_job.as_ref() + .expect("computation_job must only be called on master nodes") + } + + /// Get consensus session state. pub fn state(&self) -> ConsensusSessionState { self.state } + /// Get requester, for which consensus has been reached. pub fn requester(&self) -> Result<&Public, Error> { self.requester.as_ref().ok_or(Error::InvalidStateForRequest) } + /// Get computation result. pub fn result(&self) -> Result { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); if self.state != ConsensusSessionState::Finished { return Err(Error::InvalidStateForRequest); } - self.computation_job + self.computation_job.as_ref() .expect("we are on master node in finished state; computation_job is set on master node during initialization; qed") .result() } + /// Initialize session on master node. pub fn initialize(&mut self, nodes: BTreeSet) -> Result<(), Error> { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); let initialization_result = self.consensus_job.initialize(nodes); self.state = ConsensusSessionState::EstablishingConsensus; self.process_result(initialization_result) } + /// Process consensus message. pub fn on_consensus_message(&mut self, sender: &NodeId, message: &ConsensusMessage) -> Result<(), Error> { let consensus_result = match message { &ConsensusMessage::InitializeConsensusSession(ref message) => { @@ -105,10 +139,9 @@ impl ConsensusSes self.process_result(consensus_result) } + /// Disseminate jobs from master node. pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - return Err(Error::InvalidMessage); - } + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); if self.state != ConsensusSessionState::ConsensusEstablished { return Err(Error::InvalidStateForRequest); } @@ -121,13 +154,14 @@ impl ConsensusSes consensus_nodes.insert(self.meta.master_node_id.clone()); } - let mut computation_job = JobSession::new(self.meta, executor, transport); + let mut computation_job = JobSession::new(self.meta.clone(), executor, transport); let computation_result = computation_job.initialize(consensus_nodes); self.computation_job = Some(computation_job); self.state = ConsensusSessionState::WaitingForPartialResults; self.process_result(computation_result) } + /// Process job request on slave node. pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { if &self.meta.master_node_id != node { return Err(Error::InvalidMessage); @@ -136,11 +170,10 @@ impl ConsensusSes return Err(Error::InvalidStateForRequest); } - let mut computation_job = JobSession::new(self.meta, executor, transport); - let computation_result = computation_job.on_partial_request(node, request); - self.process_result(computation_result) + JobSession::new(self.meta.clone(), executor, transport).on_partial_request(node, request) } + /// Process job response on slave node. pub fn on_job_response(&mut self, node: &NodeId, response: ComputationExecutor::PartialJobResponse) -> Result<(), Error> { if self.state != ConsensusSessionState::WaitingForPartialResults { return Err(Error::InvalidStateForRequest); @@ -152,6 +185,7 @@ impl ConsensusSes self.process_result(computation_result) } + /// When session is completed on slave node. pub fn on_session_completed(&mut self, node: &NodeId) -> Result<(), Error> { if node != &self.meta.master_node_id { return Err(Error::InvalidMessage); @@ -165,51 +199,93 @@ impl ConsensusSes Ok(()) } + /// When error is received from node. pub fn on_node_error(&mut self, node: &NodeId) -> Result { - let timeout_result = match self.state { - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Finished | ConsensusSessionState::Failed => - Err(Error::ConsensusUnreachable), - ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => - self.consensus_job.on_node_timeout(node), + let is_self_master = self.meta.master_node_id == self.meta.self_node_id; + let is_node_master = self.meta.master_node_id == *node; + let (is_restart_needed, timeout_result) = match self.state { + ConsensusSessionState::WaitingForInitialization if is_self_master => { + // it is strange to receive error before session is initialized && slave doesn't know access_key + // => ignore this error for now + (false, Ok(())) + } + ConsensusSessionState::WaitingForInitialization if is_node_master => { + // can not establish consensus + // => fatal error + self.state = ConsensusSessionState::Failed; + (false, Err(Error::ConsensusUnreachable)) + }, + ConsensusSessionState::EstablishingConsensus => { + debug_assert!(is_self_master); + + // consensus still can be established + // => try to live without this node + (false, self.consensus_job.on_node_error(node)) + }, + ConsensusSessionState::ConsensusEstablished => { + // we could try to continue without this node, if enough nodes left + (false, self.consensus_job.on_node_error(node)) + }, ConsensusSessionState::WaitingForPartialResults => { + // check if *current* computation job can continue without this node let is_computation_node = self.computation_job.as_mut() .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .on_node_timeout(node) + .on_node_error(node) .is_err(); if !is_computation_node { - Ok(()) + // it is not used by current computation job + // => no restart required + (false, Ok(())) } else { - self.consensus_job.on_node_timeout(node) + // it is used by current computation job + // => restart is required if there are still enough nodes + self.state = ConsensusSessionState::EstablishingConsensus; + let consensus_result = self.consensus_job.on_node_error(node); + let is_consensus_established = self.consensus_job.state() == JobSessionState::Finished; + (is_consensus_established, consensus_result) } }, + // in all other cases - just ignore error + ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Failed | ConsensusSessionState::Finished => (false, Ok(())), }; - self.process_result(timeout_result) + self.process_result(timeout_result)?; + Ok(is_restart_needed) } + /// When session is timeouted. pub fn on_session_timeout(&mut self) -> Result { match self.state { - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Finished | ConsensusSessionState::Failed | - ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => - return self.process_result(Err(Error::ConsensusUnreachable)), - ConsensusSessionState::WaitingForPartialResults => () + // if we are waiting for results from slaves, there is a chance to send request to other nodes subset => fall through + ConsensusSessionState::WaitingForPartialResults => (), + // in some states this error is fatal + ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => { + let _ = self.consensus_job.on_session_timeout(); + self.state = ConsensusSessionState::EstablishingConsensus; + return self.process_result(Err(Error::ConsensusUnreachable)).map(|_| unreachable!()); + }, + // in all other cases - just ignore error + ConsensusSessionState::Finished | ConsensusSessionState::Failed => return Ok(false), }; let timeouted_nodes = self.computation_job.as_ref() .expect("WaitingForPartialResults state is only set when computation_job is created; qed") .requests() .clone(); + assert!(!timeouted_nodes.is_empty()); // timeout should not ever happen if no requests are active && we are waiting for responses + for timeouted_node in timeouted_nodes { - let timeout_result = self.consensus_job.on_node_timeout(&timeouted_node); - self.process_result(timeout_result); + let timeout_result = self.consensus_job.on_node_error(&timeouted_node); + self.state = ConsensusSessionState::EstablishingConsensus; + self.process_result(timeout_result)?; } - self.state = ConsensusSessionState::ConsensusEstablished; - Ok(()) + Ok(self.state == ConsensusSessionState::ConsensusEstablished) } + /// Process result of job. fn process_result(&mut self, result: Result<(), Error>) -> Result<(), Error> { match self.state { - ConsensusSessionState::EstablishingConsensus => match self.consensus_job.state() { + ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => match self.consensus_job.state() { JobSessionState::Finished => self.state = ConsensusSessionState::ConsensusEstablished, JobSessionState::Failed => self.state = ConsensusSessionState::Failed, _ => (), @@ -227,3 +303,403 @@ impl ConsensusSes result } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use ethkey::{Signature, KeyPair, Random, Generator, sign}; + use key_server_cluster::{Error, NodeId, SessionId, DummyAclStorage}; + use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; + use key_server_cluster::jobs::job_session::tests::{make_master_session_meta, make_slave_session_meta, SquaredSumJobExecutor, DummyJobTransport}; + use super::{ConsensusSession, ConsensusSessionParams, ConsensusSessionState}; + + type SquaredSumConsensusSession = ConsensusSession, SquaredSumJobExecutor, DummyJobTransport>; + + fn make_master_consensus_session(threshold: usize, requester: Option, acl_storage: Option) -> SquaredSumConsensusSession { + let secret = requester.map(|kp| kp.secret().clone()).unwrap_or(Random.generate().unwrap().secret().clone()); + SquaredSumConsensusSession::new_on_master(ConsensusSessionParams { + meta: make_master_session_meta(threshold), + acl_storage: Arc::new(acl_storage.unwrap_or(DummyAclStorage::default())), + consensus_transport: DummyJobTransport::default(), + }, sign(&secret, &SessionId::default()).unwrap()).unwrap() + } + + fn make_slave_consensus_session(threshold: usize, acl_storage: Option) -> SquaredSumConsensusSession { + SquaredSumConsensusSession::new_on_slave(ConsensusSessionParams { + meta: make_slave_session_meta(threshold), + acl_storage: Arc::new(acl_storage.unwrap_or(DummyAclStorage::default())), + consensus_transport: DummyJobTransport::default(), + }).unwrap() + } + + #[test] + fn consensus_session_consensus_is_not_reached_when_initializes_with_non_zero_threshold() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_consensus_is_reached_when_initializes_with_zero_threshold() { + let mut session = make_master_consensus_session(0, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_consensus_is_not_reached_when_initializes_with_zero_threshold_and_master_rejects() { + let requester = Random.generate().unwrap(); + let acl_storage = DummyAclStorage::default(); + acl_storage.prohibit(requester.public().clone(), SessionId::default()); + + let mut session = make_master_consensus_session(0, Some(requester), Some(acl_storage)); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_consensus_is_failed_by_master_node() { + let requester = Random.generate().unwrap(); + let acl_storage = DummyAclStorage::default(); + acl_storage.prohibit(requester.public().clone(), SessionId::default()); + + let mut session = make_master_consensus_session(1, Some(requester), Some(acl_storage)); + assert_eq!(session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_consensus_is_failed_by_slave_node() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + assert_eq!(session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: false, + })).unwrap_err(), Error::ConsensusUnreachable); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_job_dissemination_fails_if_consensus_is_not_reached() { + let mut session = make_master_consensus_session(1, None, None); + assert_eq!(session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn consensus_session_job_dissemination_selects_master_node_if_agreed() { + let mut session = make_master_consensus_session(0, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::Finished); + assert!(session.computation_job().responses().contains_key(&NodeId::from(1))); + } + + #[test] + fn consensus_session_job_dissemination_does_not_select_master_node_if_rejected() { + let requester = Random.generate().unwrap(); + let acl_storage = DummyAclStorage::default(); + acl_storage.prohibit(requester.public().clone(), SessionId::default()); + + let mut session = make_master_consensus_session(0, Some(requester), Some(acl_storage)); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + assert!(!session.computation_job().responses().contains_key(&NodeId::from(1))); + } + + #[test] + fn consensus_session_computation_request_is_rejected_when_received_by_master_node() { + let mut session = make_master_consensus_session(0, None, None); + assert_eq!(session.on_job_request(&NodeId::from(2), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn consensus_session_computation_request_is_rejected_when_received_before_consensus_is_established() { + let mut session = make_slave_consensus_session(0, None); + assert_eq!(session.on_job_request(&NodeId::from(1), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn consensus_session_computation_request_is_ignored_when_wrong() { + let mut session = make_slave_consensus_session(0, None); + assert_eq!(session.state(), ConsensusSessionState::WaitingForInitialization); + session.on_consensus_message(&NodeId::from(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + assert_eq!(session.on_job_request(&NodeId::from(1), 20, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidMessage); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_computation_request_is_processed_when_correct() { + let mut session = make_slave_consensus_session(0, None); + assert_eq!(session.state(), ConsensusSessionState::WaitingForInitialization); + session.on_consensus_message(&NodeId::from(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.on_job_request(&NodeId::from(1), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_computation_response_is_ignored_when_consensus_is_not_reached() { + let mut session = make_master_consensus_session(1, None, None); + assert_eq!(session.on_job_response(&NodeId::from(2), 4).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn consessus_session_completion_is_ignored_when_received_from_non_master_node() { + let mut session = make_slave_consensus_session(0, None); + assert_eq!(session.on_session_completed(&NodeId::from(3)).unwrap_err(), Error::InvalidMessage); + } + + #[test] + fn consessus_session_completion_is_ignored_when_consensus_is_not_established() { + let mut session = make_slave_consensus_session(0, None); + assert_eq!(session.on_session_completed(&NodeId::from(1)).unwrap_err(), Error::InvalidStateForRequest); + } + + #[test] + fn consessus_session_completion_is_accepted() { + let mut session = make_slave_consensus_session(0, None); + session.on_consensus_message(&NodeId::from(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), + })).unwrap(); + session.on_session_completed(&NodeId::from(1)).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::Finished); + } + + #[test] + fn consensus_session_continues_if_node_error_received_by_uninitialized_master() { + let mut session = make_master_consensus_session(0, None, None); + assert_eq!(session.on_node_error(&NodeId::from(2)), Ok(false)); + } + + #[test] + fn consensus_session_fails_if_node_error_received_by_uninitialized_slave_from_master() { + let mut session = make_slave_consensus_session(0, None); + assert_eq!(session.on_node_error(&NodeId::from(1)), Err(Error::ConsensusUnreachable)); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_continues_if_node_error_received_by_master_during_establish_and_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3)].into_iter().collect()).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(2)), Ok(false)); + } + + #[test] + fn consensus_session_fails_if_node_error_received_by_master_during_establish_and_not_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(2)), Err(Error::ConsensusUnreachable)); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_continues_if_node2_error_received_by_master_after_consensus_established_and_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(2)), Ok(false)); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_continues_if_node3_error_received_by_master_after_consensus_established_and_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(3)), Ok(false)); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + } + + #[test] + fn consensus_session_fails_if_node_error_received_by_master_after_consensus_established_and_not_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(2)), Err(Error::ConsensusUnreachable)); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_continues_if_node_error_received_from_slave_not_participating_in_computation() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3), NodeId::from(4)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + session.on_consensus_message(&NodeId::from(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(3)), Ok(false)); + assert_eq!(session.on_node_error(&NodeId::from(4)), Ok(false)); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + } + + #[test] + fn consensus_session_restarts_if_node_error_received_from_slave_participating_in_computation_and_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3), NodeId::from(4)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + session.on_consensus_message(&NodeId::from(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(2)), Ok(true)); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + assert_eq!(session.on_node_error(&NodeId::from(3)), Ok(false)); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + } + + #[test] + fn consensus_session_fails_if_node_error_received_from_slave_participating_in_computation_and_not_enough_nodes_left() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.on_node_error(&NodeId::from(2)), Err(Error::ConsensusUnreachable)); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_fails_if_uninitialized_session_timeouts() { + let mut session = make_master_consensus_session(1, None, None); + assert_eq!(session.on_session_timeout(), Err(Error::ConsensusUnreachable)); + } + + #[test] + fn consensus_session_continues_if_session_timeouts_and_enough_nodes_left_for_computation() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3), NodeId::from(4)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + session.on_consensus_message(&NodeId::from(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.on_session_timeout(), Ok(true)); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + assert_eq!(session.on_session_timeout(), Ok(false)); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + } + + #[test] + fn consensus_session_continues_if_session_timeouts_and_not_enough_nodes_left_for_computation() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + assert_eq!(session.on_session_timeout(), Err(Error::ConsensusUnreachable)); + assert_eq!(session.state(), ConsensusSessionState::Failed); + } + + #[test] + fn consensus_session_complete_2_of_4() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3), NodeId::from(3)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + session.on_job_response(&NodeId::from(2), 16).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::Finished); + assert_eq!(session.result(), Ok(20)); + } + + #[test] + fn consensus_session_complete_2_of_4_after_restart() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3), NodeId::from(4)].into_iter().collect()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + session.on_consensus_message(&NodeId::from(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + + assert_eq!(session.on_node_error(&NodeId::from(2)).unwrap(), true); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + assert_eq!(session.on_node_error(&NodeId::from(3)).unwrap(), false); + assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); + + session.on_consensus_message(&NodeId::from(4), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); + + session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); + + session.on_job_response(&NodeId::from(4), 16).unwrap(); + assert_eq!(session.state(), ConsensusSessionState::Finished); + assert_eq!(session.result(), Ok(20)); + } +} diff --git a/secret_store/src/key_server_cluster/jobs/decryption_job.rs b/secret_store/src/key_server_cluster/jobs/decryption_job.rs index a6c59d9d8b9..ce6a7617042 100644 --- a/secret_store/src/key_server_cluster/jobs/decryption_job.rs +++ b/secret_store/src/key_server_cluster/jobs/decryption_job.rs @@ -1,9 +1,8 @@ -use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethkey::{Public, Secret}; use ethcrypto::ecies::encrypt; use ethcrypto::DEFAULT_MAC; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare, EncryptedDocumentKeyShadow}; +use key_server_cluster::{Error, NodeId, DocumentKeyShare, EncryptedDocumentKeyShadow}; use key_server_cluster::math; use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobExecutor}; @@ -21,8 +20,6 @@ pub struct DecryptionJob { request_id: Option, /// Is shadow decryption requested. is_shadow_decryption: Option, - /// Id of nodes, participating in decryption. - participants: Option>, } /// Decryption job partial request. @@ -46,29 +43,28 @@ pub struct PartialDecryptionResponse { } impl DecryptionJob { - pub fn new_on_slave(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare) -> Self { + pub fn new_on_slave(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare) -> Result { debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); - DecryptionJob { + Ok(DecryptionJob { self_node_id: self_node_id, access_key: access_key, requester: requester, key_share: key_share, request_id: None, is_shadow_decryption: None, - participants: None, - } + }) } - pub fn new_on_master(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, is_shadow_decryption: bool, participants: BTreeSet) -> Self { + pub fn new_on_master(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, is_shadow_decryption: bool) -> Result { debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); - DecryptionJob { + Ok(DecryptionJob { self_node_id: self_node_id, access_key: access_key, requester: requester, key_share: key_share, + request_id: Some(math::generate_random_scalar()?), is_shadow_decryption: Some(is_shadow_decryption), - participants: Some(participants), - } + }) } } @@ -77,15 +73,18 @@ impl JobExecutor for DecryptionJob { type PartialJobResponse = PartialDecryptionResponse; type JobResponse = EncryptedDocumentKeyShadow; - fn prepare_partial_request(&self, node: &NodeId) -> Result { + fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result { + debug_assert!(nodes.len() == self.key_share.threshold + 1); + + let request_id = self.request_id.as_ref() + .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); let is_shadow_decryption = self.is_shadow_decryption .expect("prepare_partial_request is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); - let mut other_nodes_ids = self.participants.as_ref() - .expect("prepare_partial_request is only called on master nodes; participants is filed in constructor on master nodes; qed") - .clone(); + let mut other_nodes_ids = nodes.clone(); other_nodes_ids.remove(node); Ok(PartialDecryptionRequest { + id: request_id.clone(), is_shadow_decryption: is_shadow_decryption, other_nodes_ids: other_nodes_ids, }) @@ -105,6 +104,7 @@ impl JobExecutor for DecryptionJob { let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(&self.access_key, &common_point, &node_shadow, decrypt_shadow)?; Ok(PartialDecryptionResponse { + request_id: partial_request.id, shadow_point: shadow_point, decrypt_shadow: match decrypt_shadow { None => None, @@ -114,7 +114,7 @@ impl JobExecutor for DecryptionJob { } fn check_partial_response(&self, partial_response: &PartialDecryptionResponse) -> Result { - if Some(partial_response.request_id) != self.request_id { + if Some(&partial_response.request_id) != self.request_id.as_ref() { return Ok(JobPartialResponseAction::Ignore); } if self.is_shadow_decryption != Some(partial_response.decrypt_shadow.is_some()) { diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs index 877525e32e3..e98eb609446 100644 --- a/secret_store/src/key_server_cluster/jobs/job_session.rs +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -1,6 +1,5 @@ -use std::marker::PhantomData; use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; +use key_server_cluster::{Error, NodeId, SessionMeta}; #[derive(Debug, Clone, Copy, PartialEq)] /// Partial rsponse action. @@ -20,7 +19,7 @@ pub trait JobExecutor { type JobResponse; /// Prepare job request for given node. - fn prepare_partial_request(&self, node: &NodeId) -> Result; + fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result; /// Process partial request. fn process_partial_request(&self, partial_request: Self::PartialJobRequest) -> Result; /// Check partial response of given node. @@ -110,6 +109,16 @@ impl JobSession where Executor: JobExe self.data.state } + #[cfg(test)] + /// Get rejects. + pub fn rejects(&self) -> &BTreeSet { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); + + &self.data.active_data.as_ref() + .expect("rejects is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") + .rejects + } + /// Get active requests. pub fn requests(&self) -> &BTreeSet { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); @@ -119,6 +128,16 @@ impl JobSession where Executor: JobExe .requests } + #[cfg(test)] + /// Get responses. + pub fn responses(&self) -> &BTreeMap { + debug_assert!(self.meta.self_node_id == self.meta.master_node_id); + + &self.data.active_data.as_ref() + .expect("responses is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") + .responses + } + /// Get job result. pub fn result(&self) -> Result { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); @@ -133,7 +152,7 @@ impl JobSession where Executor: JobExe } /// Initialize. - pub fn initialize(&mut self, mut nodes: BTreeSet) -> Result<(), Error> { + pub fn initialize(&mut self, nodes: BTreeSet) -> Result<(), Error> { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); debug_assert!(nodes.len() >= self.meta.threshold + 1); @@ -150,21 +169,28 @@ impl JobSession where Executor: JobExe }; for node in &active_data.requests { if node != &self.meta.self_node_id { - self.transport.send_partial_request(&node, self.executor.prepare_partial_request(node)?)?; + self.transport.send_partial_request(&node, self.executor.prepare_partial_request(node, &active_data.requests)?)?; } else { waits_for_self = true; } } + // result from self + let self_response = if waits_for_self { + let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id, &active_data.requests)?; + Some(self.executor.process_partial_request(partial_request)?) + } else { + None + }; + // update state self.data.active_data = Some(active_data); self.data.state = JobSessionState::Active; // if we are waiting for response from self => do it - if waits_for_self { - let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id)?; - let partial_response = self.executor.process_partial_request(partial_request)?; - self.on_partial_response(&self.meta.self_node_id, partial_response)?; + if let Some(self_response) = self_response { + let self_node_id = self.meta.self_node_id.clone(); + self.on_partial_response(&self_node_id, self_response)?; } Ok(()) @@ -181,7 +207,6 @@ impl JobSession where Executor: JobExe if self.data.state != JobSessionState::Inactive && self.data.state != JobSessionState::Finished { return Err(Error::InvalidStateForRequest); } - self.data.state = JobSessionState::Finished; self.transport.send_partial_response(node, self.executor.process_partial_request(request)?) } @@ -225,15 +250,15 @@ impl JobSession where Executor: JobExe } } - /// When node is timeouted. - pub fn on_node_timeout(&mut self, node: &NodeId) -> Result<(), Error> { + /// When error from node is received. + pub fn on_node_error(&mut self, node: &NodeId) -> Result<(), Error> { if self.meta.self_node_id != self.meta.master_node_id { if node != &self.meta.master_node_id { return Ok(()); } self.data.state = JobSessionState::Failed; - return Err(Error::NodeDisconnected); + return Err(Error::ConsensusUnreachable); } let active_data = self.data.active_data.as_mut() @@ -243,85 +268,91 @@ impl JobSession where Executor: JobExe } if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { active_data.rejects.insert(node.clone()); + if self.data.state == JobSessionState::Finished && active_data.responses.len() < self.meta.threshold + 1 { + self.data.state = JobSessionState::Active; + } if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { return Ok(()); } self.data.state = JobSessionState::Failed; - return Err(Error::NodeDisconnected); + return Err(Error::ConsensusUnreachable); } Ok(()) } /// When session timeouted. - pub fn on_session_timeout(&mut self) { + pub fn on_session_timeout(&mut self) -> Result<(), Error> { + if self.data.state == JobSessionState::Finished || self.data.state == JobSessionState::Failed { + return Ok(()); + } + self.data.state = JobSessionState::Failed; + Err(Error::ConsensusUnreachable) } } #[cfg(test)] -mod tests { - use std::collections::{VecDeque, BTreeMap}; +pub mod tests { + use std::collections::{VecDeque, BTreeMap, BTreeSet}; use parking_lot::Mutex; use ethkey::Public; - use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, DocumentKeyShare}; + use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; use super::{JobPartialResponseAction, JobExecutor, JobTransport, JobSession, JobSessionState}; - struct SquaredSumJobExecutor; + pub struct SquaredSumJobExecutor; impl JobExecutor for SquaredSumJobExecutor { type PartialJobRequest = u32; type PartialJobResponse = u32; type JobResponse = u32; - fn prepare_partial_request(&self, _n: &NodeId) -> Result { Ok(2) } - fn process_partial_request(&self, r: u32) -> Result { Ok(r * r) } + fn prepare_partial_request(&self, _n: &NodeId, _nodes: &BTreeSet) -> Result { Ok(2) } + fn process_partial_request(&self, r: u32) -> Result { if r <= 10 { Ok(r * r) } else { Err(Error::InvalidMessage) } } fn check_partial_response(&self, r: &u32) -> Result { if r % 2 == 0 { Ok(JobPartialResponseAction::Accept) } else { Ok(JobPartialResponseAction::Reject) } } fn compute_response(&self, r: &BTreeMap) -> Result { Ok(r.values().fold(0, |v1, v2| v1 + v2)) } } #[derive(Default)] - struct DummyJobTransport { - pub requests: Mutex>, - pub responses: Mutex>, + pub struct DummyJobTransport { + pub requests: Mutex>, + pub responses: Mutex>, } - impl DummyJobTransport { - pub fn response(&self) -> (NodeId, u32) { + impl DummyJobTransport { + pub fn response(&self) -> (NodeId, U) { self.responses.lock().pop_front().unwrap() } } - impl JobTransport for DummyJobTransport { - type PartialJobRequest = u32; - type PartialJobResponse = u32; + impl JobTransport for DummyJobTransport { + type PartialJobRequest = T; + type PartialJobResponse = U; - fn send_partial_request(&self, node: &NodeId, request: u32) -> Result<(), Error> { self.requests.lock().push_back((node.clone(), request)); Ok(()) } - fn send_partial_response(&self, node: &NodeId, response: u32) -> Result<(), Error> { self.responses.lock().push_back((node.clone(), response)); Ok(()) } + fn send_partial_request(&self, node: &NodeId, request: T) -> Result<(), Error> { self.requests.lock().push_back((node.clone(), request)); Ok(()) } + fn send_partial_response(&self, node: &NodeId, response: U) -> Result<(), Error> { self.responses.lock().push_back((node.clone(), response)); Ok(()) } } - fn make_master_session_meta(threshold: usize) -> SessionMeta { + pub fn make_master_session_meta(threshold: usize) -> SessionMeta { SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(1), threshold: threshold } } - fn make_slave_session_meta(threshold: usize) -> SessionMeta { + pub fn make_slave_session_meta(threshold: usize) -> SessionMeta { SessionMeta { id: SessionId::default(), master_node_id: NodeId::from(1), self_node_id: NodeId::from(2), threshold: threshold } } #[test] fn job_initialize_fails_if_not_inactive() { - let meta = make_master_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1)].into_iter().collect()).unwrap(); assert_eq!(job.initialize(vec![Public::from(1)].into_iter().collect()).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn job_initialization_leads_to_finish_if_single_node_is_required() { - let meta = make_master_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Finished); assert_eq!(job.result(), Ok(4)); @@ -329,38 +360,33 @@ mod tests { #[test] fn job_initialization_does_not_leads_to_finish_if_single_other_node_is_required() { - let meta = make_master_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(2)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] fn job_request_fails_if_comes_from_non_master_node() { - let meta = make_slave_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.on_partial_request(&NodeId::from(3), 2).unwrap_err(), Error::InvalidMessage); } #[test] fn job_request_fails_if_comes_to_master_node() { - let meta = make_master_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.on_partial_request(&NodeId::from(1), 2).unwrap_err(), Error::InvalidMessage); } #[test] fn job_request_fails_if_comes_to_failed_state() { - let meta = make_slave_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); - job.on_session_timeout(); + let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); + job.on_session_timeout().unwrap_err(); assert_eq!(job.on_partial_request(&NodeId::from(1), 2).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn job_request_succeeds_if_comes_to_finished_state() { - let meta = make_slave_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.on_partial_request(&NodeId::from(1), 2).unwrap(); assert_eq!(job.transport().response(), (NodeId::from(1), 4)); assert_eq!(job.state(), JobSessionState::Finished); @@ -371,32 +397,28 @@ mod tests { #[test] fn job_response_fails_if_comes_to_slave_node() { - let meta = make_slave_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.on_partial_response(&NodeId::from(1), 2).unwrap_err(), Error::InvalidMessage); } #[test] fn job_response_fails_if_comes_to_failed_state() { - let meta = make_master_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(2)].into_iter().collect()).unwrap(); - job.on_session_timeout(); + job.on_session_timeout().unwrap_err(); assert_eq!(job.on_partial_response(&NodeId::from(2), 2).unwrap_err(), Error::InvalidStateForRequest); } #[test] fn job_response_fails_if_comes_from_unknown_node() { - let meta = make_master_session_meta(0); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(2)].into_iter().collect()).unwrap(); assert_eq!(job.on_partial_response(&NodeId::from(3), 2).unwrap_err(), Error::InvalidNodeForRequest); } #[test] fn job_response_leads_to_failure_if_too_few_nodes_left() { - let meta = make_master_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); assert_eq!(job.on_partial_response(&NodeId::from(2), 3).unwrap_err(), Error::ConsensusUnreachable); @@ -405,8 +427,7 @@ mod tests { #[test] fn job_response_succeeds() { - let meta = make_master_session_meta(2); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); job.on_partial_response(&NodeId::from(2), 2).unwrap(); @@ -415,8 +436,7 @@ mod tests { #[test] fn job_response_leads_to_finish() { - let meta = make_master_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); job.on_partial_response(&NodeId::from(2), 2).unwrap(); @@ -424,61 +444,55 @@ mod tests { } #[test] - fn job_node_timeout_ignored_when_slave_disconnects_from_slave() { - let meta = make_slave_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + fn job_node_error_ignored_when_slave_disconnects_from_slave() { + let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.state(), JobSessionState::Inactive); - job.on_node_timeout(&NodeId::from(3)).unwrap(); + job.on_node_error(&NodeId::from(3)).unwrap(); assert_eq!(job.state(), JobSessionState::Inactive); } #[test] - fn job_node_timeout_leads_to_fail_when_slave_disconnects_from_master() { - let meta = make_slave_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + fn job_node_error_leads_to_fail_when_slave_disconnects_from_master() { + let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); assert_eq!(job.state(), JobSessionState::Inactive); - assert_eq!(job.on_node_timeout(&NodeId::from(1)).unwrap_err(), Error::NodeDisconnected); + assert_eq!(job.on_node_error(&NodeId::from(1)).unwrap_err(), Error::ConsensusUnreachable); assert_eq!(job.state(), JobSessionState::Failed); } #[test] - fn job_node_timeout_ignored_when_disconnects_from_rejected() { - let meta = make_master_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + fn job_node_error_ignored_when_disconnects_from_rejected() { + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); job.on_partial_response(&NodeId::from(2), 3).unwrap(); - job.on_node_timeout(&NodeId::from(2)).unwrap(); + job.on_node_error(&NodeId::from(2)).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] - fn job_node_timeout_ignored_when_disconnects_from_unknown() { - let meta = make_master_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + fn job_node_error_ignored_when_disconnects_from_unknown() { + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_node_timeout(&NodeId::from(3)).unwrap(); + job.on_node_error(&NodeId::from(3)).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] - fn job_node_timeout_ignored_when_disconnects_from_requested_and_enough_nodes_left() { - let meta = make_master_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + fn job_node_error_ignored_when_disconnects_from_requested_and_enough_nodes_left() { + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2), Public::from(3)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - job.on_node_timeout(&NodeId::from(3)).unwrap(); + job.on_node_error(&NodeId::from(3)).unwrap(); assert_eq!(job.state(), JobSessionState::Active); } #[test] - fn job_node_timeout_leads_to_fail_when_disconnects_from_requested_and_not_enough_nodes_left() { - let meta = make_master_session_meta(1); - let mut job = JobSession::new(&meta, SquaredSumJobExecutor, DummyJobTransport::default()); + fn job_node_error_leads_to_fail_when_disconnects_from_requested_and_not_enough_nodes_left() { + let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); job.initialize(vec![Public::from(1), Public::from(2)].into_iter().collect()).unwrap(); assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_node_timeout(&NodeId::from(2)).unwrap_err(), Error::NodeDisconnected); + assert_eq!(job.on_node_error(&NodeId::from(2)).unwrap_err(), Error::ConsensusUnreachable); assert_eq!(job.state(), JobSessionState::Failed); } } diff --git a/secret_store/src/key_server_cluster/jobs/key_access_job.rs b/secret_store/src/key_server_cluster/jobs/key_access_job.rs index ae125aca654..a5cf0498645 100644 --- a/secret_store/src/key_server_cluster/jobs/key_access_job.rs +++ b/secret_store/src/key_server_cluster/jobs/key_access_job.rs @@ -37,7 +37,7 @@ impl JobExecutor for KeyAccessJob { type PartialJobResponse = bool; type JobResponse = BTreeSet; - fn prepare_partial_request(&self, _node: &NodeId) -> Result { + fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { Ok(self.signature.as_ref().expect("prepare_partial_request is only called on master nodes; new_on_master fills the signature; qed").clone()) } From 9e2448f407427c5a9ac8629cca4feb45f89abf17 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 5 Jun 2017 09:40:10 +0300 Subject: [PATCH 38/45] fixed decryption tests --- .../key_server_cluster/decryption_session.rs | 59 +++++++++---------- .../key_server_cluster/jobs/decryption_job.rs | 8 +-- .../key_server_cluster/jobs/job_session.rs | 36 ++++++++--- .../key_server_cluster/jobs/key_access_job.rs | 8 ++- 4 files changed, 66 insertions(+), 45 deletions(-) diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index b1757060c87..210e29d4688 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -537,11 +537,11 @@ mod tests { (requester, clusters, acl_storages, sessions) } - fn do_messages_exchange(clusters: &[Arc], sessions: &[SessionImpl]) { - do_messages_exchange_until(clusters, sessions, |_, _, _| false); + fn do_messages_exchange(clusters: &[Arc], sessions: &[SessionImpl]) -> Result<(), Error> { + do_messages_exchange_until(clusters, sessions, |_, _, _| false) } - fn do_messages_exchange_until(clusters: &[Arc], sessions: &[SessionImpl], mut cond: F) where F: FnMut(&NodeId, &NodeId, &Message) -> bool { + fn do_messages_exchange_until(clusters: &[Arc], sessions: &[SessionImpl], mut cond: F) -> Result<(), Error> where F: FnMut(&NodeId, &NodeId, &Message) -> bool { while let Some((from, to, message)) = clusters.iter().filter_map(|c| c.take_message().map(|(to, msg)| (c.node(), to, msg))).next() { let session = &sessions[sessions.iter().position(|s| s.node() == &to).unwrap()]; if cond(&from, &to, &message) { @@ -549,10 +549,12 @@ mod tests { } match message { - Message::Decryption(message) => session.process_message(&from, &message).unwrap(), + Message::Decryption(message) => session.process_message(&from, &message)?, _ => unreachable!(), } } + + Ok(()) } #[test] @@ -728,10 +730,10 @@ mod tests { true }, _ => false, - }); + }).unwrap(); assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.clone().unwrap()).unwrap(), ()); - assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.unwrap()).unwrap_err(), Error::InvalidStateForRequest); + assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.unwrap()).unwrap_err(), Error::InvalidNodeForRequest); } #[test] @@ -739,7 +741,7 @@ mod tests { let (_, _, _, sessions) = prepare_decryption_sessions(); assert!(sessions[0].decrypted_secret().is_none()); sessions[0].on_session_timeout(); - assert!(sessions[0].decrypted_secret().unwrap().unwrap_err() == Error::NodeDisconnected); + assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap_err(), Error::ConsensusUnreachable); } #[test] @@ -765,11 +767,11 @@ mod tests { acl_storages[1].prohibit(key_pair.public().clone(), SessionId::default()); sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); // 1st node disconnects => ignore this sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); } #[test] @@ -777,11 +779,11 @@ mod tests { let (_, clusters, _, sessions) = prepare_decryption_sessions(); sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); // 1 node disconnects => we still can recover secret sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); // 2 node are disconnected => we can not recover secret sessions[0].on_node_timeout(sessions[2].node()); @@ -794,15 +796,15 @@ mod tests { sessions[0].initialize(false).unwrap(); do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults - && sessions[0].data.lock().consensus_session.consensus_job().responses().len() == 2); + && sessions[0].data.lock().consensus_session.computation_job().responses().len() == 2).unwrap(); // disconnects from the node which has already sent us its own shadow point let disconnected = sessions[0].data.lock(). - consensus_session.consensus_job().responses().keys() + consensus_session.computation_job().responses().keys() .filter(|n| *n != sessions[0].node()) .cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); - assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); } #[test] @@ -810,14 +812,14 @@ mod tests { let (_, clusters, _, sessions) = prepare_decryption_sessions(); sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); // disconnects from the node which has already confirmed its participation - let disconnected = sessions[0].data.lock().consensus_session.consensus_job().requests().iter().cloned().nth(0).unwrap(); + let disconnected = sessions[0].data.lock().consensus_session.computation_job().requests().iter().cloned().nth(0).unwrap(); sessions[0].on_node_timeout(&disconnected); - assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); - assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains(&disconnected)); - assert!(!sessions[0].data.lock().consensus_session.consensus_job().requests().contains(&disconnected)); + assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); + assert!(sessions[0].data.lock().consensus_session.computation_job().rejects().contains(&disconnected)); + assert!(!sessions[0].data.lock().consensus_session.computation_job().requests().contains(&disconnected)); } #[test] @@ -825,7 +827,7 @@ mod tests { let (_, clusters, _, sessions) = prepare_decryption_sessions(); sessions[0].initialize(false).unwrap(); - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); + do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); // disconnects from the node which has already confirmed its participation sessions[1].on_node_timeout(sessions[2].node()); @@ -840,7 +842,7 @@ mod tests { // now let's try to do a decryption sessions[0].initialize(false).unwrap(); - do_messages_exchange(&clusters, &sessions); + do_messages_exchange(&clusters, &sessions).unwrap(); // now check that: // 1) 5 of 5 sessions are in Finished state @@ -862,7 +864,7 @@ mod tests { // now let's try to do a decryption sessions[0].initialize(true).unwrap(); - do_messages_exchange(&clusters, &sessions); + do_messages_exchange(&clusters, &sessions).unwrap(); // now check that: // 1) 5 of 5 sessions are in Finished state @@ -898,16 +900,11 @@ mod tests { acl_storages[1].prohibit(key_pair.public().clone(), SessionId::default()); acl_storages[2].prohibit(key_pair.public().clone(), SessionId::default()); - let node3 = sessions[3].node().clone(); - do_messages_exchange_until(&clusters, &sessions, |from, _, _msg| from == &node3); + assert_eq!(do_messages_exchange(&clusters, &sessions).unwrap_err(), Error::ConsensusUnreachable); - // now check that: - // 1) 3 of 5 sessions are in Failed state + // check that 3 nodes have failed state + assert_eq!(sessions[0].state(), ConsensusSessionState::Failed); assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Failed).count(), 3); - // 2) 2 of 5 sessions are in WaitingForPartialDecryptionRequest state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::ConsensusEstablished).count(), 2); - // 3) 0 sessions have decrypted key value - assert!(sessions.iter().all(|s| s.decrypted_secret().is_none() || s.decrypted_secret().unwrap().is_err())); } #[test] @@ -921,7 +918,7 @@ mod tests { // now let's try to do a decryption sessions[0].initialize(false).unwrap(); - do_messages_exchange(&clusters, &sessions); + do_messages_exchange(&clusters, &sessions).unwrap(); // now check that: // 1) 4 of 5 sessions are in Finished state diff --git a/secret_store/src/key_server_cluster/jobs/decryption_job.rs b/secret_store/src/key_server_cluster/jobs/decryption_job.rs index ce6a7617042..18b28192060 100644 --- a/secret_store/src/key_server_cluster/jobs/decryption_job.rs +++ b/secret_store/src/key_server_cluster/jobs/decryption_job.rs @@ -4,7 +4,7 @@ use ethcrypto::ecies::encrypt; use ethcrypto::DEFAULT_MAC; use key_server_cluster::{Error, NodeId, DocumentKeyShare, EncryptedDocumentKeyShadow}; use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobExecutor}; +use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; /// Decryption job. pub struct DecryptionJob { @@ -90,7 +90,7 @@ impl JobExecutor for DecryptionJob { }) } - fn process_partial_request(&self, partial_request: PartialDecryptionRequest) -> Result { + fn process_partial_request(&self, partial_request: PartialDecryptionRequest) -> Result, Error> { if partial_request.other_nodes_ids.len() != self.key_share.threshold || partial_request.other_nodes_ids.contains(&self.self_node_id) || partial_request.other_nodes_ids.iter().any(|n| !self.key_share.id_numbers.contains_key(n)) { @@ -103,14 +103,14 @@ impl JobExecutor for DecryptionJob { let decrypt_shadow = if partial_request.is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(&self.access_key, &common_point, &node_shadow, decrypt_shadow)?; - Ok(PartialDecryptionResponse { + Ok(JobPartialRequestAction::Respond(PartialDecryptionResponse { request_id: partial_request.id, shadow_point: shadow_point, decrypt_shadow: match decrypt_shadow { None => None, Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, &**decrypt_shadow)?), }, - }) + })) } fn check_partial_response(&self, partial_response: &PartialDecryptionResponse) -> Result { diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs index e98eb609446..a62004f4a0b 100644 --- a/secret_store/src/key_server_cluster/jobs/job_session.rs +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -2,7 +2,7 @@ use std::collections::{BTreeSet, BTreeMap}; use key_server_cluster::{Error, NodeId, SessionMeta}; #[derive(Debug, Clone, Copy, PartialEq)] -/// Partial rsponse action. +/// Partial response action. pub enum JobPartialResponseAction { /// Ignore this response. Ignore, @@ -12,6 +12,15 @@ pub enum JobPartialResponseAction { Accept, } +#[derive(Debug, Clone, Copy, PartialEq)] +/// Partial request action. +pub enum JobPartialRequestAction { + /// Repond with reject. + Reject(PartialJobResponse), + /// Respond with this response. + Respond(PartialJobResponse), +} + /// Job executor. pub trait JobExecutor { type PartialJobRequest; @@ -21,7 +30,7 @@ pub trait JobExecutor { /// Prepare job request for given node. fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result; /// Process partial request. - fn process_partial_request(&self, partial_request: Self::PartialJobRequest) -> Result; + fn process_partial_request(&self, partial_request: Self::PartialJobRequest) -> Result, Error>; /// Check partial response of given node. fn check_partial_response(&self, partial_response: &Self::PartialJobResponse) -> Result; /// Compute final job response. @@ -190,7 +199,10 @@ impl JobSession where Executor: JobExe // if we are waiting for response from self => do it if let Some(self_response) = self_response { let self_node_id = self.meta.self_node_id.clone(); - self.on_partial_response(&self_node_id, self_response)?; + match self_response { + JobPartialRequestAction::Respond(self_response) => self.on_partial_response(&self_node_id, self_response)?, + JobPartialRequestAction::Reject(self_response) => self.on_partial_response(&self_node_id, self_response)?, + } } Ok(()) @@ -207,8 +219,18 @@ impl JobSession where Executor: JobExe if self.data.state != JobSessionState::Inactive && self.data.state != JobSessionState::Finished { return Err(Error::InvalidStateForRequest); } - self.data.state = JobSessionState::Finished; - self.transport.send_partial_response(node, self.executor.process_partial_request(request)?) + + let partial_response = match self.executor.process_partial_request(request)? { + JobPartialRequestAction::Respond(partial_response) => { + self.data.state = JobSessionState::Finished; + partial_response + }, + JobPartialRequestAction::Reject(partial_response) => { + self.data.state = JobSessionState::Failed; + partial_response + }, + }; + self.transport.send_partial_response(node, partial_response) } /// When partial request is received by master node. @@ -300,7 +322,7 @@ pub mod tests { use parking_lot::Mutex; use ethkey::Public; use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; - use super::{JobPartialResponseAction, JobExecutor, JobTransport, JobSession, JobSessionState}; + use super::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor, JobTransport, JobSession, JobSessionState}; pub struct SquaredSumJobExecutor; @@ -310,7 +332,7 @@ pub mod tests { type JobResponse = u32; fn prepare_partial_request(&self, _n: &NodeId, _nodes: &BTreeSet) -> Result { Ok(2) } - fn process_partial_request(&self, r: u32) -> Result { if r <= 10 { Ok(r * r) } else { Err(Error::InvalidMessage) } } + fn process_partial_request(&self, r: u32) -> Result, Error> { if r <= 10 { Ok(JobPartialRequestAction::Respond(r * r)) } else { Err(Error::InvalidMessage) } } fn check_partial_response(&self, r: &u32) -> Result { if r % 2 == 0 { Ok(JobPartialResponseAction::Accept) } else { Ok(JobPartialResponseAction::Reject) } } fn compute_response(&self, r: &BTreeMap) -> Result { Ok(r.values().fold(0, |v1, v2| v1 + v2)) } } diff --git a/secret_store/src/key_server_cluster/jobs/key_access_job.rs b/secret_store/src/key_server_cluster/jobs/key_access_job.rs index a5cf0498645..f705dcfe7d7 100644 --- a/secret_store/src/key_server_cluster/jobs/key_access_job.rs +++ b/secret_store/src/key_server_cluster/jobs/key_access_job.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethkey::{Signature, recover}; use key_server_cluster::{Error, NodeId, SessionId, AclStorage}; -use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobExecutor}; +use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor}; /// Purpose of this job is to construct set of nodes, which have agreed to provide access to the given key for the given requestor. pub struct KeyAccessJob { @@ -41,8 +41,10 @@ impl JobExecutor for KeyAccessJob { Ok(self.signature.as_ref().expect("prepare_partial_request is only called on master nodes; new_on_master fills the signature; qed").clone()) } - fn process_partial_request(&self, partial_request: Signature) -> Result { - self.acl_storage.check(&recover(&partial_request, &self.id)?, &self.id).map_err(|_| Error::AccessDenied) + fn process_partial_request(&self, partial_request: Signature) -> Result, Error> { + self.acl_storage.check(&recover(&partial_request, &self.id)?, &self.id) + .map_err(|_| Error::AccessDenied) + .map(|is_confirmed| if is_confirmed { JobPartialRequestAction::Respond(true) } else { JobPartialRequestAction::Reject(false) }) } fn check_partial_response(&self, partial_response: &bool) -> Result { From 2db6f48db0c524a9fa6825f10a57d00b0c9eb782 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 5 Jun 2017 10:19:50 +0300 Subject: [PATCH 39/45] signing job implementation --- .../key_server_cluster/jobs/signing_job.rs | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/secret_store/src/key_server_cluster/jobs/signing_job.rs b/secret_store/src/key_server_cluster/jobs/signing_job.rs index e69de29bb2d..7145591a7f7 100644 --- a/secret_store/src/key_server_cluster/jobs/signing_job.rs +++ b/secret_store/src/key_server_cluster/jobs/signing_job.rs @@ -0,0 +1,129 @@ +use std::collections::{BTreeSet, BTreeMap}; +use ethkey::{Public, Secret}; +use util::H256; +use key_server_cluster::{Error, NodeId, DocumentKeyShare}; +use key_server_cluster::math; +use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; + +/// Signing job. +pub struct SigningJob { + /// This node id. + self_node_id: NodeId, + /// Key share. + key_share: DocumentKeyShare, + /// Session public key. + session_public: Public, + /// Session secret coefficient. + session_secret_coeff: Secret, + /// Request id. + request_id: Option, + /// Message hash. + message_hash: Option, +} + +/// Signing job partial request. +pub struct PartialSigningRequest { + /// Request id. + pub id: Secret, + /// Message hash. + pub message_hash: H256, + /// Id of other nodes, participating in signing. + pub other_nodes_ids: BTreeSet, +} + +/// Signing job partial response. +pub struct PartialSigningResponse { + /// Request id. + pub request_id: Secret, + /// Partial signature. + pub partial_signature: Secret, +} + +impl SigningJob { + pub fn new_on_slave(self_node_id: NodeId, key_share: DocumentKeyShare, session_public: Public, session_secret_coeff: Secret) -> Result { + Ok(SigningJob { + self_node_id: self_node_id, + key_share: key_share, + session_public: session_public, + session_secret_coeff: session_secret_coeff, + request_id: None, + message_hash: None, + }) + } + + pub fn new_on_master(self_node_id: NodeId, key_share: DocumentKeyShare, session_public: Public, session_secret_coeff: Secret, message_hash: H256) -> Result { + Ok(SigningJob { + self_node_id: self_node_id, + key_share: key_share, + session_public: session_public, + session_secret_coeff: session_secret_coeff, + request_id: Some(math::generate_random_scalar()?), + message_hash: Some(message_hash), + }) + } +} + +impl JobExecutor for SigningJob { + type PartialJobRequest = PartialSigningRequest; + type PartialJobResponse = PartialSigningResponse; + type JobResponse = (Secret, Secret); + + fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result { + debug_assert!(nodes.len() == self.key_share.threshold + 1); + + let request_id = self.request_id.as_ref() + .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); + let message_hash = self.message_hash.as_ref() + .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); + let mut other_nodes_ids = nodes.clone(); + other_nodes_ids.remove(node); + + Ok(PartialSigningRequest { + id: request_id.clone(), + message_hash: message_hash.clone(), + other_nodes_ids: other_nodes_ids, + }) + } + + fn process_partial_request(&self, partial_request: PartialSigningRequest) -> Result, Error> { + if partial_request.other_nodes_ids.len() != self.key_share.threshold + || partial_request.other_nodes_ids.contains(&self.self_node_id) + || partial_request.other_nodes_ids.iter().any(|n| !self.key_share.id_numbers.contains_key(n)) { + return Err(Error::InvalidMessage); + } + + let self_id_number = &self.key_share.id_numbers[&self.self_node_id]; + let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &self.key_share.id_numbers[n]); + let combined_hash = math::combine_message_hash_with_public(&partial_request.message_hash, &self.session_public)?; + Ok(JobPartialRequestAction::Respond(PartialSigningResponse { + request_id: partial_request.id, + partial_signature: math::compute_signature_share( + self.key_share.threshold, + &combined_hash, + &self.session_secret_coeff, + &self.key_share.secret_share, + self_id_number, + other_id_numbers + )?, + })) + } + + fn check_partial_response(&self, partial_response: &PartialSigningResponse) -> Result { + if Some(&partial_response.request_id) != self.request_id.as_ref() { + return Ok(JobPartialResponseAction::Ignore); + } + // TODO: check_signature_share() + + Ok(JobPartialResponseAction::Accept) + } + + fn compute_response(&self, partial_responses: &BTreeMap) -> Result<(Secret, Secret), Error> { + let message_hash = self.message_hash.as_ref() + .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); + + let signature_c = math::combine_message_hash_with_public(message_hash, &self.session_public)?; + let signature_s = math::compute_signature(partial_responses.values().map(|r| &r.partial_signature))?; + + Ok((signature_c, signature_s)) + } +} From 3f6100173c971ccd387cd7ae66caee074f561044 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 5 Jun 2017 13:12:47 +0300 Subject: [PATCH 40/45] siginng_session using new consensus_session --- .../src/key_server_cluster/cluster.rs | 45 +- .../key_server_cluster/cluster_sessions.rs | 15 +- .../src/key_server_cluster/consensus.rs | 533 --------- .../key_server_cluster/consensus_session.rs | 223 ---- .../key_server_cluster/decryption_session.rs | 105 +- .../key_server_cluster/generation_session.rs | 20 + .../jobs/consensus_session.rs | 39 +- secret_store/src/key_server_cluster/mod.rs | 2 - .../src/key_server_cluster/signing_session.rs | 1047 +++++++---------- 9 files changed, 530 insertions(+), 1499 deletions(-) delete mode 100644 secret_store/src/key_server_cluster/consensus.rs delete mode 100644 secret_store/src/key_server_cluster/consensus_session.rs diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 44247c9494f..c86f30267e2 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -38,7 +38,7 @@ use key_server_cluster::generation_session::{Session as GenerationSession, Sessi use key_server_cluster::generation_session::SessionImpl as GenerationSessionImpl; use key_server_cluster::decryption_session::{Session as DecryptionSession, DecryptionSessionId}; use key_server_cluster::encryption_session::{Session as EncryptionSession, SessionState as EncryptionSessionState}; -use key_server_cluster::signing_session::{Session as SigningSession, SessionState as SigningSessionState, SigningSessionId}; +use key_server_cluster::signing_session::{Session as SigningSession, SigningSessionId}; use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, read_encrypted_message, WriteMessage, write_encrypted_message}; use key_server_cluster::net::{accept_connection as net_accept_connection, connect as net_connect, Connection as NetConnection}; @@ -418,22 +418,7 @@ impl ClusterCore { let mut is_queued_message = false; loop { - match session.clone().and_then(|session| match message { - GenerationMessage::InitializeSession(ref message) => - session.on_initialize_session(sender.clone(), message), - GenerationMessage::ConfirmInitialization(ref message) => - session.on_confirm_initialization(sender.clone(), message), - GenerationMessage::CompleteInitialization(ref message) => - session.on_complete_initialization(sender.clone(), message), - GenerationMessage::KeysDissemination(ref message) => - session.on_keys_dissemination(sender.clone(), message), - GenerationMessage::PublicKeyShare(ref message) => - session.on_public_key_share(sender.clone(), message), - GenerationMessage::SessionError(ref message) => - session.on_session_error(sender.clone(), message), - GenerationMessage::SessionCompleted(ref message) => - session.on_session_completed(sender.clone(), message), - }) { + match session.clone().and_then(|session| session.process_message(&sender, &message)) { Ok(_) => { // if session is completed => stop let session = session.clone().expect("session.method() call finished with success; session exists; qed"); @@ -618,7 +603,7 @@ impl ClusterCore { connected_nodes.insert(data.self_key_pair.public().clone()); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); - data.sessions.new_signing_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster) + data.sessions.new_signing_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster, None) }, _ => { data.sessions.signing_sessions.get(&signing_session_id) @@ -628,28 +613,12 @@ impl ClusterCore { let mut is_queued_message = false; loop { - match session.clone().and_then(|session| match message { - SigningMessage::SigningConsensusMessage(ref message) => - session.on_consensus_message(sender.clone(), message), - SigningMessage::SigningGenerationMessage(ref message) => - session.on_generation_message(sender.clone(), message), - SigningMessage::RequestPartialSignature(ref message) => - session.on_partial_signature_requested(sender.clone(), message), - SigningMessage::PartialSignature(ref message) => - session.on_partial_signature(sender.clone(), message), - SigningMessage::SigningSessionError(ref message) => - session.on_session_error(sender.clone(), message), - SigningMessage::SigningSessionCompleted(ref message) => - session.on_session_completed(sender.clone(), message), - }) { + match session.clone().and_then(|session| session.process_message(&sender, &message)) { Ok(_) => { // if session is completed => stop let session = session.clone().expect("session.method() call finished with success; session exists; qed"); - let session_state = session.state(); - if session_state == SigningSessionState::Finished { + if session.is_finished() { info!(target: "secretstore_net", "{}: signing session completed", data.self_key_pair.public()); - } - if session_state == SigningSessionState::Finished || session_state == SigningSessionState::Failed { data.sessions.signing_sessions.remove(&signing_session_id); break; } @@ -926,8 +895,8 @@ impl ClusterClient for ClusterClientImpl { let access_key = Random.generate()?.secret().clone(); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); - let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster)?; - session.initialize(requestor_signature, message_hash)?; + let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster, Some(requestor_signature))?; + session.initialize(message_hash)?; Ok(SigningSessionWrapper::new(Arc::downgrade(&self.data), SigningSessionId::new(session_id, access_key), session)) } diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 5fc8406e326..8b07006dd14 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -269,8 +269,9 @@ impl ClusterSessions { } /// Create new signing session. - pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc) -> Result, Error> { + pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc, requester_signature: Option) -> Result, Error> { let session_id = SigningSessionId::new(session_id, sub_session_id); + // some of nodes, which were encrypting secret may be down // => do not use these in signing session let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; @@ -281,13 +282,17 @@ impl ClusterSessions { } self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), move || SigningSessionImpl::new(SigningSessionParams { - id: session_id.id, + meta: SessionMeta { + id: session_id.id, + self_node_id: self.self_node_id.clone(), + master_node_id: master, + threshold: encrypted_data.threshold, + }, access_key: session_id.access_key, - self_node_id: self.self_node_id.clone(), - encrypted_data: encrypted_data, + key_share: encrypted_data, acl_storage: self.acl_storage.clone(), cluster: cluster, - })) + }, requester_signature)) } /// Send signing session error. diff --git a/secret_store/src/key_server_cluster/consensus.rs b/secret_store/src/key_server_cluster/consensus.rs deleted file mode 100644 index 48afec6f228..00000000000 --- a/secret_store/src/key_server_cluster/consensus.rs +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::fmt::Debug; -use std::collections::{BTreeSet, BTreeMap}; -use ethkey::Secret; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::math; - -#[derive(Debug, Clone)] -/// Consensus. -pub enum Consensus { - /// Consensus is currently establishing. - Establishing(ConsensusCore), - /// Consensus is established. - Established(ConsensusCore), - /// Consensus nodes are currently doing their job. - Active(ActiveConsensus), - /// All consensus nodes have responded. - Completed(ActiveConsensus), - /// Consensus is unreachable. - Unreachable, -} - -#[derive(Debug, Clone)] -/// Consensus core data. -pub struct ConsensusCore { - /// Consensus threshold. - threshold: usize, - /// Nodes, which have been requested for participatining in consensus, but not yet responded. - requested_nodes: BTreeSet, - /// Nodes, which have responded with reject to participation request. - rejected_nodes: BTreeSet, - /// Nodes, which have responded with confirm to participation request. - confirmed_nodes: BTreeSet, -} - -#[derive(Debug, Clone)] -/// Active consensus (i.e. consensus with sent requests). -pub struct ActiveConsensus { - /// Consensus core data. - core: ConsensusCore, - /// Selection key. - selection_key: Option, - /// Selected nodes. - selected_nodes: BTreeSet, - /// Active job requests to confirmed nodes. - active_requests: BTreeSet, - /// Confirmed nodes responses. - responses: BTreeMap, -} - -impl Consensus where T: Debug + Clone { - /// Create new consensus. - pub fn new(threshold: usize, nodes: BTreeSet) -> Result { - if nodes.len() < threshold + 1 { - return Err(Error::InvalidThreshold); - } - - Ok(Consensus::Establishing(ConsensusCore { - threshold: threshold, - requested_nodes: nodes, - rejected_nodes: BTreeSet::new(), - confirmed_nodes: BTreeSet::new(), - })) - } - - #[cfg(test)] - /// Return consensus core reference. - pub fn core(&self) -> Option<&ConsensusCore> { - match *self { - Consensus::Establishing(ref consensus) | Consensus::Established(ref consensus) => Some(&consensus), - Consensus::Active(ref consensus) | Consensus::Completed(ref consensus) => Some(&consensus.core), - Consensus::Unreachable => None, - } - } - - /// Is consenus established. - pub fn is_established(&self) -> bool { - match *self { - Consensus::Established(_) => true, - _ => false, - } - } - - /// Are consenus jobs completed. - pub fn is_completed(&self) -> bool { - match *self { - Consensus::Completed(_) => true, - _ => false, - } - } - - /// Is consensus unreachable. - pub fn is_unreachable(&self) -> bool { - match *self { - Consensus::Unreachable => true, - _ => false, - } - } - - /// When node responds to join offer. - pub fn offer_response(&mut self, node: &NodeId, is_accepted: bool) -> Result<(), Error> { - if is_accepted { - self.accept_offer(node) - } else { - self.reject_offer(node) - } - } - - /// When node has accepted join offer. - pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { - let established_consensus = match *self { - Consensus::Establishing(ref mut consensus) => { - consensus.accept_offer(node)?; - if consensus.confirmed_nodes.len() != consensus.threshold + 1 { - return Ok(()); - } - - consensus.clone() - }, - Consensus::Established(ref mut consensus) => return consensus.accept_offer(node), - Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => - return consensus.core.accept_offer(node), - Consensus::Unreachable => return Err(Error::InvalidStateForRequest), - }; - - *self = Consensus::Established(established_consensus); - Ok(()) - } - - /// When node has rejected join offer. - pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { - match *self { - Consensus::Establishing(ref mut consensus) => { - consensus.reject_offer(node)?; - if consensus.requested_nodes.len() + consensus.confirmed_nodes.len() >= consensus.threshold + 1 { - return Ok(()); - } - - // else fall through - }, - Consensus::Established(ref mut consensus) => return consensus.reject_offer(node), - Consensus::Active(ref mut consensus) | Consensus::Completed(ref mut consensus) => - return consensus.core.reject_offer(node), - _ => return Err(Error::InvalidStateForRequest), - } - - *self = Consensus::Unreachable; - Ok(()) - } - - /// When starting/restarting requesting consensus nodes to do their job. - pub fn activate(&mut self) -> Result<(), Error> { - let active_consensus = match *self { - Consensus::Established(ref established_consensus) => ActiveConsensus::new(established_consensus.clone()), - Consensus::Active(ref active_consensus) => ActiveConsensus::new(active_consensus.core.clone()), - _ => return Err(Error::InvalidStateForRequest), - }; - - *self = Consensus::Active(active_consensus); - Ok(()) - } - - /// Select nodes for completing their jobs. - pub fn select_nodes(&mut self, self_node_id: &NodeId) -> Result<(&Secret, &BTreeSet), Error> { - match *self { - Consensus::Active(ref mut consensus) => consensus.select_nodes(self_node_id), - _ => Err(Error::InvalidStateForRequest), - } - } - - /// Get nodes, select nodes for completing their jobs. - pub fn selected_nodes(&self) -> Result<(&Secret, &BTreeSet), Error> { - match *self { - Consensus::Active(ref consensus) => consensus.selected_nodes(), - _ => Err(Error::InvalidStateForRequest), - } - } - - /// When job request is sent to the node. - pub fn job_request_sent(&mut self, node: &NodeId) -> Result<(), Error> { - match *self { - Consensus::Active(ref mut consensus) => consensus.job_request_sent(node), - _ => Err(Error::InvalidStateForRequest), - } - } - - /// When job response is received from the node. - pub fn job_response_received(&mut self, node: &NodeId, selection_key: &Secret, response: T) -> Result<(), Error> { - let completed_consensus = match *self { - Consensus::Active(ref mut consensus) => { - consensus.job_response_received(node, selection_key, response)?; - if consensus.responses.len() != consensus.core.threshold + 1 { - return Ok(()); - } - debug_assert!(consensus.active_requests.is_empty()); - - // else fall through - consensus.clone() - }, - _ => return Err(Error::InvalidStateForRequest), - }; - - *self = Consensus::Completed(completed_consensus); - Ok(()) - } - - #[cfg(test)] - /// Return job reqeuests. - pub fn job_requests(&self) -> Result<&BTreeSet, Error> { - match *self { - Consensus::Active(ref consensus) => consensus.job_requests(), - _ => Err(Error::InvalidStateForRequest), - } - } - - /// Return job responses. - pub fn job_responses(&self) -> Result<&BTreeMap, Error> { - match *self { - Consensus::Active(ref consensus) => consensus.job_responses(), - Consensus::Completed(ref consensus) => consensus.job_responses(), - _ => Err(Error::InvalidStateForRequest), - } - } - - /// When node is timeouted. Returns true if consensus restarted (i.e. caller must resend job requests). - pub fn node_timeouted(&mut self, node: &NodeId) -> Result { - match *self { - Consensus::Establishing(ref mut consensus) => { - consensus.node_timeouted(node)?; - if consensus.requested_nodes.len() + consensus.confirmed_nodes.len() >= consensus.threshold + 1 { - return Ok(false); - } - - // else fall through - }, - Consensus::Established(ref mut consensus) => { - consensus.node_timeouted(node)?; - if consensus.requested_nodes.len() + consensus.confirmed_nodes.len() >= consensus.threshold + 1 { - return Ok(false); - } - - // else fall through - } - Consensus::Active(ref mut consensus) => { - let is_restart_required = consensus.node_timeouted(node)?; - let is_consensus_reachable = consensus.core.requested_nodes.len() + consensus.core.confirmed_nodes.len() >= consensus.core.threshold + 1; - if is_consensus_reachable { - if !is_restart_required { - return Ok(false); - } - - consensus.restart()?; - return Ok(true); - } - - // else fall through - }, - Consensus::Completed(_) => return Ok(false), - _ => return Err(Error::InvalidStateForRequest), - } - - *self = Consensus::Unreachable; - Err(Error::ConsensusUnreachable) - } - - /// When session is timeouted. Consensus is always restarted (if active). - pub fn session_timeouted(&mut self) -> Result<(), Error> { - match *self { - Consensus::Establishing(_) => (), // fall through - Consensus::Established(_) => (), // fall through - Consensus::Active(ref mut consensus) => { - consensus.session_timeouted()?; - if consensus.core.requested_nodes.len() + consensus.core.confirmed_nodes.len() >= consensus.core.threshold + 1 { - return Ok(()); - } - - // else fall through - }, - Consensus::Completed(_) | Consensus::Unreachable => return Ok(()), - } - - *self = Consensus::Unreachable; - Err(Error::ConsensusUnreachable) - } -} - -impl ConsensusCore { - #[cfg(test)] - /// Return rejected nodes list. - pub fn rejected_nodes(&self) -> &BTreeSet { - &self.rejected_nodes - } - - /// When node has accepted join offer. - pub fn accept_offer(&mut self, node: &NodeId) -> Result<(), Error> { - if !self.requested_nodes.remove(node) { - return Err(Error::InvalidNodeForRequest); - } - - self.confirmed_nodes.insert(node.clone()); - Ok(()) - } - - /// When node has rejected join offer. - pub fn reject_offer(&mut self, node: &NodeId) -> Result<(), Error> { - if !self.requested_nodes.remove(node) { - return Err(Error::InvalidNodeForRequest); - } - - self.rejected_nodes.insert(node.clone()); - Ok(()) - } - - /// When node is timeouted. - pub fn node_timeouted(&mut self, node: &NodeId) -> Result<(), Error> { - if self.requested_nodes.remove(node) || self.confirmed_nodes.remove(node) { - self.rejected_nodes.insert(node.clone()); - } - Ok(()) - } -} - -impl ActiveConsensus where T: Debug + Clone { - /// Create new active consensus. - pub fn new(core: ConsensusCore) -> Self { - ActiveConsensus { - core: core, - selection_key: None, - selected_nodes: BTreeSet::new(), - active_requests: BTreeSet::new(), - responses: BTreeMap::new(), - } - } - - /// Select nodes to make job. - pub fn select_nodes(&mut self, self_node_id: &NodeId) -> Result<(&Secret, &BTreeSet), Error> { - if !self.selected_nodes.is_empty() { - return Err(Error::InvalidStateForRequest); - } - - self.selection_key = Some(math::generate_random_scalar()?); - self.selected_nodes.clear(); - if self.core.confirmed_nodes.contains(self_node_id) { - self.selected_nodes.insert(self_node_id.clone()); - } - for confirmed_node in &self.core.confirmed_nodes { - self.selected_nodes.insert(confirmed_node.clone()); - if self.selected_nodes.len() == self.core.threshold + 1 { - break; - } - } - Ok((self.selection_key.as_ref().expect("filled couple of lines above"), &self.selected_nodes)) - } - - /// Get nodes, selected nodes to make their job. - pub fn selected_nodes(&self) -> Result<(&Secret, &BTreeSet), Error> { - if self.selection_key.is_none() || self.selected_nodes.is_empty() { - return Err(Error::InvalidStateForRequest); - } - - Ok((self.selection_key.as_ref().expect("checked couple of lines above"), &self.selected_nodes)) - } - - /// When job request is sent to the node. - pub fn job_request_sent(&mut self, node: &NodeId) -> Result<(), Error> { - if !self.core.confirmed_nodes.contains(node) { - return Err(Error::InvalidNodeForRequest); - } - if !self.selected_nodes.contains(node) { - return Err(Error::InvalidNodeForRequest); - } - if !self.active_requests.insert(node.clone()) { - return Err(Error::InvalidNodeForRequest); - } - - debug_assert!(self.active_requests.len() <= self.core.threshold + 1); - Ok(()) - } - - /// When job response is received from the node. - pub fn job_response_received(&mut self, node: &NodeId, selection_key: &Secret, response: T) -> Result<(), Error> { - if self.selection_key.as_ref() != Some(selection_key) { - // response from previous request => ignore - return Ok(()); - } - if !self.active_requests.remove(node) { - return Err(Error::InvalidStateForRequest); - } - - self.responses.insert(node.clone(), response); - Ok(()) - } - - #[cfg(test)] - /// Return job requests. - pub fn job_requests(&self) -> Result<&BTreeSet, Error> { - Ok(&self.active_requests) - } - - /// Return job responses. - pub fn job_responses(&self) -> Result<&BTreeMap, Error> { - Ok(&self.responses) - } - - /// Restart jobs. - pub fn restart(&mut self) -> Result<(), Error> { - self.selected_nodes.clear(); - self.active_requests.clear(); - self.responses.clear(); - Ok(()) - } - - /// When node is timeouted. - pub fn node_timeouted(&mut self, node: &NodeId) -> Result { - self.core.node_timeouted(node)?; - Ok(self.active_requests.remove(node) || self.responses.remove(node).is_some()) - } - - /// When session is timeouted. - pub fn session_timeouted(&mut self) -> Result<(), Error> { - for timeouted_node in &self.active_requests { - self.core.node_timeouted(timeouted_node)?; - } - self.restart() - } -} - -#[cfg(test)] -mod tests { - use key_server_cluster::Error; - use key_server_cluster::math; - use super::Consensus; - - #[test] - fn consensus_is_not_created_when_not_enough_nodes() { - assert_eq!(Consensus::::new(0, vec![].into_iter().collect()).unwrap_err(), Error::InvalidThreshold); - } - - #[test] - fn consensus_establishing_state() { - let consensus = Consensus::::new(0, vec![math::generate_random_point().unwrap()].into_iter().collect()).unwrap(); - assert!(consensus.core().is_some()); - assert!(!consensus.is_established()); - assert!(!consensus.is_completed()); - assert!(!consensus.is_unreachable()); - } - - #[test] - fn consensus_establishing_2_of_3_succeeded() { - let nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); - consensus.offer_response(&nodes[0], true).unwrap(); - assert!(!consensus.is_established()); - consensus.offer_response(&nodes[1], true).unwrap(); - assert!(consensus.is_established()); - consensus.offer_response(&nodes[2], true).unwrap(); - assert!(consensus.is_established()); - } - - #[test] - fn consensus_establishing_2_of_3_failed() { - let nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); - consensus.offer_response(&nodes[0], false).unwrap(); - assert!(!consensus.is_established()); - assert!(!consensus.is_unreachable()); - consensus.offer_response(&nodes[1], false).unwrap(); - assert!(!consensus.is_established()); - assert!(consensus.is_unreachable()); - } - - #[test] - fn consensus_completion_2_of_3() { - let nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); - nodes.iter().map(|n| consensus.accept_offer(n).unwrap()).collect::>(); - assert!(consensus.is_established()); - - consensus.activate().unwrap(); - let (key, selected_nodes) = consensus.select_nodes(&nodes[0]).map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); - - assert!(selected_nodes.contains(&nodes[0])); - consensus.job_request_sent(&selected_nodes[0]).unwrap(); - consensus.job_request_sent(&selected_nodes[1]).unwrap(); - - consensus.job_response_received(&selected_nodes[0], &key, 1).unwrap(); - consensus.job_response_received(&selected_nodes[1], &key, 2).unwrap(); - - assert_eq!(consensus.job_responses().unwrap().values().fold(0, |i1, i2| i1 + i2), 3); - } - - #[test] - fn consensus_restarts_after_node_timeout() { - let nodes: Vec<_> = (0..4).map(|_| math::generate_random_point().unwrap()).collect(); - let mut consensus = Consensus::::new(1, nodes.iter().cloned().collect()).unwrap(); - nodes.iter().map(|n| consensus.accept_offer(n).unwrap()).collect::>(); - assert!(consensus.is_established()); - - consensus.activate().unwrap(); - let (_, selected_nodes) = consensus.select_nodes(&nodes[0]).map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); - let unselected_nodes: Vec<_> = nodes.iter().filter(|n| !selected_nodes.contains(n)).collect(); - assert!(selected_nodes.contains(&nodes[0])); - - // when non-selected node timeouted => nothing happens - selected_nodes.iter().map(|n| consensus.job_request_sent(n)).collect::>(); - assert_eq!(consensus.node_timeouted(&unselected_nodes[0]), Ok(false)); - let (_, selected_nodes) = consensus.selected_nodes().map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); - - // when selected node timeouted => restarts - selected_nodes.iter().map(|n| consensus.job_request_sent(n)).collect::>(); - assert_eq!(consensus.node_timeouted(&selected_nodes[0]), Ok(true)); - let (_, selected_nodes) = consensus.select_nodes(&nodes[0]).map(|(k, n)| (k.clone(), n.iter().cloned().collect::>())).unwrap(); - - // when selected node timeouted && there are not enough nodes for consensus => fail - selected_nodes.iter().map(|n| consensus.job_request_sent(n)).collect::>(); - assert_eq!(consensus.node_timeouted(&selected_nodes[0]), Err(Error::ConsensusUnreachable)); - } -} diff --git a/secret_store/src/key_server_cluster/consensus_session.rs b/secret_store/src/key_server_cluster/consensus_session.rs deleted file mode 100644 index 188917b21a5..00000000000 --- a/secret_store/src/key_server_cluster/consensus_session.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2015-2017 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::sync::Arc; -use std::fmt::Debug; -use ethkey::{self, Public, Signature}; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage}; -use key_server_cluster::consensus::Consensus; -use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; - -/// Consenus checker. -pub trait ConsensusChecker { - /// Check if we want to accept offer to join consensus group. Consensus is about revealing key `key` to requestor `requestor`. - fn check_offer(&self, key: &SessionId, requestor: &Public) -> bool; -} - -/// Consensus establishing session. -pub struct ConsensusSession { - /// Key generation session id. - id: SessionId, - /// Public identifier of this node. - self_node_id: NodeId, - /// Master node id. - master_node_id: Public, - /// Consensus checker. - consensus_checker: C, - /// Mutable session data. - data: SessionData, -} - -/// ACL checker for consensus establishing session. -pub struct AclConsensusChecker { - /// ACL storate to check access to the resource. - acl_storage: Arc, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Key generation session id. - pub id: SessionId, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Master node id. - pub master_node_id: Public, - /// Consensus checker. - pub consensus_checker: C, -} - -#[derive(Debug)] -/// Mutable data of signing session. -struct SessionData { - /// Current state of the session. - state: SessionState, - /// Consensus establishing result. - result: Option>, -} - -#[derive(Debug, Clone, PartialEq)] -/// Distributed key generation session state. -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Master node waits for other nodes to confirm session initialization. - WaitingForInitializationConfirm, - - // === Final states of the session === - /// Consensus group is established. - Finished, - /// Consensus establish has failed. - Failed, -} - -#[derive(Debug, Clone)] -/// Session action. -pub enum SessionAction { - /// Check session status. - CheckStatus, - /// Broadcast consensus message. - BroadcastMessage(ConsensusMessage), - /// Send consensus message. - SendMessage(NodeId, ConsensusMessage), -} - -impl ConsensusSession where C: ConsensusChecker { - /// Create new signing session. - pub fn new(params: SessionParams) -> Result { - Ok(ConsensusSession { - id: params.id, - self_node_id: params.self_node_id, - master_node_id: params.master_node_id, - consensus_checker: params.consensus_checker, - data: SessionData { - state: SessionState::WaitingForInitialization, - result: None, - } - }) - } - - /// Get current session state. - pub fn state(&self) -> SessionState { - self.data.state.clone() - } - - /// Initialize consensus session. - pub fn initialize(&mut self, requestor_signature: Signature, consensus: &mut Consensus) -> Result { - debug_assert_eq!(self.self_node_id, self.master_node_id); - - // check state - if self.data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // recover requestor public - let requestor = ethkey::recover(&requestor_signature, &self.id)?; - - // update state - self.data.state = SessionState::WaitingForInitializationConfirm; - - // ..and finally check access on our's own - let self_node_id = self.self_node_id.clone(); - let is_confirmed = self.consensus_checker.check_offer(&self.id, &requestor); - self.process_initialization_response(&self_node_id, is_confirmed, consensus)?; - if self.data.state != SessionState::Finished { - Ok(SessionAction::BroadcastMessage(ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requestor_signature: requestor_signature.into(), - }))) - } else { - Ok(SessionAction::CheckStatus) - } - } - - /// When session initialization message is received. - pub fn on_initialize_session(&mut self, sender: NodeId, requestor: &Public) -> Result { - debug_assert!(sender != self.self_node_id); - - // check message - if self.master_node_id != sender { - return Err(Error::InvalidMessage); - } - // check state - if self.data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // check access - let is_confirmed = self.consensus_checker.check_offer(&self.id, &requestor); - - // update state - self.data.state = if is_confirmed { SessionState::Finished } else { SessionState::Failed }; - - // respond to sender - Ok(SessionAction::SendMessage(sender, ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: is_confirmed, - }))) - } - - /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&mut self, sender: NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { - debug_assert!(sender != self.self_node_id); - - // check state - if self.self_node_id != self.master_node_id { - return Err(Error::InvalidMessage); - } - if self.data.state != SessionState::WaitingForInitializationConfirm && self.data.state != SessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - // update state - self.process_initialization_response(&sender, is_confirmed, consensus) - } - - /// Process initialization response from given node. - fn process_initialization_response(&mut self, node: &NodeId, is_confirmed: bool, consensus: &mut Consensus) -> Result { - match consensus.offer_response(node, is_confirmed) { - Ok(_) if consensus.is_established() => { - self.data.result = Some(Ok(())); - self.data.state = SessionState::Finished; - Ok(SessionAction::CheckStatus) - }, - Ok(_) if consensus.is_unreachable() => { - self.data.result = Some(Err(Error::ConsensusUnreachable)); - self.data.state = SessionState::Failed; - Ok(SessionAction::CheckStatus) - }, - Ok(_) => Ok(SessionAction::CheckStatus), - Err(err) => { - self.data.result = Some(Err(err.clone())); - self.data.state = SessionState::Failed; - Err(err) - }, - } - } -} - -impl AclConsensusChecker { - /// Create new ACL-consensus checker. - pub fn new(acl_storage: Arc) -> Self { - AclConsensusChecker { - acl_storage: acl_storage, - } - } -} - -impl ConsensusChecker for AclConsensusChecker { - fn check_offer(&self, key: &SessionId, requestor: &Public) -> bool { - self.acl_storage.check(requestor, key).unwrap_or(false) - } -} diff --git a/secret_store/src/key_server_cluster/decryption_session.rs b/secret_store/src/key_server_cluster/decryption_session.rs index 210e29d4688..6a806bb9217 100644 --- a/secret_store/src/key_server_cluster/decryption_session.rs +++ b/secret_store/src/key_server_cluster/decryption_session.rs @@ -63,6 +63,9 @@ struct SessionCore { pub completed: Condvar, } +/// Decryption consensus session type. +type DecryptionConsensusSession = ConsensusSession; + /// Mutable session data. struct SessionData { /// Consensus-based decryption session. @@ -96,8 +99,6 @@ pub struct SessionParams { pub cluster: Arc, } -type DecryptionConsensusSession = ConsensusSession; - /// Decryption consensus transport. struct DecryptionConsensusTransport { /// Session id. @@ -108,31 +109,6 @@ struct DecryptionConsensusTransport { cluster: Arc, } -impl JobTransport for DecryptionConsensusTransport { - type PartialJobRequest=Signature; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Signature) -> Result<(), Error> { - self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requestor_signature: request.into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - /// Decryption job transport struct DecryptionJobTransport { /// Session id. @@ -143,31 +119,6 @@ struct DecryptionJobTransport { cluster: Arc, } -impl JobTransport for DecryptionJobTransport { - type PartialJobRequest=PartialDecryptionRequest; - type PartialJobResponse=PartialDecryptionResponse; - - fn send_partial_request(&self, node: &NodeId, request: PartialDecryptionRequest) -> Result<(), Error> { - self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - request_id: request.id.into(), - is_shadow_decryption: request.is_shadow_decryption, - nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: PartialDecryptionResponse) -> Result<(), Error> { - self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - request_id: response.request_id.into(), - shadow_point: response.shadow_point.into(), - decrypt_shadow: response.decrypt_shadow, - }))) - } -} - impl SessionImpl { /// Create new decryption session. pub fn new(params: SessionParams, requester_signature: Option) -> Result { @@ -437,6 +388,56 @@ impl SessionCore { } } +impl JobTransport for DecryptionConsensusTransport { + type PartialJobRequest=Signature; + type PartialJobResponse=bool; + + fn send_partial_request(&self, node: &NodeId, request: Signature) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: request.into(), + }) + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: response, + }) + }))) + } +} + +impl JobTransport for DecryptionJobTransport { + type PartialJobRequest=PartialDecryptionRequest; + type PartialJobResponse=PartialDecryptionResponse; + + fn send_partial_request(&self, node: &NodeId, request: PartialDecryptionRequest) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + request_id: request.id.into(), + is_shadow_decryption: request.is_shadow_decryption, + nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: PartialDecryptionResponse) -> Result<(), Error> { + self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + request_id: response.request_id.into(), + shadow_point: response.shadow_point.into(), + decrypt_shadow: response.decrypt_shadow, + }))) + } +} + impl DecryptionSessionId { /// Create new decryption session Id. pub fn new(session_id: SessionId, sub_session_id: Secret) -> Self { diff --git a/secret_store/src/key_server_cluster/generation_session.rs b/secret_store/src/key_server_cluster/generation_session.rs index dbbdea185a1..e94d5bd358a 100644 --- a/secret_store/src/key_server_cluster/generation_session.rs +++ b/secret_store/src/key_server_cluster/generation_session.rs @@ -267,6 +267,26 @@ impl SessionImpl { } } + /// Process single message. + pub fn process_message(&self, sender: &NodeId, message: &GenerationMessage) -> Result<(), Error> { + match message { + &GenerationMessage::InitializeSession(ref message) => + self.on_initialize_session(sender.clone(), message), + &GenerationMessage::ConfirmInitialization(ref message) => + self.on_confirm_initialization(sender.clone(), message), + &GenerationMessage::CompleteInitialization(ref message) => + self.on_complete_initialization(sender.clone(), message), + &GenerationMessage::KeysDissemination(ref message) => + self.on_keys_dissemination(sender.clone(), message), + &GenerationMessage::PublicKeyShare(ref message) => + self.on_public_key_share(sender.clone(), message), + &GenerationMessage::SessionError(ref message) => + self.on_session_error(sender.clone(), message), + &GenerationMessage::SessionCompleted(ref message) => + self.on_session_completed(sender.clone(), message), + } + } + /// When session initialization message is received. pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeSession) -> Result<(), Error> { debug_assert!(self.id == *message.session); diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs index aa26129c9d6..765ad67cf6f 100644 --- a/secret_store/src/key_server_cluster/jobs/consensus_session.rs +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -40,6 +40,8 @@ pub struct ConsensusSession, /// Consensus establish job. consensus_job: JobSession, + /// Consensus group. + consensus_group: BTreeSet, /// Computation job. computation_job: Option>, } @@ -78,6 +80,7 @@ impl ConsensusSes meta: params.meta, requester: requester, consensus_job: consensus_job, + consensus_group: BTreeSet::new(), computation_job: None, }) } @@ -139,23 +142,34 @@ impl ConsensusSes self.process_result(consensus_result) } - /// Disseminate jobs from master node. - pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { + /// Select nodes for processing partial requests. + pub fn select_consensus_group(&mut self) -> Result<&BTreeSet, Error> { debug_assert!(self.meta.self_node_id == self.meta.master_node_id); if self.state != ConsensusSessionState::ConsensusEstablished { return Err(Error::InvalidStateForRequest); } - let consensus_nodes = self.consensus_job.result().expect("disseminate_jobs is only called on master node when consensus is established; qed"); - let is_self_in_consensus = consensus_nodes.contains(&self.meta.self_node_id); - let mut consensus_nodes: BTreeSet<_> = consensus_nodes.into_iter().take(self.meta.threshold + 1).collect(); - if is_self_in_consensus { - consensus_nodes.remove(&self.meta.master_node_id); - consensus_nodes.insert(self.meta.master_node_id.clone()); + if self.consensus_group.is_empty() { + let consensus_group = self.consensus_job.result()?; + let is_self_in_consensus = consensus_group.contains(&self.meta.self_node_id); + self.consensus_group = consensus_group.into_iter().take(self.meta.threshold + 1).collect(); + + if is_self_in_consensus { + self.consensus_group.remove(&self.meta.master_node_id); + self.consensus_group.insert(self.meta.master_node_id.clone()); + } } + Ok(&self.consensus_group) + } + + /// Disseminate jobs from master node. + pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport) -> Result<(), Error> { + let consensus_group = self.select_consensus_group()?.clone(); + self.consensus_group.clear(); + let mut computation_job = JobSession::new(self.meta.clone(), executor, transport); - let computation_result = computation_job.initialize(consensus_nodes); + let computation_result = computation_job.initialize(consensus_group); self.computation_job = Some(computation_job); self.state = ConsensusSessionState::WaitingForPartialResults; self.process_result(computation_result) @@ -239,7 +253,9 @@ impl ConsensusSes } else { // it is used by current computation job // => restart is required if there are still enough nodes + self.consensus_group.clear(); self.state = ConsensusSessionState::EstablishingConsensus; + let consensus_result = self.consensus_job.on_node_error(node); let is_consensus_established = self.consensus_job.state() == JobSessionState::Finished; (is_consensus_established, consensus_result) @@ -260,6 +276,8 @@ impl ConsensusSes // in some states this error is fatal ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => { let _ = self.consensus_job.on_session_timeout(); + + self.consensus_group.clear(); self.state = ConsensusSessionState::EstablishingConsensus; return self.process_result(Err(Error::ConsensusUnreachable)).map(|_| unreachable!()); }, @@ -273,6 +291,7 @@ impl ConsensusSes .clone(); assert!(!timeouted_nodes.is_empty()); // timeout should not ever happen if no requests are active && we are waiting for responses + self.consensus_group.clear(); for timeouted_node in timeouted_nodes { let timeout_result = self.consensus_job.on_node_error(&timeouted_node); self.state = ConsensusSessionState::EstablishingConsensus; @@ -702,4 +721,6 @@ mod tests { assert_eq!(session.state(), ConsensusSessionState::Finished); assert_eq!(session.result(), Ok(20)); } + + // TODO: tests for select_consensus_group } diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index 10acb33c51e..71c505f950c 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -153,8 +153,6 @@ impl Into for Error { mod cluster; mod cluster_sessions; -mod consensus; -mod consensus_session; mod decryption_session; mod encryption_session; mod generation_session; diff --git a/secret_store/src/key_server_cluster/signing_session.rs b/secret_store/src/key_server_cluster/signing_session.rs index 4a497663bed..00246ae643c 100644 --- a/secret_store/src/key_server_cluster/signing_session.rs +++ b/secret_store/src/key_server_cluster/signing_session.rs @@ -14,24 +14,22 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use std::collections::{BTreeSet, VecDeque}; -use std::mem::swap; -use std::ops::DerefMut; +use std::collections::BTreeSet; use std::sync::Arc; use parking_lot::{Mutex, Condvar}; -use ethkey::{self, Public, Secret, Signature}; +use ethkey::{Public, Secret, Signature}; use util::H256; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, DocumentKeyShare}; +use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, AclStorage, DocumentKeyShare}; use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::ClusterSession; -use key_server_cluster::consensus::Consensus; -use key_server_cluster::consensus_session::{ConsensusSession, AclConsensusChecker, SessionParams as ConsensusSessionParams, - SessionState as ConsensusSessionState, SessionAction as ConsensusSessionAction}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - Session as GenerationSessionApi}; -use key_server_cluster::math; + Session as GenerationSessionApi, SessionState as GenerationSessionState}; use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, - RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage, SigningSessionError}; + RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage, SigningSessionError, + InitializeConsensusSession, ConfirmConsensusInitialization}; +use key_server_cluster::jobs::job_session::JobTransport; +use key_server_cluster::jobs::signing_job::{PartialSigningRequest, PartialSigningResponse, SigningJob}; +use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; pub use key_server_cluster::decryption_session::DecryptionSessionId as SigningSessionId; @@ -49,763 +47,533 @@ pub trait Session: Send + Sync + 'static { /// 3) partial signing: every node which has succussfully checked access for the requestor do a partial signing /// 4) signing: master node receives all partial signatures of the secret and computes the signature pub struct SessionImpl { - /// Key generation session id. - id: SessionId, + /// Session core. + core: SessionCore, + /// Session data. + data: Mutex, +} + +/// Immutable session data. +struct SessionCore { + /// Session metadata. + pub meta: SessionMeta, /// Signing session access key. - access_key: Secret, - /// Public identifier of this node. - self_node_id: NodeId, - /// Encrypted data. - encrypted_data: DocumentKeyShare, - /// ACL storate to check access to the resource. - acl_storage: Arc, + pub access_key: Secret, + /// Key share. + pub key_share: DocumentKeyShare, /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, + pub cluster: Arc, /// SessionImpl completion condvar. - completed: Condvar, - /// Mutable session data. - data: Mutex, + pub completed: Condvar, +} + +/// Signing consensus session type. +type SigningConsensusSession = ConsensusSession; + +/// Mutable session data. +struct SessionData { + /// Session state. + pub state: SessionState, + /// Message hash. + pub message_hash: Option, + /// Consensus-based signing session. + pub consensus_session: SigningConsensusSession, + /// Session key generation session. + pub generation_session: Option, + /// Decryption result. + pub result: Option>, } -/// SessionImpl creation parameters +/// Signing session state. +#[derive(Debug, PartialEq)] +pub enum SessionState { + /// State when consensus is establishing. + ConsensusEstablishing, + /// State when session key is generating. + SessionKeyGeneration, + /// State when signature is computing. + SignatureComputing, +} + +/// Session creation parameters pub struct SessionParams { - /// SessionImpl identifier. - pub id: SessionId, - /// SessionImpl access key. + /// Session metadata. + pub meta: SessionMeta, + /// Session access key. pub access_key: Secret, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Encrypted data (result of running encryption_session::SessionImpl). - pub encrypted_data: DocumentKeyShare, - /// Key storage. + /// Key share. + pub key_share: DocumentKeyShare, + /// ACL storage. pub acl_storage: Arc, /// Cluster pub cluster: Arc, } -/// Mutable data of signing session. -struct SessionData { - /// Current state of the session. - state: SessionState, - - // === Values, filled when session initialization just starts === - /// Reference to the node, which has started this session. - master: Option, - /// Public key of requestor. - requestor: Option, - /// Hash of the message to sign. - message_hash: Option, - /// Signing consensus group. - consensus: Option>, - - // === Values, filled when consensus is establishing === - /// Consensus session. - consensus_session: Option>, - - // === Values, filled when session key is generating === - /// Signing cluster subgroup. - generation_cluster: Option>, - /// Session key generation session. - generation_session: Option, - /// Generated session public key. - session_joint_public: Option, - /// Generated session secret coefficient. - session_secret_coeff: Option, - - /// === Values, filled during final decryption === - /// Decrypted secret - signed_message: Option>, +/// Signing consensus transport. +struct SigningConsensusTransport { + /// Session id. + id: SessionId, + /// Session access key. + access_key: Secret, + /// Cluster. + cluster: Arc, } -#[derive(Debug, Clone, PartialEq)] -/// Distributed key generation session state. -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Establishing consensus. - EstablishingConsensus, - /// Consensus established - EstablishedConsensus, - - /// === One-time key genration states === - /// Generating one-time key. - SessionKeyGeneration, - /// One-time key generated. - SessionKeyGenerated, - - // === Signature generation states === - /// Waiting for partial signatures. - WaitingForPartialSignature, - /// Waiting for partial signature request. - WaitingForPartialSignatureRequest, - - // === Final states of the session === - /// Signing is completed. - Finished, - /// Signing is failed. - Failed, +/// Signing key generation transport. +struct SessionKeyGenerationTransport { + /// Session access key. + access_key: Secret, + /// Cluster. + cluster: Arc, + /// Other nodes ids. + other_nodes_ids: BTreeSet, } -/// Signing group of cluster nodes. -struct SigningCluster { - /// Original cluster reference. +/// Signing job transport +struct SigningJobTransport { + /// Session id. + id: SessionId, + //// Session access key. + access_key: Secret, + /// Cluster. cluster: Arc, - /// This node id. - self_node_id: NodeId, - /// Signing group. - nodes: BTreeSet, - /// Generation session messages. - messages: Mutex>, } impl SessionImpl { - /// Create new decryption session. - pub fn new(params: SessionParams) -> Result { - check_encrypted_data(¶ms.self_node_id, ¶ms.encrypted_data)?; + /// Create new signing session. + pub fn new(params: SessionParams, requester_signature: Option) -> Result { + debug_assert_eq!(params.meta.threshold, params.key_share.threshold); + debug_assert_eq!(params.meta.self_node_id == params.meta.master_node_id, requester_signature.is_some()); + + use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; + + // check nodes and threshold + let nodes = params.key_share.id_numbers.keys().cloned().collect(); + check_cluster_nodes(¶ms.meta.self_node_id, &nodes)?; + check_threshold(params.key_share.threshold, &nodes)?; + + let consensus_transport = SigningConsensusTransport { + id: params.meta.id.clone(), + access_key: params.access_key.clone(), + cluster: params.cluster.clone(), + }; Ok(SessionImpl { - id: params.id, - access_key: params.access_key, - self_node_id: params.self_node_id, - encrypted_data: params.encrypted_data, - acl_storage: params.acl_storage, - cluster: params.cluster, - completed: Condvar::new(), + core: SessionCore { + meta: params.meta.clone(), + access_key: params.access_key, + key_share: params.key_share, + cluster: params.cluster, + completed: Condvar::new(), + }, data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - master: None, - requestor: None, + state: SessionState::ConsensusEstablishing, message_hash: None, - consensus_session: None, - consensus: None, - generation_cluster: None, + consensus_session: match requester_signature { + Some(requester_signature) => ConsensusSession::new_on_master(ConsensusSessionParams { + meta: params.meta, + acl_storage: params.acl_storage.clone(), + consensus_transport: consensus_transport, + }, requester_signature)?, + None => ConsensusSession::new_on_slave(ConsensusSessionParams { + meta: params.meta, + acl_storage: params.acl_storage.clone(), + consensus_transport: consensus_transport, + })?, + }, generation_session: None, - session_joint_public: None, - session_secret_coeff: None, - signed_message: None, - }) + result: None, + }), }) } - /// Get current session state. - pub fn state(&self) -> SessionState { - self.data.lock().state.clone() - } - - /// Initialize signing session. - pub fn initialize(&self, requestor_signature: Signature, message_hash: H256) -> Result<(), Error> { + /// Initialize signing session on master node. + pub fn initialize(&self, message_hash: H256) -> Result<(), Error> { let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // recover requestor signature - let requestor_public = ethkey::recover(&requestor_signature, &self.id)?; - - // update state - data.state = SessionState::EstablishingConsensus; - data.master = Some(self.self_node_id.clone()); - data.requestor = Some(requestor_public); data.message_hash = Some(message_hash); - - // create consensus session - let mut consensus = Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?; - let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { - id: self.id.clone(), - self_node_id: self.self_node_id.clone(), - master_node_id: self.self_node_id.clone(), - consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), - })?; - - // start consensus session - let consensus_action = consensus_session.initialize(requestor_signature, &mut consensus)?; - data.consensus = Some(consensus); - data.consensus_session = Some(consensus_session); - - // process consensus action - SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; - - // if single node is required to sign message, proceed - if data.state == SessionState::EstablishedConsensus { - SessionImpl::start_generating_session_key(&self.self_node_id, &self.encrypted_data, &self.cluster, &mut *data)?; - SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; - SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data)?; - SessionImpl::do_signing(&mut *data)?; - self.completed.notify_all(); + data.consensus_session.initialize(self.core.key_share.id_numbers.keys().cloned().collect())?; + + if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { + let generation_session = GenerationSession::new(GenerationSessionParams { + id: self.core.meta.id.clone(), + self_node_id: self.core.meta.self_node_id.clone(), + key_storage: None, + cluster: Arc::new(SessionKeyGenerationTransport { + access_key: self.core.access_key.clone(), + cluster: self.core.cluster.clone(), + other_nodes_ids: BTreeSet::new() + }), + }); + generation_session.initialize(Public::default(), 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect())?; + + debug_assert_eq!(generation_session.state(), GenerationSessionState::WaitingForGenerationConfirmation); + let joint_public_and_secret = generation_session + .joint_public_and_secret() + .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; + data.generation_session = Some(generation_session); + data.state = SessionState::SignatureComputing; + + self.core.disseminate_jobs(&mut data.consensus_session, joint_public_and_secret.0, joint_public_and_secret.1, message_hash)?; + + debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); + data.result = Some(Ok(data.consensus_session.result()?)); + self.core.completed.notify_all(); } Ok(()) } + /// Process signing message. + pub fn process_message(&self, sender: &NodeId, message: &SigningMessage) -> Result<(), Error> { + match message { + &SigningMessage::SigningConsensusMessage(ref message) => + self.on_consensus_message(sender, message), + &SigningMessage::SigningGenerationMessage(ref message) => + self.on_generation_message(sender, message), + &SigningMessage::RequestPartialSignature(ref message) => + self.on_partial_signature_requested(sender, message), + &SigningMessage::PartialSignature(ref message) => + self.on_partial_signature(sender, message), + &SigningMessage::SigningSessionError(ref message) => + self.on_session_error(sender, message), + &SigningMessage::SigningSessionCompleted(ref message) => + self.on_session_completed(sender, message), + } + } + /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: NodeId, message: &SigningConsensusMessage) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); + pub fn on_consensus_message(&self, sender: &NodeId, message: &SigningConsensusMessage) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); + let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; + data.consensus_session.on_consensus_message(&sender, &message.message)?; - // if we are waiting for initialization - if data.state == SessionState::WaitingForInitialization { - data.master = Some(sender.clone()); - data.state = SessionState::EstablishingConsensus; - data.consensus = Some(Consensus::new(self.encrypted_data.threshold, self.encrypted_data.id_numbers.keys().cloned().collect())?); - data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { - id: self.id.clone(), - self_node_id: self.self_node_id.clone(), - master_node_id: sender.clone(), - consensus_checker: AclConsensusChecker::new(self.acl_storage.clone()), - })?); + let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; + if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { + return Ok(()); } - // process message - let consensus_action = { - let mut data = data.deref_mut(); - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidStateForRequest)?; - match message.message { - ConsensusMessage::InitializeConsensusSession(ref message) => { - // check state - if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); - } - let requestor = ethkey::recover(&message.requestor_signature, &self.id)?; - let consensus_action = consensus_session.on_initialize_session(sender, &requestor)?; - data.requestor = Some(requestor); - consensus_action - }, - ConsensusMessage::ConfirmConsensusInitialization(ref message) => { - let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; - if data.state != SessionState::EstablishingConsensus { - // consensus is already established => mark node as confirmed (for restart case) and ignore - return consensus.offer_response(&sender, message.is_confirmed); - } - consensus_session.on_confirm_initialization(sender, message.is_confirmed, consensus)? - }, - } - }; - SessionImpl::process_consensus_session_action(&self.id, &self.access_key, &self.cluster, &self.completed, &mut *data, consensus_action)?; + let consensus_group = data.consensus_session.select_consensus_group()?.clone(); + let mut other_consensus_group_nodes = consensus_group.clone(); + other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - // if consensus is established, start generating session key on master - if data.state != SessionState::EstablishedConsensus || data.master.as_ref() != Some(&self.self_node_id) { - return Ok(()); - } + let generation_session = GenerationSession::new(GenerationSessionParams { + id: self.core.meta.id.clone(), + self_node_id: self.core.meta.self_node_id.clone(), + key_storage: None, + cluster: Arc::new(SessionKeyGenerationTransport { + access_key: self.core.access_key.clone(), + cluster: self.core.cluster.clone(), + other_nodes_ids: other_consensus_group_nodes, + }), + }); + generation_session.initialize(Public::default(), self.core.key_share.threshold, consensus_group)?; + data.generation_session = Some(generation_session); + data.state = SessionState::SessionKeyGeneration; - SessionImpl::start_generating_session_key(&self.self_node_id, &self.encrypted_data, &self.cluster, &mut *data)?; - SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data) + Ok(()) } /// When session key related message is received. - pub fn on_generation_message(&self, sender: NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); + pub fn on_generation_message(&self, sender: &NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); - // check state - if data.state == SessionState::EstablishingConsensus { - // on 'slave' nodes, consensus is established when session key generation starts - if data.master.as_ref() != Some(&sender) { + if let &GenerationMessage::InitializeSession(ref message) = &message.message { + if &self.core.meta.master_node_id != sender { return Err(Error::InvalidMessage); } - data.state = SessionState::EstablishedConsensus; - } - if data.state == SessionState::EstablishedConsensus { - match message.message { - GenerationMessage::InitializeSession(ref message) => { - // check message - if data.master.as_ref() != Some(&sender) { - return Err(Error::InvalidMessage); - } - - // update state - data.state = SessionState::SessionKeyGeneration; - - // cluster for generation session would only include nodes, which has fodmed consensus group - let generation_cluster = Arc::new(SigningCluster::new(self.cluster.clone(), self.self_node_id.clone(), message.nodes.keys().cloned().map(Into::into).collect())); - data.generation_cluster = Some(generation_cluster.clone()); + let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); + let mut other_consensus_group_nodes = consensus_group.clone(); + other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - // create generation session - data.generation_session = Some(GenerationSession::new(GenerationSessionParams { - id: message.session.clone().into(), - self_node_id: self.self_node_id.clone(), - key_storage: None, - cluster: generation_cluster, - })); - }, - _ => return Err(Error::InvalidStateForRequest), - } + let generation_session = GenerationSession::new(GenerationSessionParams { + id: self.core.meta.id.clone(), + self_node_id: self.core.meta.self_node_id.clone(), + key_storage: None, + cluster: Arc::new(SessionKeyGenerationTransport { + access_key: self.core.access_key.clone(), + cluster: self.core.cluster.clone(), + other_nodes_ids: other_consensus_group_nodes + }), + }); + data.generation_session = Some(generation_session); + data.state = SessionState::SessionKeyGeneration; } - // do not check for other states, as completion message can come after moving to other states - // if generation session exists, let it process message - // process message - let is_generation_completed = data.session_joint_public.is_some(); { let generation_session = data.generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - match message.message { - GenerationMessage::InitializeSession(ref message) => - generation_session.on_initialize_session(sender, message)?, - GenerationMessage::ConfirmInitialization(ref message) => - generation_session.on_confirm_initialization(sender, message)?, - GenerationMessage::CompleteInitialization(ref message) => - generation_session.on_complete_initialization(sender, message)?, - GenerationMessage::KeysDissemination(ref message) => - generation_session.on_keys_dissemination(sender, message)?, - GenerationMessage::PublicKeyShare(ref message) => - generation_session.on_public_key_share(sender, message)?, - GenerationMessage::SessionError(ref message) => - generation_session.on_session_error(sender, message)?, - GenerationMessage::SessionCompleted(ref message) => - generation_session.on_session_completed(sender, message)?, + let is_key_generating = generation_session.state() != GenerationSessionState::Finished; + generation_session.process_message(sender, &message.message)?; + + let is_key_generated = generation_session.state() == GenerationSessionState::Finished; + if !is_key_generating || !is_key_generated { + return Ok(()); } } - SessionImpl::process_generation_session_action(&self.id, &self.access_key, &self.completed, &mut *data)?; - // if session key generated just now => start generating partial signatures - if data.state != SessionState::SessionKeyGenerated || is_generation_completed { + data.state = SessionState::SignatureComputing; + if self.core.meta.master_node_id != self.core.meta.self_node_id { return Ok(()); } - SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data) + let message_hash = data.message_hash + .expect("we are on master node; on master node message_hash is filled in initialize(); on_generation_message follows initialize; qed"); + let joint_public_and_secret = data.generation_session.as_ref() + .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") + .joint_public_and_secret() + .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; + self.core.disseminate_jobs(&mut data.consensus_session, joint_public_and_secret.0, joint_public_and_secret.1, message_hash) } /// When partial signature is requested. - pub fn on_partial_signature_requested(&self, sender: NodeId, message: &RequestPartialSignature) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(sender != self.self_node_id); + pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &RequestPartialSignature) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); - // check state - if data.master != Some(sender) { + if sender != &self.core.meta.master_node_id { return Err(Error::InvalidMessage); } - if data.state != SessionState::WaitingForPartialSignatureRequest { - match data.state { - SessionState::SessionKeyGeneration => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } + if data.state != SessionState::SignatureComputing { + return Err(Error::InvalidStateForRequest); } - // update data - data.message_hash = Some(message.message_hash.clone().into()); - - // calculate partial signature - let session_joint_public = data.session_joint_public.as_ref().expect("we are in SessionKeyGenerated state; public is generated during SessionKeyGenerating; qed"); - let session_secret_coeff = data.session_secret_coeff.as_ref().expect("we are in SessionKeyGenerated state; coeff is generated during SessionKeyGenerating; qed"); - let nodes: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).filter(|n| n != &self.self_node_id).collect(); - let partial_signature = SessionImpl::do_partial_signing(&self.self_node_id, &message.message_hash.clone().into(), &self.encrypted_data, &nodes, session_joint_public, session_secret_coeff)?; - - self.cluster.send(&sender, Message::Signing(SigningMessage::PartialSignature(PartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - request_id: message.request_id.clone().into(), - partial_signature: partial_signature.into(), - })))?; - - // master could ask us for another partial signature in case of restart - // => no state change is required + let joint_public_and_secret = data.generation_session.as_ref() + .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") + .joint_public_and_secret() + .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; + let signing_job = SigningJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.key_share.clone(), joint_public_and_secret.0, joint_public_and_secret.1)?; + let signing_transport = self.core.signing_transport(); - Ok(()) + data.consensus_session.on_job_request(sender, PartialSigningRequest { + id: message.request_id.clone().into(), + message_hash: message.message_hash.clone().into(), + other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), + }, signing_job, signing_transport) } /// When partial signature is received. - pub fn on_partial_signature(&self, sender: NodeId, message: &PartialSignature) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(sender != self.self_node_id); + pub fn on_partial_signature(&self, sender: &NodeId, message: &PartialSignature) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); let mut data = self.data.lock(); + data.consensus_session.on_job_response(sender, PartialSigningResponse { + request_id: message.request_id.clone().into(), + partial_signature: message.partial_signature.clone().into(), + })?; - // check state - if data.state != SessionState::WaitingForPartialSignature { - return Err(Error::InvalidStateForRequest); - } - - // check partial signature - // TODO: check_signature_share() - - // remember partial signature - { - let consensus = data.consensus.as_mut().ok_or(Error::InvalidStateForRequest)?; - consensus.job_response_received(&sender, &message.request_id.clone().into(), message.partial_signature.clone().into())?; - - // check if we have enough shadow points to decrypt the secret - if !consensus.is_completed() { - return Ok(()); - } + if data.consensus_session.state() != ConsensusSessionState::Finished { + return Ok(()); } - // notify all other nodes about session completion - self.cluster.broadcast(Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), + self.core.cluster.broadcast(Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { + session: self.core.meta.id.clone().into(), + sub_session: self.core.access_key.clone().into(), })))?; - // do signing - SessionImpl::do_signing(&mut *data)?; - self.completed.notify_all(); + data.result = Some(Ok(data.consensus_session.result()?)); + self.core.completed.notify_all(); Ok(()) } /// When session is completed. - pub fn on_session_completed(&self, sender: NodeId, message: &SigningSessionCompleted) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(self.access_key == *message.sub_session); - debug_assert!(sender != self.self_node_id); - - let mut data = self.data.lock(); + pub fn on_session_completed(&self, sender: &NodeId, message: &SigningSessionCompleted) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + debug_assert!(sender != &self.core.meta.self_node_id); - if data.master != Some(sender) { - return Err(Error::InvalidMessage); - } - // it is up to master node to decide when to complete session - // => we could only fail if already failed - if data.state == SessionState::Failed { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.state = SessionState::Finished; - - Ok(()) + self.data.lock().consensus_session.on_session_completed(sender) } /// When error has occured on another node. - pub fn on_session_error(&self, sender: NodeId, message: &SigningSessionError) -> Result<(), Error> { - let mut data = self.data.lock(); - - warn!("{}: signing session failed with error: {:?} from {}", self.self_node_id, message.error, sender); - - data.state = SessionState::Failed; - data.signed_message = Some(Err(Error::Io(message.error.clone()))); - self.completed.notify_all(); - - Ok(()) - } - - /// Process nested consensus session action. - fn process_consensus_session_action(id: &SessionId, access_key: &Secret, cluster: &Arc, completed: &Condvar, data: &mut SessionData, action: ConsensusSessionAction) -> Result<(), Error> { - match action { - ConsensusSessionAction::BroadcastMessage(message) => { - cluster.broadcast(Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - })))? - }, - ConsensusSessionAction::SendMessage(to, message) => { - cluster.send(&to, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - })))? - }, - ConsensusSessionAction::CheckStatus => (), - } - - match data.consensus_session.as_ref() - .expect("we are processing consensus session action; action is a result of processing message by session; qed") - .state() { - ConsensusSessionState::Finished => data.state = SessionState::EstablishedConsensus, - ConsensusSessionState::Failed => { - data.state = SessionState::Failed; - data.signed_message = Some(Err(Error::ConsensusUnreachable)); - completed.notify_all(); - }, - _ => (), - } - - Ok(()) - } - - /// Start generating one-time session key. - fn start_generating_session_key(self_node_id: &NodeId, encrypted_data: &DocumentKeyShare, cluster: &Arc, data: &mut SessionData) -> Result<(), Error> { - // update state - data.state = SessionState::SessionKeyGeneration; - - // select nodes to make signature - let mut consensus = data.consensus.as_mut().expect("consensus is filled during initialization phase; key generation phase follows initialization; qed"); - consensus.activate()?; - let (_, selected_nodes) = consensus.select_nodes(self_node_id)?; - - // create generation session - let generation_cluster = Arc::new(SigningCluster::new(cluster.clone(), self_node_id.clone(), selected_nodes.clone())); - let generation_session = GenerationSession::new(GenerationSessionParams { - id: H256::default(), // doesn't matter - self_node_id: self_node_id.clone(), - key_storage: None, - cluster: generation_cluster.clone(), - }); - - // start generation session - generation_session.initialize(Public::default(), // doesn't matter - encrypted_data.threshold, selected_nodes.clone())?; - data.generation_cluster = Some(generation_cluster); - data.generation_session = Some(generation_session); - - Ok(()) + pub fn on_session_error(&self, sender: &NodeId, message: &SigningSessionError) -> Result<(), Error> { + self.process_node_error(Some(&sender), &message.error) } - /// Process nested key generation session action. - fn process_generation_session_action(id: &SessionId, access_key: &Secret, completed: &Condvar, data: &mut SessionData) -> Result<(), Error> { - // it only makes sense to process actions if session is currently active - if data.state != SessionState::SessionKeyGeneration { - return Ok(()); - } - - // send every scheduled message - { - let generation_cluster = data.generation_cluster.as_ref() - .expect("generation cluster is crated when SessionKeyGeneration state starts; we are in SessionKeyGeneration state; qed"); - for (to, message) in generation_cluster.messages() { - match message { - Message::Generation(message) => generation_cluster.cluster().send(&to, Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { - session: id.clone().into(), - sub_session: access_key.clone().into(), - message: message, - })))?, - _ => unreachable!("generation session only sends generation messages"), - } + /// Process error from the other node. + fn process_node_error(&self, node: Option<&NodeId>, error: &String) -> Result<(), Error> { + let mut data = self.data.lock(); + match { + match node { + Some(node) => data.consensus_session.on_node_error(node), + None => data.consensus_session.on_session_timeout(), } - } - - // and now check if session key is generated - let generation_session = data.generation_session.as_ref() - .expect("generation session is crated when SessionKeyGeneration state starts; we are in SessionKeyGeneration state; qed"); - match generation_session.joint_public_and_secret() { - Some(Ok(session_joint_public_and_secret)) => { - data.state = SessionState::SessionKeyGenerated; - data.session_joint_public = Some(session_joint_public_and_secret.0); - data.session_secret_coeff = Some(session_joint_public_and_secret.1); - Ok(()) - }, - Some(Err(err)) => { - data.state = SessionState::Failed; - data.signed_message = Some(Err(err)); - completed.notify_all(); - Ok(()) + } { + Ok(false) => Ok(()), + Ok(true) => { + let message_hash = data.message_hash.as_ref().cloned() + .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed"); + let joint_public_and_secret = data.generation_session.as_ref() + .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed") + .joint_public_and_secret() + .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed")?; + let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, joint_public_and_secret.0, joint_public_and_secret.1, message_hash); + match disseminate_result { + Ok(()) => Ok(()), + Err(err) => { + warn!("{}: signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); + + data.result = Some(Err(err.clone())); + self.core.completed.notify_all(); + Err(err) + } + } }, - None => Ok(()), - } - } - - /// Start waiting for partial signatures/partial signatures requests. - fn start_waiting_for_partial_signing(self_node_id: &NodeId, session_id: &SessionId, access_key: &Secret, cluster: &Arc, encrypted_data: &DocumentKeyShare, data: &mut SessionData) -> Result<(), Error> { - if data.master.as_ref() != Some(self_node_id) { - // if we are on the slave node, wait for partial signature requests - data.state = SessionState::WaitingForPartialSignatureRequest; - return Ok(()); - } - - // update state - data.state = SessionState::WaitingForPartialSignature; - - // send jobs to all selected nodes - let consensus = data.consensus.as_mut().expect("consensus is created on initialization phase; partial signing phase follows initialization; qed"); - let (request_id, mut confirmed_nodes) = consensus.selected_nodes().map(|(r, n)| (r.clone(), n.clone()))?; - - // send requests - let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); - for node in confirmed_nodes.iter().filter(|n| n != &self_node_id) { - consensus.job_request_sent(node)?; - cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { - session: session_id.clone().into(), - sub_session: access_key.clone().into(), - request_id: request_id.clone().into(), - message_hash: message_hash.clone().into(), - nodes: confirmed_nodes.iter().cloned().map(Into::into).collect(), - })))?; - } - - // confirmation from this node, if this node is in consensus group - if confirmed_nodes.remove(self_node_id) { - let signing_result = { - let session_joint_public = data.session_joint_public.as_ref().expect("session key is generated on key generation phase; partial signing phase follows initialization; qed"); - let session_secret_coeff = data.session_secret_coeff.as_ref().expect("sessin coeff is generated on key generation phase; partial signing phase follows initialization; qed"); - SessionImpl::do_partial_signing(self_node_id, message_hash, encrypted_data, &confirmed_nodes, session_joint_public, session_secret_coeff)? - }; + Err(err) => { + warn!("{}: signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - consensus.job_request_sent(&self_node_id)?; - consensus.job_response_received(&self_node_id, &request_id, signing_result)?; + data.result = Some(Err(err.clone())); + self.core.completed.notify_all(); + Err(err) + }, } - - Ok(()) - } - - /// Compute partial signature. - fn do_partial_signing(self_node_id: &NodeId, message_hash: &H256, encrypted_data: &DocumentKeyShare, session_nodes: &BTreeSet, session_joint_public: &Public, session_secret_coeff: &Secret) -> Result { - debug_assert!(!session_nodes.contains(self_node_id)); - debug_assert!(session_nodes.len() == encrypted_data.threshold); - - let combined_hash = math::combine_message_hash_with_public(&message_hash, &session_joint_public)?; - math::compute_signature_share( - encrypted_data.threshold, - &combined_hash, - &session_secret_coeff, - &encrypted_data.secret_share, - &encrypted_data.id_numbers[self_node_id], - session_nodes.iter().map(|n| &encrypted_data.id_numbers[n]) - ) - } - - /// Compute signature - fn do_signing(data: &mut SessionData) -> Result<(), Error> { - let message_hash = data.message_hash.as_ref().expect("message_hash on master is filled in initialization phase; this is master node; qed"); - let session_joint_public = data.session_joint_public.as_ref().expect("session key is generated on key generation phase; signing phase follows initialization; qed"); - let partial_signatures = data.consensus.as_ref().expect("consensus on master is filled in initialization phase; this is master node; qed").job_responses()?.values(); - - let signature_c = math::combine_message_hash_with_public(message_hash, session_joint_public)?; - let signature_s = math::compute_signature(partial_signatures)?; - - data.signed_message = Some(Ok((signature_c, signature_s))); - - Ok(()) } } impl ClusterSession for SessionImpl { fn is_finished(&self) -> bool { let data = self.data.lock(); - data.state == SessionState::Failed - || data.state == SessionState::Finished + data.consensus_session.state() == ConsensusSessionState::Failed + || data.consensus_session.state() == ConsensusSessionState::Finished } fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - let is_self_master = data.master.as_ref() == Some(&self.self_node_id); - let is_other_master = data.master.as_ref() == Some(node); - // if this is master node, we might have to restart - if is_self_master { - let is_restart_required = match data.consensus.as_mut() { - None => false, - Some(consensus) => match consensus.node_timeouted(node) { - Ok(false) => return, - Ok(true) => true, - Err(_) => false, //fall through - }, - }; - if is_restart_required { - if SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { - return; - } - } - } else if !is_other_master { - // disconnected from non-master node on non-master node - // => this does not affect this session - return; - } - // else: disconnecting from master node means failure - - warn!("{}: signing session failed because {} connection has timeouted", self.self_node_id, node); - - data.state = SessionState::Failed; - data.signed_message = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + // ignore error, only state matters + let _ = self.process_node_error(Some(node), &Error::NodeDisconnected.into()); } fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - let is_self_master = data.master.as_ref() == Some(&self.self_node_id); - // if this is master node, we might have to restart - if is_self_master { - let is_restart_required = match data.consensus.as_mut() { - None => false, - Some(consensus) => match consensus.session_timeouted() { - Ok(_) => true, - Err(_) => false, - }, - }; - if is_restart_required { - if SessionImpl::start_waiting_for_partial_signing(&self.self_node_id, &self.id, &self.access_key, &self.cluster, &self.encrypted_data, &mut *data).is_ok() { - return; - } - } - } - - warn!("{}: signing session failed with timeout", self.self_node_id); - - data.state = SessionState::Failed; - data.signed_message = Some(Err(Error::NodeDisconnected)); - self.completed.notify_all(); + // ignore error, only state matters + let _ = self.process_node_error(None, &Error::NodeDisconnected.into()); } } -impl SigningCluster { - pub fn new(cluster: Arc, self_node_id: NodeId, subset: BTreeSet) -> Self { - SigningCluster { - cluster: cluster, - self_node_id: self_node_id, - nodes: subset, - messages: Mutex::new(VecDeque::new()), +impl Session for SessionImpl { + fn wait(&self) -> Result<(Secret, Secret), Error> { + let mut data = self.data.lock(); + if !data.result.is_some() { + self.core.completed.wait(&mut data); } - } - pub fn cluster(&self) -> &Arc { - &self.cluster + data.result.as_ref() + .expect("checked above or waited for completed; completed is only signaled when result.is_some(); qed") + .clone() } +} - pub fn messages(&self) -> VecDeque<(NodeId, Message)> { - let mut lock = self.messages.lock(); - let mut messages = VecDeque::new(); - swap(&mut messages, &mut *lock); - messages +impl SessionKeyGenerationTransport { + fn map_message(&self, message: Message) -> Result { + match message { + Message::Generation(message) => Ok(Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { + session: message.session_id().clone().into(), + sub_session: self.access_key.clone().into(), + message: message, + }))), + _ => Err(Error::InvalidMessage), + } } } -impl Cluster for SigningCluster { +impl Cluster for SessionKeyGenerationTransport { fn broadcast(&self, message: Message) -> Result<(), Error> { - let mut messages = self.messages.lock(); - for node in &self.nodes { - if node != &self.self_node_id { - messages.push_back((node.clone(), message.clone())); - } + let message = self.map_message(message)?; + for to in &self.other_nodes_ids { + self.cluster.send(to, message.clone())?; } Ok(()) } fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - self.messages.lock().push_back((to.clone(), message)); - Ok(()) + debug_assert!(self.other_nodes_ids.contains(to)); + self.cluster.send(to, self.map_message(message)?) } } -impl Session for SessionImpl { - fn wait(&self) -> Result<(Secret, Secret), Error> { - let mut data = self.data.lock(); - if !data.signed_message.is_some() { - self.completed.wait(&mut data); +impl SessionCore { + pub fn signing_transport(&self) -> SigningJobTransport { + SigningJobTransport { + id: self.meta.id.clone(), + access_key: self.access_key.clone(), + cluster: self.cluster.clone() } + } - data.signed_message.as_ref() - .expect("checked above or waited for completed; completed is only signaled when signed_message.is_some(); qed") - .clone() + pub fn disseminate_jobs(&self, consensus_session: &mut SigningConsensusSession, session_public: Public, session_secret_share: Secret, message_hash: H256) -> Result<(), Error> { + let signing_job = SigningJob::new_on_master(self.meta.self_node_id.clone(), self.key_share.clone(), session_public, session_secret_share, message_hash)?; + consensus_session.disseminate_jobs(signing_job, self.signing_transport()) } } -fn check_encrypted_data(self_node_id: &Public, encrypted_data: &DocumentKeyShare) -> Result<(), Error> { - use key_server_cluster::generation_session::{check_cluster_nodes, check_threshold}; +impl JobTransport for SigningConsensusTransport { + type PartialJobRequest=Signature; + type PartialJobResponse=bool; - let nodes = encrypted_data.id_numbers.keys().cloned().collect(); - check_cluster_nodes(self_node_id, &nodes)?; - check_threshold(encrypted_data.threshold, &nodes) + fn send_partial_request(&self, node: &NodeId, request: Signature) -> Result<(), Error> { + self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: request.into(), + }) + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { + self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: response, + }) + }))) + } +} + +impl JobTransport for SigningJobTransport { + type PartialJobRequest=PartialSigningRequest; + type PartialJobResponse=PartialSigningResponse; + + fn send_partial_request(&self, node: &NodeId, request: PartialSigningRequest) -> Result<(), Error> { + self.cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + request_id: request.id.into(), + message_hash: request.message_hash.into(), + nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: PartialSigningResponse) -> Result<(), Error> { + self.cluster.send(node, Message::Signing(SigningMessage::PartialSignature(PartialSignature { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + request_id: response.request_id.into(), + partial_signature: response.partial_signature.into(), + }))) + } } #[cfg(test)] mod tests { use std::sync::Arc; use std::collections::{BTreeMap, VecDeque}; - use ethkey::{Random, Generator, Public, sign}; + use ethkey::{self, Random, Generator, Public}; use util::H256; use super::super::super::acl_storage::tests::DummyAclStorage; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage}; + use key_server_cluster::{NodeId, SessionId, SessionMeta, Error, KeyStorage}; use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::generation_session::{Session as GenerationSession}; use key_server_cluster::generation_session::tests::MessageLoop as KeyGenerationMessageLoop; @@ -829,17 +597,24 @@ mod tests { pub fn new(gl: &KeyGenerationMessageLoop) -> Self { let mut nodes = BTreeMap::new(); let session_id = gl.session_id.clone(); - for (gl_node_id, gl_node) in &gl.nodes { + let requester = Random.generate().unwrap(); + let signature = Some(ethkey::sign(requester.secret(), &SessionId::default()).unwrap()); + let master_node_id = gl.nodes.keys().nth(0).unwrap().clone(); + for (i, (gl_node_id, gl_node)) in gl.nodes.iter().enumerate() { let acl_storage = Arc::new(DummyAclStorage::default()); let cluster = Arc::new(DummyCluster::new(gl_node_id.clone())); let session = SessionImpl::new(SessionParams { - id: session_id.clone(), + meta: SessionMeta { + id: session_id.clone(), + self_node_id: gl_node_id.clone(), + master_node_id: master_node_id.clone(), + threshold: gl_node.key_storage.get(&session_id).unwrap().threshold, + }, access_key: "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), - self_node_id: gl_node_id.clone(), - encrypted_data: gl_node.key_storage.get(&session_id).unwrap(), + key_share: gl_node.key_storage.get(&session_id).unwrap(), acl_storage: acl_storage, cluster: cluster.clone(), - }).unwrap(); + }, if i == 0 { signature.clone() } else { None }).unwrap(); nodes.insert(gl_node_id.clone(), Node { node_id: gl_node_id.clone(), cluster: cluster, session: session }); } @@ -873,11 +648,11 @@ mod tests { loop { match { match msg.2 { - Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => self.nodes[&msg.1].session.on_consensus_message(msg.0.clone(), &message), - Message::Signing(SigningMessage::SigningGenerationMessage(ref message)) => self.nodes[&msg.1].session.on_generation_message(msg.0.clone(), &message), - Message::Signing(SigningMessage::RequestPartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature_requested(msg.0.clone(), &message), - Message::Signing(SigningMessage::PartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature(msg.0.clone(), &message), - Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(msg.0.clone(), &message), + Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => self.nodes[&msg.1].session.on_consensus_message(&msg.0, &message), + Message::Signing(SigningMessage::SigningGenerationMessage(ref message)) => self.nodes[&msg.1].session.on_generation_message(&msg.0, &message), + Message::Signing(SigningMessage::RequestPartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature_requested(&msg.0, &message), + Message::Signing(SigningMessage::PartialSignature(ref message)) => self.nodes[&msg.1].session.on_partial_signature(&msg.0, &message), + Message::Signing(SigningMessage::SigningSessionCompleted(ref message)) => self.nodes[&msg.1].session.on_session_completed(&msg.0, &message), _ => panic!("unexpected"), } } { @@ -915,11 +690,9 @@ mod tests { } // run signing session - let requestor_pair = Random.generate().unwrap(); - let requestor_signature = sign(&requestor_pair.secret(), &SessionId::default()).unwrap(); let message_hash = H256::from(777); let mut sl = MessageLoop::new(&gl); - sl.master().initialize(requestor_signature, message_hash).unwrap(); + sl.master().initialize(message_hash).unwrap(); while let Some((from, to, message)) = sl.take_message() { sl.process_message((from, to, message)).unwrap(); } From 009d93e7408675b742a650c57aabae5f37d8cf66 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 5 Jun 2017 13:13:38 +0300 Subject: [PATCH 41/45] added license preambles --- .../key_server_cluster/jobs/consensus_session.rs | 16 ++++++++++++++++ .../key_server_cluster/jobs/decryption_job.rs | 16 ++++++++++++++++ .../src/key_server_cluster/jobs/job_session.rs | 16 ++++++++++++++++ .../key_server_cluster/jobs/key_access_job.rs | 16 ++++++++++++++++ secret_store/src/key_server_cluster/jobs/mod.rs | 16 ++++++++++++++++ .../src/key_server_cluster/jobs/signing_job.rs | 16 ++++++++++++++++ 6 files changed, 96 insertions(+) diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs index 765ad67cf6f..a8b39bd9120 100644 --- a/secret_store/src/key_server_cluster/jobs/consensus_session.rs +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::BTreeSet; use std::sync::Arc; use ethkey::{Public, Signature, recover}; diff --git a/secret_store/src/key_server_cluster/jobs/decryption_job.rs b/secret_store/src/key_server_cluster/jobs/decryption_job.rs index 18b28192060..54594c8272f 100644 --- a/secret_store/src/key_server_cluster/jobs/decryption_job.rs +++ b/secret_store/src/key_server_cluster/jobs/decryption_job.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::{BTreeSet, BTreeMap}; use ethkey::{Public, Secret}; use ethcrypto::ecies::encrypt; diff --git a/secret_store/src/key_server_cluster/jobs/job_session.rs b/secret_store/src/key_server_cluster/jobs/job_session.rs index a62004f4a0b..7ae1da42a92 100644 --- a/secret_store/src/key_server_cluster/jobs/job_session.rs +++ b/secret_store/src/key_server_cluster/jobs/job_session.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::{BTreeSet, BTreeMap}; use key_server_cluster::{Error, NodeId, SessionMeta}; diff --git a/secret_store/src/key_server_cluster/jobs/key_access_job.rs b/secret_store/src/key_server_cluster/jobs/key_access_job.rs index f705dcfe7d7..0bbb8bf042a 100644 --- a/secret_store/src/key_server_cluster/jobs/key_access_job.rs +++ b/secret_store/src/key_server_cluster/jobs/key_access_job.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::sync::Arc; use std::collections::{BTreeSet, BTreeMap}; use ethkey::{Signature, recover}; diff --git a/secret_store/src/key_server_cluster/jobs/mod.rs b/secret_store/src/key_server_cluster/jobs/mod.rs index ed3ca38409f..d9a358abab6 100644 --- a/secret_store/src/key_server_cluster/jobs/mod.rs +++ b/secret_store/src/key_server_cluster/jobs/mod.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + pub mod consensus_session; pub mod decryption_job; pub mod job_session; diff --git a/secret_store/src/key_server_cluster/jobs/signing_job.rs b/secret_store/src/key_server_cluster/jobs/signing_job.rs index 7145591a7f7..28ac31a1e57 100644 --- a/secret_store/src/key_server_cluster/jobs/signing_job.rs +++ b/secret_store/src/key_server_cluster/jobs/signing_job.rs @@ -1,3 +1,19 @@ +// Copyright 2015-2017 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity. If not, see . + use std::collections::{BTreeSet, BTreeMap}; use ethkey::{Public, Secret}; use util::H256; From 2912f8e696f4f18550dd5bb51443b0c048c36c5a Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 5 Jun 2017 13:25:49 +0300 Subject: [PATCH 42/45] same_consensus_group_returned_after_second_selection --- .../jobs/consensus_session.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/secret_store/src/key_server_cluster/jobs/consensus_session.rs b/secret_store/src/key_server_cluster/jobs/consensus_session.rs index a8b39bd9120..27542bc446a 100644 --- a/secret_store/src/key_server_cluster/jobs/consensus_session.rs +++ b/secret_store/src/key_server_cluster/jobs/consensus_session.rs @@ -683,6 +683,22 @@ mod tests { assert_eq!(session.state(), ConsensusSessionState::Failed); } + #[test] + fn same_consensus_group_returned_after_second_selection() { + let mut session = make_master_consensus_session(1, None, None); + session.initialize(vec![NodeId::from(1), NodeId::from(2), NodeId::from(3)].into_iter().collect()).unwrap(); + session.on_consensus_message(&NodeId::from(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + session.on_consensus_message(&NodeId::from(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: true, + })).unwrap(); + + let consensus_group1 = session.select_consensus_group().unwrap().clone(); + let consensus_group2 = session.select_consensus_group().unwrap().clone(); + assert_eq!(consensus_group1, consensus_group2); + } + #[test] fn consensus_session_complete_2_of_4() { let mut session = make_master_consensus_session(1, None, None); @@ -737,6 +753,4 @@ mod tests { assert_eq!(session.state(), ConsensusSessionState::Finished); assert_eq!(session.result(), Ok(20)); } - - // TODO: tests for select_consensus_group } From 8dc6b0f7c3fc72b68d3c1c0a9742d2b7071291e6 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Mon, 5 Jun 2017 15:06:01 +0300 Subject: [PATCH 43/45] database upgrade v0 -> v1 --- secret_store/src/key_storage.rs | 147 ++++++++++++++++++++++++-------- 1 file changed, 110 insertions(+), 37 deletions(-) diff --git a/secret_store/src/key_storage.rs b/secret_store/src/key_storage.rs index f150ad7356b..a2bfbca1bec 100644 --- a/secret_store/src/key_storage.rs +++ b/secret_store/src/key_storage.rs @@ -22,6 +22,9 @@ use util::Database; use types::all::{Error, ServiceConfiguration, ServerKeyId, NodeId}; use serialization::{SerializablePublic, SerializableSecret}; +/// Key of version value. +const DB_META_KEY_VERSION: &'static [u8; 7] = b"version"; + #[derive(Debug, Clone, PartialEq)] /// Encrypted key share, stored by key storage on the single key server. pub struct DocumentKeyShare { @@ -57,10 +60,8 @@ pub struct PersistentKeyStorage { } #[derive(Serialize, Deserialize)] -/// Encrypted key share, as it is stored by key storage on the single key server. -struct SerializableDocumentKeyShare { - /// Authore of the entry. - pub author: SerializablePublic, +/// V0 of encrypted key share, as it is stored by key storage on the single key server. +struct SerializableDocumentKeyShareV0 { /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). pub threshold: usize, /// Nodes ids numbers. @@ -73,6 +74,23 @@ struct SerializableDocumentKeyShare { pub encrypted_point: SerializablePublic, } +#[derive(Serialize, Deserialize)] +/// V1 of encrypted key share, as it is stored by key storage on the single key server. +struct SerializableDocumentKeyShareV1 { + /// Authore of the entry. + pub author: SerializablePublic, + /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). + pub threshold: usize, + /// Nodes ids numbers. + pub id_numbers: BTreeMap, + /// Node secret share. + pub secret_share: SerializableSecret, + /// Common (shared) encryption point. + pub common_point: Option, + /// Encrypted point. + pub encrypted_point: Option, +} + impl PersistentKeyStorage { /// Create new persistent document encryption keys storage pub fn new(config: &ServiceConfiguration) -> Result { @@ -80,15 +98,48 @@ impl PersistentKeyStorage { db_path.push("db"); let db_path = db_path.to_str().ok_or(Error::Database("Invalid secretstore path".to_owned()))?; + let db = Database::open_default(&db_path).map_err(Error::Database)?; + let db = upgrade_db(db)?; + Ok(PersistentKeyStorage { - db: Database::open_default(&db_path).map_err(Error::Database)?, + db: db, }) } } +fn upgrade_db(db: Database) -> Result { + let version = db.get(None, DB_META_KEY_VERSION).map_err(Error::Database)?; + let version = version.and_then(|v| v.get(0).cloned()).unwrap_or(0); + match version { + 0 => { + let mut batch = db.transaction(); + batch.put(None, DB_META_KEY_VERSION, &[1]); + for (db_key, db_value) in db.iter(None).into_iter().flat_map(|inner| inner) { + let v0_key = serde_json::from_slice::(&db_value).map_err(|e| Error::Database(e.to_string()))?; + let v1_key = SerializableDocumentKeyShareV1 { + // author is used in separate generation + encrypt sessions. + // in v0 there have been only simultaneous GenEnc sessions. + author: Public::default().into(), + threshold: v0_key.threshold, + id_numbers: v0_key.id_numbers, + secret_share: v0_key.secret_share, + common_point: Some(v0_key.common_point), + encrypted_point: Some(v0_key.encrypted_point), + }; + let db_value = serde_json::to_vec(&v1_key).map_err(|e| Error::Database(e.to_string()))?; + batch.put(None, &*db_key, &*db_value); + } + db.write(batch).map_err(Error::Database)?; + Ok(db) + }, + 1 => Ok(db), + _ => Err(Error::Database(format!("unsupported SecretStore database version:? {}", version))), + } +} + impl KeyStorage for PersistentKeyStorage { fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - let key: SerializableDocumentKeyShare = key.into(); + let key: SerializableDocumentKeyShareV1 = key.into(); let key = serde_json::to_vec(&key).map_err(|e| Error::Database(e.to_string()))?; let mut batch = self.db.transaction(); batch.put(None, &document, &key); @@ -104,7 +155,7 @@ impl KeyStorage for PersistentKeyStorage { .map_err(Error::Database)? .ok_or(Error::DocumentNotFound) .map(|key| key.to_vec()) - .and_then(|key| serde_json::from_slice::(&key).map_err(|e| Error::Database(e.to_string()))) + .and_then(|key| serde_json::from_slice::(&key).map_err(|e| Error::Database(e.to_string()))) .map(Into::into) } @@ -115,48 +166,28 @@ impl KeyStorage for PersistentKeyStorage { } } -impl From for SerializableDocumentKeyShare { +impl From for SerializableDocumentKeyShareV1 { fn from(key: DocumentKeyShare) -> Self { - SerializableDocumentKeyShare { + SerializableDocumentKeyShareV1 { author: key.author.into(), threshold: key.threshold, id_numbers: key.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), secret_share: key.secret_share.into(), - common_point: match key.common_point { - Some(common_point) => common_point.into(), - None => Public::default().into(), - }, - encrypted_point: match key.encrypted_point { - Some(encrypted_point) => encrypted_point.into(), - None => Public::default().into(), - }, + common_point: key.common_point.map(Into::into), + encrypted_point: key.encrypted_point.map(Into::into), } } } -impl From for DocumentKeyShare { - fn from(key: SerializableDocumentKeyShare) -> Self { +impl From for DocumentKeyShare { + fn from(key: SerializableDocumentKeyShareV1) -> Self { DocumentKeyShare { author: key.author.into(), threshold: key.threshold, id_numbers: key.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), secret_share: key.secret_share.into(), - common_point: { - let common_point = key.common_point.into(); - if common_point == Public::default() { - None - } else { - Some(common_point) - } - }, - encrypted_point: { - let encrypted_point = key.encrypted_point.into(); - if encrypted_point == Public::default() { - None - } else { - Some(encrypted_point) - } - }, + common_point: key.common_point.map(Into::into), + encrypted_point: key.encrypted_point.map(Into::into), } } } @@ -165,10 +196,13 @@ impl From for DocumentKeyShare { pub mod tests { use std::collections::{BTreeMap, HashMap}; use parking_lot::RwLock; + use serde_json; use devtools::RandomTempPath; - use ethkey::{Random, Generator, Public}; + use ethkey::{Random, Generator, Public, Secret}; + use util::Database; use super::super::types::all::{Error, NodeAddress, ServiceConfiguration, ClusterConfiguration, ServerKeyId}; - use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare}; + use super::{DB_META_KEY_VERSION, KeyStorage, PersistentKeyStorage, DocumentKeyShare, + SerializableDocumentKeyShareV0, SerializableDocumentKeyShareV1, upgrade_db}; #[derive(Default)] /// In-memory document encryption keys storage @@ -254,4 +288,43 @@ pub mod tests { assert_eq!(key_storage.get(&key2), Ok(value2)); assert_eq!(key_storage.get(&key3), Err(Error::DocumentNotFound)); } + + #[test] + fn upgrade_db_0_to_1() { + let db_path = RandomTempPath::create_dir(); + let db = Database::open_default(db_path.as_str()).unwrap(); + + // prepare v0 database + { + let key = serde_json::to_vec(&SerializableDocumentKeyShareV0 { + threshold: 777, + id_numbers: vec![( + "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".into(), + "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap().into(), + )].into_iter().collect(), + secret_share: "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap().into(), + common_point: "99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".into(), + encrypted_point: "7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".into(), + }).unwrap(); + let mut batch = db.transaction(); + batch.put(None, &[7], &key); + db.write(batch).unwrap(); + } + + // upgrade database + let db = upgrade_db(db).unwrap(); + + // check upgrade + assert_eq!(db.get(None, DB_META_KEY_VERSION).unwrap().unwrap()[0], 1); + let key = serde_json::from_slice::(&db.get(None, &[7]).unwrap().map(|key| key.to_vec()).unwrap()).unwrap(); + assert_eq!(Public::default(), key.author.clone().into()); + assert_eq!(777, key.threshold); + assert_eq!(vec![( + "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse::().unwrap(), + "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse::().unwrap(), + )], key.id_numbers.clone().into_iter().map(|(k, v)| (k.into(), v.into())).collect::>()); + assert_eq!("00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse::().unwrap(), key.secret_share.into()); + assert_eq!(Some("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc".parse::().unwrap()), key.common_point.clone().map(Into::into)); + assert_eq!(Some("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c".parse::().unwrap()), key.encrypted_point.clone().map(Into::into)); + } } From 2ba595ef5fb1b51212ad7cfc3299f8ccffd296c1 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Wed, 21 Jun 2017 07:23:49 +0300 Subject: [PATCH 44/45] typo --- secret_store/src/http_listener.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/secret_store/src/http_listener.rs b/secret_store/src/http_listener.rs index 8ffdd628cef..1f7f14edeee 100644 --- a/secret_store/src/http_listener.rs +++ b/secret_store/src/http_listener.rs @@ -145,7 +145,7 @@ impl HttpHandler for KeyServerHttpHandler where T: KeyServer + 'static { match &req_uri { &RequestUri::AbsolutePath(ref path) => match parse_request(&req_method, &path) { Request::GenerateServerKey(document, signature, threshold) => { - return_server_pubic_key(req, res, self.handler.key_server.generate_key(&document, &signature, threshold) + return_server_public_key(req, res, self.handler.key_server.generate_key(&document, &signature, threshold) .map_err(|err| { warn!(target: "secretstore", "GenerateServerKey request {} has failed with: {}", req_uri, err); err @@ -203,7 +203,7 @@ fn return_empty(req: HttpRequest, res: HttpResponse, empty: Result<(), Error>) { return_bytes::(req, res, empty.map(|_| None)) } -fn return_server_pubic_key(req: HttpRequest, res: HttpResponse, server_public: Result) { +fn return_server_public_key(req: HttpRequest, res: HttpResponse, server_public: Result) { return_bytes(req, res, server_public.map(|k| Some(SerializablePublic(k)))) } From b3392afcb350cf61690a5b58ec12a761e711ae47 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Tue, 27 Jun 2017 19:27:24 +0300 Subject: [PATCH 45/45] fixed grumbles --- secret_store/src/key_server.rs | 6 +-- .../key_server_cluster/cluster_sessions.rs | 45 +++++++------------ 2 files changed, 20 insertions(+), 31 deletions(-) diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 3f502ef9da5..fd4e154fa27 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -131,9 +131,9 @@ impl MessageSigner for KeyServerImpl { let message_signature = signing_session.wait()?; // compose two message signature components into single one - let mut combined_signature: Vec = Vec::with_capacity(64); - combined_signature.extend_from_slice(&**message_signature.0); - combined_signature.extend_from_slice(&**message_signature.1); + let mut combined_signature = [0; 64]; + combined_signature[..32].clone_from_slice(&**message_signature.0); + combined_signature[32..].clone_from_slice(&**message_signature.1); // encrypt combined signature with requestor public key let message_signature = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &combined_signature) diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index 8b07006dd14..f66ad972ff7 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -20,7 +20,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::collections::{VecDeque, BTreeSet, BTreeMap}; use parking_lot::RwLock; use ethkey::{Public, Secret, Signature}; -use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, EncryptedDocumentKeyShadow, SessionMeta}; +use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, DocumentKeyShare, EncryptedDocumentKeyShadow, SessionMeta}; use key_server_cluster::cluster::{Cluster, ClusterData, ClusterView, ClusterConfiguration}; use key_server_cluster::message::{self, Message, GenerationMessage, EncryptionMessage, DecryptionMessage, SigningMessage}; use key_server_cluster::generation_session::{Session as GenerationSession, SessionImpl as GenerationSessionImpl, @@ -193,15 +193,7 @@ impl ClusterSessions { /// Create new encryption session. pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc) -> Result, Error> { - // some of nodes, which were generating the key may be down - // => do not use these in encryption session - let mut encrypted_data = self.key_storage.get(&session_id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } - + let encrypted_data = self.read_key_share(&session_id, &cluster)?; self.encryption_sessions.insert(master, session_id, cluster.clone(), move || EncryptionSessionImpl::new(EncryptionSessionParams { id: session_id.clone(), self_node_id: self.self_node_id.clone(), @@ -226,15 +218,7 @@ impl ClusterSessions { /// Create new decryption session. pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc, requester_signature: Option) -> Result, Error> { let session_id = DecryptionSessionId::new(session_id, sub_session_id); - - // some of nodes, which were encrypting secret may be down - // => do not use these in decryption session - let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } + let encrypted_data = self.read_key_share(&session_id.id, &cluster)?; self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), move || DecryptionSessionImpl::new(DecryptionSessionParams { meta: SessionMeta { @@ -271,15 +255,7 @@ impl ClusterSessions { /// Create new signing session. pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc, requester_signature: Option) -> Result, Error> { let session_id = SigningSessionId::new(session_id, sub_session_id); - - // some of nodes, which were encrypting secret may be down - // => do not use these in signing session - let mut encrypted_data = self.key_storage.get(&session_id.id).map_err(|e| Error::KeyStorage(e.into()))?; - let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); - let disconnected_nodes: BTreeSet<_> = disconnected_nodes.difference(&cluster.nodes()).cloned().collect(); - for disconnected_node in disconnected_nodes { - encrypted_data.id_numbers.remove(&disconnected_node); - } + let encrypted_data = self.read_key_share(&session_id.id, &cluster)?; self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), move || SigningSessionImpl::new(SigningSessionParams { meta: SessionMeta { @@ -328,6 +304,19 @@ impl ClusterSessions { self.decryption_sessions.on_connection_timeout(node_id); self.signing_sessions.on_connection_timeout(node_id); } + + /// Read key share && remove disconnected nodes. + fn read_key_share(&self, key_id: &SessionId, cluster: &Arc) -> Result { + let mut encrypted_data = self.key_storage.get(key_id).map_err(|e| Error::KeyStorage(e.into()))?; + + // some of nodes, which were encrypting secret may be down + // => do not use these in session + let disconnected_nodes: BTreeSet<_> = encrypted_data.id_numbers.keys().cloned().collect(); + for disconnected_node in disconnected_nodes.difference(&cluster.nodes()) { + encrypted_data.id_numbers.remove(&disconnected_node); + } + Ok(encrypted_data) + } } impl ClusterSessionsContainer where K: Clone + Ord, V: ClusterSession {