From 0da6c7eb4505e77f2fc94dc4b2f439779435a1a9 Mon Sep 17 00:00:00 2001 From: Svyatoslav Nikolsky Date: Thu, 1 Mar 2018 11:59:21 +0300 Subject: [PATCH] SecretStore: ECDSA session for cases when 2*t < N (#7739) * SecretStore: ECDSA PoC * SecretStore: fixed ECDSA serialization + cleanup * removed unused param * removed unused method * removed debug unwrap * SecretStore: 'limited' ECDSA session * fix after merge --- secret_store/src/key_server.rs | 64 +- .../key_version_negotiation_session.rs | 9 +- .../servers_set_change_session.rs | 2 +- .../client_sessions/generation_session.rs | 156 +- .../key_server_cluster/client_sessions/mod.rs | 3 +- .../client_sessions/signing_session_ecdsa.rs | 1302 +++++++++++++++++ ..._session.rs => signing_session_schnorr.rs} | 148 +- .../src/key_server_cluster/cluster.rs | 145 +- .../key_server_cluster/cluster_sessions.rs | 25 +- .../cluster_sessions_creator.rs | 78 +- .../src/key_server_cluster/io/message.rs | 71 +- .../src/key_server_cluster/jobs/mod.rs | 3 +- .../jobs/signing_job_ecdsa.rs | 151 ++ ...{signing_job.rs => signing_job_schnorr.rs} | 38 +- secret_store/src/key_server_cluster/math.rs | 46 +- .../src/key_server_cluster/message.rs | 390 ++++- secret_store/src/key_server_cluster/mod.rs | 3 +- secret_store/src/listener/http_listener.rs | 57 +- secret_store/src/listener/mod.rs | 8 +- secret_store/src/traits.rs | 11 +- 20 files changed, 2381 insertions(+), 329 deletions(-) create mode 100644 secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs rename secret_store/src/key_server_cluster/client_sessions/{signing_session.rs => signing_session_schnorr.rs} (86%) create mode 100644 secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs rename secret_store/src/key_server_cluster/jobs/{signing_job.rs => signing_job_schnorr.rs} (81%) diff --git a/secret_store/src/key_server.rs b/secret_store/src/key_server.rs index 32d1da79f..bafcea3e7 100644 --- a/secret_store/src/key_server.rs +++ b/secret_store/src/key_server.rs @@ -132,13 +132,13 @@ impl DocumentKeyServer for KeyServerImpl { } impl MessageSigner for KeyServerImpl { - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + fn sign_message_schnorr(&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(), None, message)?; + let signing_session = self.data.lock().cluster.new_schnorr_signing_session(key_id.clone(), signature.clone(), None, message)?; let message_signature = signing_session.wait()?; // compose two message signature components into single one @@ -151,6 +151,21 @@ impl MessageSigner for KeyServerImpl { .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?; Ok(message_signature) } + + fn sign_message_ecdsa(&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_ecdsa_signing_session(key_id.clone(), signature.clone(), None, message)?; + let message_signature = signing_session.wait()?; + + // encrypt combined signature with requestor public key + let message_signature = ethcrypto::ecies::encrypt(&public, ðcrypto::DEFAULT_MAC, &*message_signature) + .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))?; + Ok(message_signature) + } } impl KeyServerCore { @@ -209,13 +224,13 @@ pub mod tests { use std::net::SocketAddr; use std::collections::BTreeMap; use ethcrypto; - use ethkey::{self, Secret, Random, Generator}; + use ethkey::{self, Secret, Random, Generator, verify_public}; use acl_storage::DummyAclStorage; use key_storage::tests::DummyKeyStorage; use node_key_pair::PlainNodeKeyPair; use key_server_set::tests::MapKeyServerSet; use key_server_cluster::math; - use ethereum_types::H256; + use ethereum_types::{H256, H520}; use types::all::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature, NodeId}; use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; @@ -260,7 +275,11 @@ pub mod tests { } impl MessageSigner for DummyKeyServer { - fn sign_message(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { + fn sign_message_schnorr(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { + unimplemented!("test-only") + } + + fn sign_message_ecdsa(&self, _key_id: &ServerKeyId, _signature: &RequestSignature, _message: MessageHash) -> Result { unimplemented!("test-only") } } @@ -411,13 +430,13 @@ 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 = key_servers[0].sign_message_schnorr(&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..]); // check signature - assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); + assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); } } @@ -444,7 +463,7 @@ pub mod tests { } #[test] - fn signing_session_is_delegated_when_node_does_not_have_key_share() { + fn schnorr_signing_session_is_delegated_when_node_does_not_have_key_share() { //::logger::init_log(); let key_servers = make_key_servers(6114, 3); let threshold = 1; @@ -460,13 +479,38 @@ 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 = key_servers[0].sign_message_schnorr(&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..]); // check signature - assert_eq!(math::verify_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); + assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); + } + + #[test] + fn ecdsa_signing_session_is_delegated_when_node_does_not_have_key_share() { + //::logger::init_log(); + let key_servers = make_key_servers(6117, 4); + let threshold = 1; + + // 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(); + + // remove key from node0 + key_servers[0].cluster().key_storage().remove(&server_key_id).unwrap(); + + // sign message + let message_hash = H256::random(); + let signature = key_servers[0].sign_message_ecdsa(&server_key_id, &signature, message_hash.clone()).unwrap(); + let signature = ethcrypto::ecies::decrypt(&requestor_secret, ðcrypto::DEFAULT_MAC, &signature).unwrap(); + let signature: H520 = signature[0..65].into(); + + // check signature + assert!(verify_public(&server_public, &signature.into(), &message_hash).unwrap()); } #[test] diff --git a/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs b/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs index 1fb3a3fe6..b7291c25c 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs @@ -23,7 +23,8 @@ use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare}; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; -use key_server_cluster::signing_session::SessionImpl as SigningSession; +use key_server_cluster::signing_session_ecdsa::SessionImpl as EcdsaSigningSession; +use key_server_cluster::signing_session_schnorr::SessionImpl as SchnorrSigningSession; use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, KeyVersions}; use key_server_cluster::admin_sessions::ShareChangeSessionMeta; @@ -56,8 +57,10 @@ pub struct SessionImpl { pub enum ContinueAction { /// Decryption session + is_shadow_decryption. Decrypt(Arc, bool), - /// Signing session + message hash. - Sign(Arc, H256), + /// Schnorr signing session + message hash. + SchnorrSign(Arc, H256), + /// ECDSA signing session + message hash. + EcdsaSign(Arc, H256), } /// Immutable session data. diff --git a/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs b/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs index 271649307..916b67217 100644 --- a/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs +++ b/secret_store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs @@ -1166,7 +1166,7 @@ pub mod tests { pub fn generate_key(threshold: usize, nodes_ids: BTreeSet) -> GenerationMessageLoop { let mut gml = GenerationMessageLoop::with_nodes_ids(nodes_ids); - gml.master().initialize(Public::default(), threshold, gml.nodes.keys().cloned().collect()).unwrap(); + gml.master().initialize(Public::default(), false, threshold, gml.nodes.keys().cloned().collect::>().into()).unwrap(); while let Some((from, to, message)) = gml.take_message() { gml.process_message((from, to, message)).unwrap(); } diff --git a/secret_store/src/key_server_cluster/client_sessions/generation_session.rs b/secret_store/src/key_server_cluster/client_sessions/generation_session.rs index 18e831ef0..a8b9501e2 100644 --- a/secret_store/src/key_server_cluster/client_sessions/generation_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/generation_session.rs @@ -81,6 +81,8 @@ struct SessionData { author: Option, // === Values, filled when session initialization is completed === + /// Is zero secret generation session? + is_zero: Option, /// 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, @@ -103,7 +105,7 @@ struct SessionData { /// Key share. key_share: Option>, /// Jointly generated public key, which can be used to encrypt secret. Public. - joint_public_and_secret: Option>, + joint_public_and_secret: Option>, } /// Mutable node-specific data. @@ -171,6 +173,32 @@ pub enum SessionState { Failed, } +pub enum InitializationNodes { + RandomNumbers(BTreeSet), + SpecificNumbers(BTreeMap) +} + +impl InitializationNodes { + pub fn set(&self) -> BTreeSet { + match *self { + InitializationNodes::RandomNumbers(ref nodes) => nodes.clone(), + InitializationNodes::SpecificNumbers(ref nodes) => nodes.keys().cloned().collect(), + } + } +} + +impl From> for InitializationNodes { + fn from(nodes: BTreeSet) -> Self { + InitializationNodes::RandomNumbers(nodes) + } +} + +impl From> for InitializationNodes { + fn from(nodes: BTreeMap) -> Self { + InitializationNodes::SpecificNumbers(nodes) + } +} + impl SessionImpl { /// Create new generation session. pub fn new(params: SessionParams) -> Self { @@ -188,6 +216,7 @@ impl SessionImpl { simulate_faulty_behaviour: false, master: None, author: None, + is_zero: None, threshold: None, derived_point: None, nodes: BTreeMap::new(), @@ -228,14 +257,14 @@ impl SessionImpl { } /// Get generated public and secret (if any). - pub fn joint_public_and_secret(&self) -> Option> { + pub fn joint_public_and_secret(&self) -> Option> { self.data.lock().joint_public_and_secret.clone() } /// 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)?; + pub fn initialize(&self, author: Public, is_zero: bool, threshold: usize, nodes: InitializationNodes) -> Result<(), Error> { + check_cluster_nodes(self.node(), &nodes.set())?; + check_threshold(threshold, &nodes.set())?; let mut data = self.data.lock(); @@ -247,11 +276,21 @@ impl SessionImpl { // update state data.master = Some(self.node().clone()); data.author = Some(author.clone()); + data.is_zero = Some(is_zero); 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)); + match nodes { + InitializationNodes::RandomNumbers(nodes) => { + for node_id in nodes { + // generate node identification parameter + let node_id_number = math::generate_random_scalar()?; + data.nodes.insert(node_id, NodeData::with_id_number(node_id_number)); + } + }, + InitializationNodes::SpecificNumbers(nodes) => { + for (node_id, node_id_number) in nodes { + data.nodes.insert(node_id, NodeData::with_id_number(node_id_number)); + } + }, } let mut visit_policy = EveryOtherNodeVisitor::new(self.node(), data.nodes.keys().cloned()); @@ -266,6 +305,7 @@ impl SessionImpl { session_nonce: self.nonce, author: author.into(), nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), + is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), derived_point: derived_point.into(), }))) @@ -339,6 +379,7 @@ impl SessionImpl { 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.is_zero = Some(message.is_zero); data.threshold = Some(message.threshold); Ok(()) @@ -371,6 +412,7 @@ impl SessionImpl { session_nonce: self.nonce, 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(), + is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), derived_point: message.derived_point.clone().into(), }))); @@ -427,8 +469,9 @@ impl SessionImpl { debug_assert!(data.nodes.contains_key(&sender)); // check message + let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"); let threshold = data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"); - if message.publics.len() != threshold + 1 { + if !is_zero && message.publics.len() != threshold + 1 { return Err(Error::InvalidMessage); } @@ -509,9 +552,12 @@ impl SessionImpl { } // calculate joint public key - let joint_public = { + let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); + let joint_public = if !is_zero { 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)? + } else { + Default::default() }; // save encrypted data to key storage @@ -585,16 +631,23 @@ impl SessionImpl { // 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 is_zero = data.is_zero.expect("is_zero is filled on initialization phase; KD phase follows initialization phase; qed"); + let mut polynom1 = math::generate_random_polynom(threshold)?; + if is_zero { + polynom1[0] = math::zero_scalar(); + } let polynom2 = math::generate_random_polynom(threshold)?; data.polynom1 = Some(polynom1.clone()); 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)?; + let publics = match is_zero { + false => math::public_values_generation(threshold, + data.derived_point.as_ref().expect("keys dissemination occurs after derived point is agreed; qed"), + &polynom1, + &polynom2)?, + true => Default::default(), + }; // compute secret values for every other node for (node, node_data) in data.nodes.iter_mut() { @@ -629,25 +682,35 @@ impl SessionImpl { // 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 is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KV phase follows initialization phase; qed"); 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)? + if !is_zero { + 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)? + }; + + self_public_share + } else { + // TODO [Trust]: add verification when available + Default::default() + } }; // calculate self secret + public shares @@ -676,12 +739,16 @@ impl SessionImpl { let mut data = self.data.lock(); // calculate joint public key - let joint_public = { + let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); + let joint_public = if !is_zero { 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)? + } else { + Default::default() }; // prepare key data + let 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(); 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"), @@ -690,7 +757,7 @@ impl SessionImpl { encrypted_point: None, versions: vec![DocumentKeyShareVersion::new( data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), + secret_share.clone(), )], }; @@ -698,7 +765,7 @@ impl SessionImpl { 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_and_secret = Some(Ok((joint_public, secret_coeff))); + data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff, secret_share))); data.state = SessionState::WaitingForGenerationConfirmation; return Ok(()); } @@ -721,7 +788,7 @@ impl SessionImpl { self_node.completion_confirmed = true; } data.key_share = Some(Ok(encrypted_data)); - data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff))); + data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff, secret_share))); data.state = SessionState::WaitingForGenerationConfirmation; Ok(()) @@ -996,7 +1063,7 @@ pub mod tests { 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())?; + l.master().initialize(Public::default(), false, threshold, l.nodes.keys().cloned().collect::>().into())?; let session_id = l.session_id.clone(); let master_id = l.master().node().clone(); @@ -1007,7 +1074,7 @@ pub mod tests { #[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()); + assert!(l.master().initialize(Public::default(), false, 0, l.nodes.keys().cloned().collect::>().into()).is_ok()); } #[test] @@ -1022,7 +1089,7 @@ pub mod tests { nonce: Some(0), }); 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); + assert_eq!(session.initialize(Public::default(), false, 0, cluster_nodes.into()).unwrap_err(), Error::InvalidNodesConfiguration); } #[test] @@ -1036,7 +1103,8 @@ pub mod tests { #[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); + assert_eq!(l.master().initialize(Public::default(), false, 0, l.nodes.keys().cloned().collect::>().into()).unwrap_err(), + Error::InvalidStateForRequest); } #[test] @@ -1117,6 +1185,7 @@ pub mod tests { session_nonce: 0, author: Public::default().into(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + is_zero: false, threshold: 0, derived_point: math::generate_random_point().unwrap().into(), }).unwrap_err(), Error::InvalidNodesConfiguration); @@ -1133,6 +1202,7 @@ pub mod tests { session_nonce: 0, author: Public::default().into(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), + is_zero: false, threshold: 2, derived_point: math::generate_random_point().unwrap().into(), }).unwrap_err(), Error::InvalidThreshold); @@ -1273,7 +1343,7 @@ pub mod tests { 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(); + l.master().initialize(Public::default(), false, threshold, l.nodes.keys().cloned().collect::>().into()).unwrap(); assert_eq!(l.nodes.len(), num_nodes); // let nodes do initialization + keys dissemination diff --git a/secret_store/src/key_server_cluster/client_sessions/mod.rs b/secret_store/src/key_server_cluster/client_sessions/mod.rs index 0ee5f5841..ba2fbd535 100644 --- a/secret_store/src/key_server_cluster/client_sessions/mod.rs +++ b/secret_store/src/key_server_cluster/client_sessions/mod.rs @@ -17,4 +17,5 @@ pub mod decryption_session; pub mod encryption_session; pub mod generation_session; -pub mod signing_session; +pub mod signing_session_ecdsa; +pub mod signing_session_schnorr; diff --git a/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs b/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs new file mode 100644 index 000000000..9bb063840 --- /dev/null +++ b/secret_store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs @@ -0,0 +1,1302 @@ +// 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 std::collections::btree_map::Entry; +use std::sync::Arc; +use parking_lot::{Mutex, Condvar}; +use ethkey::{Public, Secret, Signature, sign}; +use ethereum_types::H256; +use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, AclStorage, DocumentKeyShare}; +use key_server_cluster::cluster::{Cluster}; +use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; +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, EcdsaSigningMessage, EcdsaSigningConsensusMessage, EcdsaSignatureNonceGenerationMessage, + EcdsaInversionNonceGenerationMessage, EcdsaInversionZeroGenerationMessage, EcdsaSigningInversedNonceCoeffShare, + EcdsaRequestPartialSignature, EcdsaPartialSignature, EcdsaSigningSessionCompleted, GenerationMessage, + ConsensusMessage, EcdsaSigningSessionError, InitializeConsensusSession, ConfirmConsensusInitialization, + EcdsaSigningSessionDelegation, EcdsaSigningSessionDelegationCompleted}; +use key_server_cluster::jobs::job_session::JobTransport; +use key_server_cluster::jobs::key_access_job::KeyAccessJob; +use key_server_cluster::jobs::signing_job_ecdsa::{EcdsaPartialSigningRequest, EcdsaPartialSigningResponse, EcdsaSigningJob}; +use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; + +/// Distributed ECDSA-signing session. +/// Based on "A robust threshold elliptic curve digital signature providing a new verifiable secret sharing scheme" paper. +/// WARNING: can only be used if 2*t < N is true for key generation scheme +pub struct SessionImpl { + /// Session core. + core: SessionCore, + /// Session data. + data: Mutex, +} + +/// Immutable session data. +struct SessionCore { + /// Session metadata. + pub meta: SessionMeta, + /// Signing session access key. + pub access_key: Secret, + /// Key share. + pub key_share: Option, + /// Cluster which allows this node to send messages to other nodes in the cluster. + pub cluster: Arc, + /// Session-level nonce. + pub nonce: u64, + /// SessionImpl completion condvar. + 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, + /// Key version to use for decryption. + pub version: Option, + /// Consensus-based signing session. + pub consensus_session: SigningConsensusSession, + /// Signature nonce generation session. + pub sig_nonce_generation_session: Option, + /// Inversion nonce generation session. + pub inv_nonce_generation_session: Option, + /// Inversion zero generation session. + pub inv_zero_generation_session: Option, + /// Inversed nonce coefficient shares. + pub inversed_nonce_coeff_shares: Option>, + /// Delegation status. + pub delegation_status: Option, + /// Decryption result. + pub result: Option>, +} + +/// Signing session state. +#[derive(Debug, PartialEq)] +pub enum SessionState { + /// Consensus is establishing. + ConsensusEstablishing, + /// Nonces (signature, inversion && zero) are generating. + NoncesGenerating, + /// Waiting for inversed nonce shares. + WaitingForInversedNonceShares, + /// State when signature is computing. + SignatureComputing, +} + +/// Session creation parameters +pub struct SessionParams { + /// Session metadata. + pub meta: SessionMeta, + /// Session access key. + pub access_key: Secret, + /// Key share. + pub key_share: Option, + /// ACL storage. + pub acl_storage: Arc, + /// Cluster + pub cluster: Arc, + /// Session nonce. + pub nonce: u64, +} + +/// Signing consensus transport. +struct SigningConsensusTransport { + /// Session id. + id: SessionId, + /// Session access key. + access_key: Secret, + /// Session-level nonce. + nonce: u64, + /// Selected key version (on master node). + version: Option, + /// Cluster. + cluster: Arc, +} + +/// Signing key generation transport. +struct NonceGenerationTransport EcdsaSigningMessage + Send + Sync> { + /// Session id. + id: SessionId, + /// Session access key. + access_key: Secret, + /// Session-level nonce. + nonce: u64, + /// Cluster. + cluster: Arc, + /// Other nodes ids. + other_nodes_ids: BTreeSet, + /// Message mapping function. + map: F, +} + +/// Signing job transport +struct SigningJobTransport { + /// Session id. + id: SessionId, + /// Session access key. + access_key: Secret, + /// Session-level nonce. + nonce: u64, + /// Cluster. + cluster: Arc, +} + +/// Session delegation status. +enum DelegationStatus { + /// Delegated to other node. + DelegatedTo(NodeId), + /// Delegated from other node. + DelegatedFrom(NodeId, u64), +} + +impl SessionImpl { + /// Create new signing session. + pub fn new(params: SessionParams, requester_signature: Option) -> Result { + debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); + + let consensus_transport = SigningConsensusTransport { + id: params.meta.id.clone(), + access_key: params.access_key.clone(), + nonce: params.nonce, + version: None, + cluster: params.cluster.clone(), + }; + let consensus_session = ConsensusSession::new(ConsensusSessionParams { + // this session requires responses from 2 * t nodes + meta: SessionMeta { + id: params.meta.id, + master_node_id: params.meta.master_node_id, + self_node_id: params.meta.self_node_id, + threshold: params.meta.threshold * 2, + }, + consensus_executor: match requester_signature { + Some(requester_signature) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester_signature), + None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), + }, + consensus_transport: consensus_transport, + })?; + + Ok(SessionImpl { + core: SessionCore { + meta: params.meta, + access_key: params.access_key, + key_share: params.key_share, + cluster: params.cluster, + nonce: params.nonce, + completed: Condvar::new(), + }, + data: Mutex::new(SessionData { + state: SessionState::ConsensusEstablishing, + message_hash: None, + version: None, + consensus_session: consensus_session, + sig_nonce_generation_session: None, + inv_nonce_generation_session: None, + inv_zero_generation_session: None, + inversed_nonce_coeff_shares: None, + delegation_status: None, + result: None, + }), + }) + } + + /// Wait for session completion. + pub fn wait(&self) -> Result { + Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) + } + + /// Delegate session to other node. + pub fn delegate(&self, master: NodeId, version: H256, message_hash: H256) -> Result<(), Error> { + if self.core.meta.master_node_id != self.core.meta.self_node_id { + return Err(Error::InvalidStateForRequest); + } + + let mut data = self.data.lock(); + if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { + return Err(Error::InvalidStateForRequest); + } + + data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); + self.core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(EcdsaSigningSessionDelegation { + session: self.core.meta.id.clone().into(), + sub_session: self.core.access_key.clone().into(), + session_nonce: self.core.nonce, + requestor_signature: data.consensus_session.consensus_job().executor().requester_signature() + .expect("signature is passed to master node on creation; session can be delegated from master node only; qed") + .clone().into(), + version: version.into(), + message_hash: message_hash.into(), + })))?; + data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); + Ok(()) + + } + + /// Initialize signing session on master node. + pub fn initialize(&self, version: H256, message_hash: H256) -> Result<(), Error> { + debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); + + // check if version exists + let key_version = match self.core.key_share.as_ref() { + None => return Err(Error::InvalidMessage), + Some(key_share) => key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?, + }; + + // select nodes to participate in consensus etablish session + let mut data = self.data.lock(); + let non_isolated_nodes = self.core.cluster.nodes(); + let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() + .filter(|n| non_isolated_nodes.contains(*n)) + .cloned() + .chain(::std::iter::once(self.core.meta.self_node_id.clone())) + .collect(); + if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { + consensus_nodes.remove(&delegation_master); + } + + // start consensus establish sesssion + data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); + data.version = Some(version.clone()); + data.message_hash = Some(message_hash); + data.consensus_session.initialize(consensus_nodes)?; + + // consensus established => threshold is 0 => we can generate signature on this node + if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { + data.result = Some(sign(&key_version.secret_share, &message_hash).map_err(Into::into)); + self.core.completed.notify_all(); + } + + Ok(()) + } + + /// Process signing message. + pub fn process_message(&self, sender: &NodeId, message: &EcdsaSigningMessage) -> Result<(), Error> { + if self.core.nonce != message.session_nonce() { + return Err(Error::ReplayProtection); + } + + match message { + &EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message) => + self.on_consensus_message(sender, message), + &EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref message) => + self.on_signature_nonce_generation_message(sender, message), + &EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref message) => + self.on_inversion_nonce_generation_message(sender, message), + &EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref message) => + self.on_inversion_zero_generation_message(sender, message), + &EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref message) => + self.on_inversed_nonce_coeff_share(sender, message), + &EcdsaSigningMessage::EcdsaRequestPartialSignature(ref message) => + self.on_partial_signature_requested(sender, message), + &EcdsaSigningMessage::EcdsaPartialSignature(ref message) => + self.on_partial_signature(sender, message), + &EcdsaSigningMessage::EcdsaSigningSessionError(ref message) => + self.process_node_error(Some(&sender), Error::Io(message.error.clone())), + &EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref message) => + self.on_session_completed(sender, message), + &EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message) => + self.on_session_delegated(sender, message), + &EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref message) => + self.on_session_delegation_completed(sender, message), + } + } + + /// When session is delegated to this node. + pub fn on_session_delegated(&self, sender: &NodeId, message: &EcdsaSigningSessionDelegation) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + + { + let mut data = self.data.lock(); + if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { + return Err(Error::InvalidStateForRequest); + } + + data.consensus_session.consensus_job_mut().executor_mut().set_requester_signature(message.requestor_signature.clone().into()); + data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); + } + + self.initialize(message.version.clone().into(), message.message_hash.clone().into()) + } + + /// When delegated session is completed on other node. + pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &EcdsaSigningSessionDelegationCompleted) -> Result<(), Error> { + debug_assert!(self.core.meta.id == *message.session); + debug_assert!(self.core.access_key == *message.sub_session); + + if self.core.meta.master_node_id != self.core.meta.self_node_id { + return Err(Error::InvalidStateForRequest); + } + + let mut data = self.data.lock(); + match data.delegation_status.as_ref() { + Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), + _ => return Err(Error::InvalidMessage), + } + + Self::set_signing_result(&self.core, &mut *data, Ok(message.signature.clone().into())); + + Ok(()) + } + + /// When consensus-related message is received. + pub fn on_consensus_message(&self, sender: &NodeId, message: &EcdsaSigningConsensusMessage) -> 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; + + if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { + let version = msg.version.clone().into(); + let has_key_share = self.core.key_share.as_ref() + .map(|ks| ks.version(&version).is_ok()) + .unwrap_or(false); + data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); + data.version = Some(version); + } + data.consensus_session.on_consensus_message(&sender, &message.message)?; + + 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(()); + } + + let key_share = self.core.key_share.as_ref() + .expect("this is master node; master node is selected so that it has key version; qed"); + let key_version = key_share.version(data.version.as_ref() + .expect("this is master node; master node is selected so that it has key version; qed") + ).map_err(|e| Error::KeyStorage(e.into()))?; + + 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); + let consensus_group_map: BTreeMap<_, _> = consensus_group.iter().map(|n| (n.clone(), key_version.id_numbers[n].clone())).collect(); + + // start generation of signature nonce + let sig_nonce_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, + |s, k, n, m| EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage( + EcdsaSignatureNonceGenerationMessage { + session: s.into(), + sub_session: k.into(), + session_nonce: n, + message: m, + })); + sig_nonce_generation_session.initialize(Public::default(), false, key_share.threshold, consensus_group_map.clone().into())?; + data.sig_nonce_generation_session = Some(sig_nonce_generation_session); + + // start generation of inversed nonce computation session + let inv_nonce_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, + move |s, k, n, m| EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage( + EcdsaInversionNonceGenerationMessage { + session: s.into(), + sub_session: k.into(), + session_nonce: n, + message: m, + })); + inv_nonce_generation_session.initialize(Public::default(), false, key_share.threshold, consensus_group_map.clone().into())?; + data.inv_nonce_generation_session = Some(inv_nonce_generation_session); + + // start generation of zero-secret shares for inversed nonce computation session + let inv_zero_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, + move |s, k, n, m| EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage( + EcdsaInversionZeroGenerationMessage { + session: s.into(), + sub_session: k.into(), + session_nonce: n, + message: m, + })); + inv_zero_generation_session.initialize(Public::default(), true, key_share.threshold * 2, consensus_group_map.clone().into())?; + data.inv_zero_generation_session = Some(inv_zero_generation_session); + + data.state = SessionState::NoncesGenerating; + + Ok(()) + } + + /// When signature nonce generation message is received. + pub fn on_signature_nonce_generation_message(&self, sender: &NodeId, message: &EcdsaSignatureNonceGenerationMessage) -> 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(); + + if let &GenerationMessage::InitializeSession(ref message) = &message.message { + if &self.core.meta.master_node_id != sender { + match data.delegation_status.as_ref() { + Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), + _ => return Err(Error::InvalidMessage), + } + } + + 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); + + data.sig_nonce_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, + |s, k, n, m| EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage( + EcdsaSignatureNonceGenerationMessage { + session: s.into(), + sub_session: k.into(), + session_nonce: n, + message: m, + }))); + + data.state = SessionState::NoncesGenerating; + } + + { + let generation_session = data.sig_nonce_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; + 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(()); + } + } + + if !Self::check_nonces_generated(&*data) { + return Ok(()); + } + + Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; + data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { + SessionState::SignatureComputing + } else { + SessionState::WaitingForInversedNonceShares + }; + + Ok(()) + } + + /// When inversion nonce generation message is received. + pub fn on_inversion_nonce_generation_message(&self, sender: &NodeId, message: &EcdsaInversionNonceGenerationMessage) -> 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(); + + if let &GenerationMessage::InitializeSession(ref message) = &message.message { + if &self.core.meta.master_node_id != sender { + match data.delegation_status.as_ref() { + Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), + _ => return Err(Error::InvalidMessage), + } + } + + 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); + + data.inv_nonce_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, + |s, k, n, m| EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage( + EcdsaInversionNonceGenerationMessage { + session: s.into(), + sub_session: k.into(), + session_nonce: n, + message: m, + }))); + + data.state = SessionState::NoncesGenerating; + } + + { + let generation_session = data.inv_nonce_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; + 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(()); + } + } + + if !Self::check_nonces_generated(&*data) { + return Ok(()); + } + + Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; + data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { + SessionState::SignatureComputing + } else { + SessionState::WaitingForInversedNonceShares + }; + + Ok(()) + } + + /// When inversion zero generation message is received. + pub fn on_inversion_zero_generation_message(&self, sender: &NodeId, message: &EcdsaInversionZeroGenerationMessage) -> 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(); + + if let &GenerationMessage::InitializeSession(ref message) = &message.message { + if &self.core.meta.master_node_id != sender { + match data.delegation_status.as_ref() { + Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), + _ => return Err(Error::InvalidMessage), + } + } + + 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); + + data.inv_zero_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, + |s, k, n, m| EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage( + EcdsaInversionZeroGenerationMessage { + session: s.into(), + sub_session: k.into(), + session_nonce: n, + message: m, + }))); + + data.state = SessionState::NoncesGenerating; + } + + { + let generation_session = data.inv_zero_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; + 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(()); + } + } + + if !Self::check_nonces_generated(&*data) { + return Ok(()); + } + + Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; + data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { + SessionState::SignatureComputing + } else { + SessionState::WaitingForInversedNonceShares + }; + + Ok(()) + } + + /// When inversed nonce share is received. + pub fn on_inversed_nonce_coeff_share(&self, sender: &NodeId, message: &EcdsaSigningInversedNonceCoeffShare) -> 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(); + + if self.core.meta.self_node_id != self.core.meta.master_node_id { + return Err(Error::InvalidMessage); + } + match data.state { + SessionState::WaitingForInversedNonceShares => (), + SessionState::NoncesGenerating => return Err(Error::TooEarlyForRequest), + _ => return Err(Error::InvalidStateForRequest), + } + + let inversed_nonce_coeff = { + let consensus_group = data.consensus_session.select_consensus_group()?.clone(); + { + let inversed_nonce_coeff_shares = data.inversed_nonce_coeff_shares.as_mut() + .expect("we are in WaitingForInversedNonceShares state; inversed_nonce_coeff_shares are filled before this state; qed"); + match inversed_nonce_coeff_shares.entry(sender.clone()) { + Entry::Occupied(_) => return Err(Error::InvalidStateForRequest), + Entry::Vacant(entry) => { + entry.insert(message.inversed_nonce_coeff_share.clone().into()); + }, + } + + if consensus_group.iter().any(|n| !inversed_nonce_coeff_shares.contains_key(n)) { + return Ok(()); + } + } + + Self::compute_inversed_nonce_coeff(&self.core, &*data)? + }; + + + let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); + 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 nonce_exists_proof = "nonce is generated before signature is computed; we are in SignatureComputing state; qed"; + let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; + let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; + + self.core.disseminate_jobs(&mut data.consensus_session, &version, sig_nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash) + } + + /// When partial signature is requested. + pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &EcdsaRequestPartialSignature) -> 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 key_share = match self.core.key_share.as_ref() { + None => return Err(Error::InvalidMessage), + Some(key_share) => key_share, + }; + + let mut data = self.data.lock(); + + if sender != &self.core.meta.master_node_id { + return Err(Error::InvalidMessage); + } + if data.state != SessionState::SignatureComputing { + return Err(Error::InvalidStateForRequest); + } + + let nonce_exists_proof = "nonce is generated before signature is computed; we are in SignatureComputing state; qed"; + let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; + let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; + + let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); + let key_version = key_share.version(&version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + + let signing_job = EcdsaSigningJob::new_on_slave(key_share.clone(), key_version, sig_nonce_public, inv_nonce_share)?; + let signing_transport = self.core.signing_transport(); + + data.consensus_session.on_job_request(sender, EcdsaPartialSigningRequest { + id: message.request_id.clone().into(), + inversed_nonce_coeff: message.inversed_nonce_coeff.clone().into(), + message_hash: message.message_hash.clone().into(), + }, signing_job, signing_transport) + } + + /// When partial signature is received. + pub fn on_partial_signature(&self, sender: &NodeId, message: &EcdsaPartialSignature) -> 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, EcdsaPartialSigningResponse { + request_id: message.request_id.clone().into(), + partial_signature_s: message.partial_signature_s.clone().into(), + })?; + + if data.consensus_session.state() != ConsensusSessionState::Finished { + return Ok(()); + } + + // send compeltion signal to all nodes, except for rejected nodes + for node in data.consensus_session.consensus_non_rejected_nodes() { + self.core.cluster.send(&node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(EcdsaSigningSessionCompleted { + session: self.core.meta.id.clone().into(), + sub_session: self.core.access_key.clone().into(), + session_nonce: self.core.nonce, + })))?; + } + + let result = data.consensus_session.result()?; + Self::set_signing_result(&self.core, &mut *data, Ok(result)); + + Ok(()) + } + + /// When session is completed. + pub fn on_session_completed(&self, sender: &NodeId, message: &EcdsaSigningSessionCompleted) -> 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); + + self.data.lock().consensus_session.on_session_completed(sender) + } + + /// Process error from the other node. + fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { + let mut data = self.data.lock(); + let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); + // error is always fatal if coming from this node + if is_self_node_error { + Self::set_signing_result(&self.core, &mut *data, Err(error.clone())); + return Err(error); + } + + match { + match node { + Some(node) => data.consensus_session.on_node_error(node), + None => data.consensus_session.on_session_timeout(), + } + } { + Ok(false) => { + Ok(()) + }, + Ok(true) => { + let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); + + 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 nonce_exists_proof = "on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when nonces generation has completed; qed"; + let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; + let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; + + let inversed_nonce_coeff = Self::compute_inversed_nonce_coeff(&self.core, &*data)?; + + let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, &version, sig_nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash); + match disseminate_result { + Ok(()) => Ok(()), + Err(err) => { + warn!("{}: ECDSA signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); + Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); + Err(err) + } + } + }, + Err(err) => { + warn!("{}: ECDSA signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); + Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); + Err(err) + }, + } + } + + /// Start generation session. + fn start_generation_session(core: &SessionCore, other_consensus_group_nodes: &BTreeSet, map_message: F) -> GenerationSession + where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync + 'static { + GenerationSession::new(GenerationSessionParams { + id: core.meta.id.clone(), + self_node_id: core.meta.self_node_id.clone(), + key_storage: None, + cluster: Arc::new(NonceGenerationTransport { + id: core.meta.id.clone(), + access_key: core.access_key.clone(), + nonce: core.nonce, + cluster: core.cluster.clone(), + other_nodes_ids: other_consensus_group_nodes.clone(), + map: map_message, + }), + nonce: None, + }) + } + + /// Set signing session result. + fn set_signing_result(core: &SessionCore, data: &mut SessionData, result: Result) { + if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { + // error means can't communicate => ignore it + let _ = match result.as_ref() { + Ok(signature) => core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(EcdsaSigningSessionDelegationCompleted { + session: core.meta.id.clone().into(), + sub_session: core.access_key.clone().into(), + session_nonce: nonce, + signature: signature.clone().into(), + }))), + Err(error) => core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(EcdsaSigningSessionError { + session: core.meta.id.clone().into(), + sub_session: core.access_key.clone().into(), + session_nonce: nonce, + error: error.clone().into(), + }))), + }; + } + + data.result = Some(result); + core.completed.notify_all(); + } + + /// Check if all nonces are generated. + fn check_nonces_generated(data: &SessionData) -> bool { + let expect_proof = "check_nonces_generated is called when som nonce-gen session is completed; + all nonce-gen sessions are created at once; qed"; + let sig_nonce_generation_session = data.sig_nonce_generation_session.as_ref().expect(expect_proof); + let inv_nonce_generation_session = data.inv_nonce_generation_session.as_ref().expect(expect_proof); + let inv_zero_generation_session = data.inv_zero_generation_session.as_ref().expect(expect_proof); + + sig_nonce_generation_session.state() == GenerationSessionState::Finished + && inv_nonce_generation_session.state() == GenerationSessionState::Finished + && inv_zero_generation_session.state() == GenerationSessionState::Finished + } + + /// Broadcast inversed nonce share. + fn send_inversed_nonce_coeff_share(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { + let proof = "inversed nonce coeff share is sent after nonces generation is completed; qed"; + + let sig_nonce_generation_session = data.sig_nonce_generation_session.as_ref().expect(proof); + let sig_nonce = sig_nonce_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; + + let inv_nonce_generation_session = data.inv_nonce_generation_session.as_ref().expect(proof); + let inv_nonce = inv_nonce_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; + + let inv_zero_generation_session = data.inv_zero_generation_session.as_ref().expect(proof); + let inv_zero = inv_zero_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; + + let inversed_nonce_coeff_share = math::compute_ecdsa_inversed_secret_coeff_share(&sig_nonce, &inv_nonce, &inv_zero)?; + if core.meta.self_node_id == core.meta.master_node_id { + let mut inversed_nonce_coeff_shares = BTreeMap::new(); + inversed_nonce_coeff_shares.insert(core.meta.self_node_id.clone(), inversed_nonce_coeff_share); + data.inversed_nonce_coeff_shares = Some(inversed_nonce_coeff_shares); + Ok(()) + } else { + core.cluster.send(&core.meta.master_node_id, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(EcdsaSigningInversedNonceCoeffShare { + session: core.meta.id.clone().into(), + sub_session: core.access_key.clone().into(), + session_nonce: core.nonce, + inversed_nonce_coeff_share: inversed_nonce_coeff_share.into(), + }))) + } + } + + /// Compute inversed nonce coefficient on master node. + fn compute_inversed_nonce_coeff(core: &SessionCore, data: &SessionData) -> Result { + let proof = "inversed nonce coeff is computed on master node; key version exists on master node"; + let key_share = core.key_share.as_ref().expect(proof); + let key_version = key_share.version(data.version.as_ref().expect(proof)).expect(proof); + + let proof = "inversed nonce coeff is computed after all shares are received; qed"; + let inversed_nonce_coeff_shares = data.inversed_nonce_coeff_shares.as_ref().expect(proof); + + math::compute_ecdsa_inversed_secret_coeff_from_shares(key_share.threshold, + &inversed_nonce_coeff_shares.keys().map(|n| key_version.id_numbers[n].clone()).collect::>(), + &inversed_nonce_coeff_shares.values().cloned().collect::>()) + } +} + +impl ClusterSession for SessionImpl { + type Id = SessionIdWithSubSession; + + fn type_name() -> &'static str { + "ecdsa_signing" + } + + fn id(&self) -> SessionIdWithSubSession { + SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) + } + + fn is_finished(&self) -> bool { + let data = self.data.lock(); + data.consensus_session.state() == ConsensusSessionState::Failed + || data.consensus_session.state() == ConsensusSessionState::Finished + || data.result.is_some() + } + + fn on_node_timeout(&self, node: &NodeId) { + // ignore error, only state matters + let _ = self.process_node_error(Some(node), Error::NodeDisconnected); + } + + fn on_session_timeout(&self) { + // ignore error, only state matters + let _ = self.process_node_error(None, Error::NodeDisconnected); + } + + fn on_session_error(&self, node: &NodeId, error: Error) { + let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); + let is_this_node_error = *node == self.core.meta.self_node_id; + if is_fatal || is_this_node_error { + // error in signing session is non-fatal, if occurs on slave node + // => either respond with error + // => or broadcast error + let message = Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(EcdsaSigningSessionError { + session: self.core.meta.id.clone().into(), + sub_session: self.core.access_key.clone().into(), + session_nonce: self.core.nonce, + error: error.clone().into(), + })); + + // do not bother processing send error, as we already processing error + let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { + self.core.cluster.broadcast(message) + } else { + self.core.cluster.send(&self.core.meta.master_node_id, message) + }; + } + } + + fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { + match *message { + Message::EcdsaSigning(ref message) => self.process_message(sender, message), + _ => unreachable!("cluster checks message to be correct before passing; qed"), + } + } +} + +impl NonceGenerationTransport where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync { + fn map_message(&self, message: Message) -> Result { + match message { + Message::Generation(message) => Ok(Message::EcdsaSigning((self.map)(self.id.clone(), self.access_key.clone(), self.nonce, message))), + _ => Err(Error::InvalidMessage), + } + } +} + +impl Cluster for NonceGenerationTransport where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync { + fn broadcast(&self, message: Message) -> Result<(), Error> { + 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> { + debug_assert!(self.other_nodes_ids.contains(to)); + self.cluster.send(to, self.map_message(message)?) + } + + fn is_connected(&self, node: &NodeId) -> bool { + self.cluster.is_connected(node) + } + + fn nodes(&self) -> BTreeSet { + self.cluster.nodes() + } +} + +impl SessionCore { + pub fn signing_transport(&self) -> SigningJobTransport { + SigningJobTransport { + id: self.meta.id.clone(), + access_key: self.access_key.clone(), + nonce: self.nonce, + cluster: self.cluster.clone() + } + } + + pub fn disseminate_jobs(&self, consensus_session: &mut SigningConsensusSession, version: &H256, nonce_public: Public, inv_nonce_share: Secret, inversed_nonce_coeff: Secret, message_hash: H256) -> Result<(), Error> { + let key_share = match self.key_share.as_ref() { + None => return Err(Error::InvalidMessage), + Some(key_share) => key_share, + }; + + let key_version = key_share.version(version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); + let signing_job = EcdsaSigningJob::new_on_master(key_share.clone(), key_version, nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash)?; + consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false) + } +} + +impl JobTransport for SigningConsensusTransport { + type PartialJobRequest=Signature; + type PartialJobResponse=bool; + + fn send_partial_request(&self, node: &NodeId, request: Signature) -> Result<(), Error> { + let version = self.version.as_ref() + .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); + self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + session_nonce: self.nonce, + message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { + requestor_signature: request.into(), + version: version.clone().into(), + }) + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { + self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + session_nonce: self.nonce, + message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { + is_confirmed: response, + }) + }))) + } +} + +impl JobTransport for SigningJobTransport { + type PartialJobRequest=EcdsaPartialSigningRequest; + type PartialJobResponse=EcdsaPartialSigningResponse; + + fn send_partial_request(&self, node: &NodeId, request: EcdsaPartialSigningRequest) -> Result<(), Error> { + self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(EcdsaRequestPartialSignature { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + session_nonce: self.nonce, + request_id: request.id.into(), + inversed_nonce_coeff: request.inversed_nonce_coeff.into(), + message_hash: request.message_hash.into(), + }))) + } + + fn send_partial_response(&self, node: &NodeId, response: EcdsaPartialSigningResponse) -> Result<(), Error> { + self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(EcdsaPartialSignature { + session: self.id.clone().into(), + sub_session: self.access_key.clone().into(), + session_nonce: self.nonce, + request_id: response.request_id.into(), + partial_signature_s: response.partial_signature_s.into(), + }))) + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::collections::{BTreeSet, BTreeMap, VecDeque}; + use ethereum_types::H256; + use ethkey::{self, Random, Generator, Public, KeyPair, verify_public}; + use acl_storage::DummyAclStorage; + use key_server_cluster::{NodeId, DummyKeyStorage, SessionId, SessionMeta, Error, KeyStorage}; + use key_server_cluster::cluster_sessions::ClusterSession; + use key_server_cluster::cluster::tests::DummyCluster; + use key_server_cluster::generation_session::tests::MessageLoop as KeyGenerationMessageLoop; + use key_server_cluster::message::Message; + use key_server_cluster::signing_session_ecdsa::{SessionImpl, SessionParams}; + + struct Node { + pub node_id: NodeId, + pub cluster: Arc, + pub key_storage: Arc, + pub session: SessionImpl, + } + + struct MessageLoop { + pub session_id: SessionId, + pub requester: KeyPair, + pub nodes: BTreeMap, + pub queue: VecDeque<(NodeId, NodeId, Message)>, + pub acl_storages: Vec>, + pub version: H256, + } + + impl MessageLoop { + pub fn new(gl: &KeyGenerationMessageLoop) -> Self { + let version = gl.nodes.values().nth(0).unwrap().key_storage.get(&Default::default()).unwrap().unwrap().versions.iter().last().unwrap().hash; + let mut nodes = BTreeMap::new(); + let session_id = gl.session_id.clone(); + 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(); + let mut acl_storages = Vec::new(); + for (i, (gl_node_id, gl_node)) in gl.nodes.iter().enumerate() { + let acl_storage = Arc::new(DummyAclStorage::default()); + acl_storages.push(acl_storage.clone()); + let cluster = Arc::new(DummyCluster::new(gl_node_id.clone())); + let session = SessionImpl::new(SessionParams { + 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().unwrap().threshold, + }, + access_key: "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), + key_share: Some(gl_node.key_storage.get(&session_id).unwrap().unwrap()), + acl_storage: acl_storage, + cluster: cluster.clone(), + nonce: 0, + }, if i == 0 { signature.clone() } else { None }).unwrap(); + nodes.insert(gl_node_id.clone(), Node { node_id: gl_node_id.clone(), cluster: cluster, key_storage: gl_node.key_storage.clone(), 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, + requester: requester, + nodes: nodes, + queue: VecDeque::new(), + acl_storages: acl_storages, + version: version, + } + } + + pub fn master(&self) -> &SessionImpl { + &self.nodes.values().nth(0).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.node_id.clone(), m.0, m.1))) + .nth(0) + .or_else(|| self.queue.pop_front()) + } + + pub fn process_message(&mut self, mut msg: (NodeId, NodeId, Message)) -> Result<(), Error> { + let mut is_queued_message = false; + loop { + match self.nodes[&msg.1].session.on_message(&msg.0, &msg.2) { + 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), + } + } + } + } + + fn prepare_signing_sessions(threshold: usize, num_nodes: usize) -> (KeyGenerationMessageLoop, MessageLoop) { + // run key generation sessions + let mut gl = KeyGenerationMessageLoop::new(num_nodes); + gl.master().initialize(Public::default(), false, threshold, gl.nodes.keys().cloned().collect::>().into()).unwrap(); + while let Some((from, to, message)) = gl.take_message() { + gl.process_message((from, to, message)).unwrap(); + } + + // run signing session + let sl = MessageLoop::new(&gl); + (gl, sl) + } + + #[test] + fn failed_gen_ecdsa_sign_session_when_threshold_is_too_low() { + let test_cases = [(1, 2), (2, 4), (3, 6), (4, 6)]; + for &(threshold, num_nodes) in &test_cases { + let (_, sl) = prepare_signing_sessions(threshold, num_nodes); + + // run signing session + let message_hash = H256::random(); + assert_eq!(sl.master().initialize(sl.version.clone(), message_hash).unwrap_err(), Error::ConsensusUnreachable); + } + } + + #[test] + fn complete_gen_ecdsa_sign_session() { + let test_cases = [(0, 1), (2, 5), (2, 6), (3, 11), (4, 11)]; + for &(threshold, num_nodes) in &test_cases { + let (gl, mut sl) = prepare_signing_sessions(threshold, num_nodes); + let key_pair = gl.compute_key_pair(threshold); + + // run signing session + let message_hash = H256::random(); + sl.master().initialize(sl.version.clone(), message_hash).unwrap(); + while let Some((from, to, message)) = sl.take_message() { + sl.process_message((from, to, message)).unwrap(); + } + + // verify signature + let signature = sl.master().wait().unwrap(); + assert!(verify_public(key_pair.public(), &signature, &message_hash).unwrap()); + } + } + + #[test] + fn ecdsa_complete_signing_session_with_single_node_failing() { + let (_, mut sl) = prepare_signing_sessions(1, 4); + sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); + + // we need at least 3-of-4 nodes to agree to reach consensus + // let's say 1 of 4 nodes disagee + sl.acl_storages[1].prohibit(sl.requester.public().clone(), SessionId::default()); + + // then consensus reachable, but single node will disagree + while let Some((from, to, message)) = sl.take_message() { + sl.process_message((from, to, message)).unwrap(); + } + + let data = sl.master().data.lock(); + match data.result { + Some(Ok(_)) => (), + _ => unreachable!(), + } + } + + #[test] + fn ecdsa_complete_signing_session_with_acl_check_failed_on_master() { + let (_, mut sl) = prepare_signing_sessions(1, 4); + sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); + + // we need at least 3-of-4 nodes to agree to reach consensus + // let's say 1 of 4 nodes disagee + sl.acl_storages[0].prohibit(sl.requester.public().clone(), SessionId::default()); + + // then consensus reachable, but single node will disagree + while let Some((from, to, message)) = sl.take_message() { + sl.process_message((from, to, message)).unwrap(); + } + + let data = sl.master().data.lock(); + match data.result { + Some(Ok(_)) => (), + _ => unreachable!(), + } + } + + #[test] + fn ecdsa_signing_works_when_delegated_to_other_node() { + let (_, mut sl) = prepare_signing_sessions(1, 4); + + // let's say node1 doesn't have a share && delegates decryption request to node0 + // initially session is created on node1 => node1 is master for itself, but for other nodes node0 is still master + let actual_master = sl.nodes.keys().nth(0).cloned().unwrap(); + let requested_node = sl.nodes.keys().skip(1).nth(0).cloned().unwrap(); + let version = sl.nodes[&actual_master].key_storage.get(&Default::default()).unwrap().unwrap().last_version().unwrap().hash.clone(); + sl.nodes[&requested_node].key_storage.remove(&Default::default()).unwrap(); + sl.nodes.get_mut(&requested_node).unwrap().session.core.key_share = None; + sl.nodes.get_mut(&requested_node).unwrap().session.core.meta.master_node_id = sl.nodes[&requested_node].session.core.meta.self_node_id.clone(); + sl.nodes[&requested_node].session.data.lock().consensus_session.consensus_job_mut().executor_mut().set_requester_signature( + sl.nodes[&actual_master].session.data.lock().consensus_session.consensus_job().executor().requester_signature().unwrap().clone() + ); + + // now let's try to do a decryption + sl.nodes[&requested_node].session.delegate(actual_master, version, H256::random()).unwrap(); + + // then consensus reachable, but single node will disagree + while let Some((from, to, message)) = sl.take_message() { + sl.process_message((from, to, message)).unwrap(); + } + } + + #[test] + fn ecdsa_signing_works_when_share_owners_are_isolated() { + let (_, mut sl) = prepare_signing_sessions(2, 6); + + // we need 5 out of 6 nodes to agree to do a decryption + // let's say that 1 of these nodes (master) is isolated + let isolated_node_id = sl.nodes.keys().skip(2).nth(0).cloned().unwrap(); + for node in sl.nodes.values() { + node.cluster.remove_node(&isolated_node_id); + } + + // now let's try to do a signing + sl.master().initialize(sl.version.clone(), H256::random()).unwrap(); + + // then consensus reachable, but single node will disagree + while let Some((from, to, message)) = sl.take_message() { + sl.process_message((from, to, message)).unwrap(); + } + + let data = sl.master().data.lock(); + match data.result { + Some(Ok(_)) => (), + _ => unreachable!(), + } + } +} diff --git a/secret_store/src/key_server_cluster/client_sessions/signing_session.rs b/secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs similarity index 86% rename from secret_store/src/key_server_cluster/client_sessions/signing_session.rs rename to secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs index 4c5676fc1..ae3e0289f 100644 --- a/secret_store/src/key_server_cluster/client_sessions/signing_session.rs +++ b/secret_store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs @@ -24,15 +24,16 @@ use key_server_cluster::cluster::{Cluster}; use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, SessionState as GenerationSessionState}; -use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, SigningGenerationMessage, - RequestPartialSignature, PartialSignature, SigningSessionCompleted, GenerationMessage, ConsensusMessage, SigningSessionError, - InitializeConsensusSession, ConfirmConsensusInitialization, SigningSessionDelegation, SigningSessionDelegationCompleted}; +use key_server_cluster::message::{Message, SchnorrSigningMessage, SchnorrSigningConsensusMessage, SchnorrSigningGenerationMessage, + SchnorrRequestPartialSignature, SchnorrPartialSignature, SchnorrSigningSessionCompleted, GenerationMessage, + ConsensusMessage, SchnorrSigningSessionError, InitializeConsensusSession, ConfirmConsensusInitialization, + SchnorrSigningSessionDelegation, SchnorrSigningSessionDelegationCompleted}; use key_server_cluster::jobs::job_session::JobTransport; use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::signing_job::{PartialSigningRequest, PartialSigningResponse, SigningJob}; +use key_server_cluster::jobs::signing_job_schnorr::{SchnorrPartialSigningRequest, SchnorrPartialSigningResponse, SchnorrSigningJob}; use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; -/// Distributed signing session. +/// Distributed Schnorr-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 @@ -63,7 +64,7 @@ struct SessionCore { } /// Signing consensus session type. -type SigningConsensusSession = ConsensusSession; +type SigningConsensusSession = ConsensusSession; /// Mutable session data. struct SessionData { @@ -222,7 +223,7 @@ impl SessionImpl { } data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::Signing(SigningMessage::SigningSessionDelegation(SigningSessionDelegation { + self.core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(SchnorrSigningSessionDelegation { session: self.core.meta.id.clone().into(), sub_session: self.core.access_key.clone().into(), session_nonce: self.core.nonce, @@ -276,7 +277,7 @@ impl SessionImpl { }), nonce: None, }); - generation_session.initialize(Public::default(), 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect())?; + generation_session.initialize(Public::default(), false, 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect::>().into())?; debug_assert_eq!(generation_session.state(), GenerationSessionState::WaitingForGenerationConfirmation); let joint_public_and_secret = generation_session @@ -296,33 +297,33 @@ impl SessionImpl { } /// Process signing message. - pub fn process_message(&self, sender: &NodeId, message: &SigningMessage) -> Result<(), Error> { + pub fn process_message(&self, sender: &NodeId, message: &SchnorrSigningMessage) -> Result<(), Error> { if self.core.nonce != message.session_nonce() { return Err(Error::ReplayProtection); } match message { - &SigningMessage::SigningConsensusMessage(ref message) => + &SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message) => self.on_consensus_message(sender, message), - &SigningMessage::SigningGenerationMessage(ref message) => + &SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref message) => self.on_generation_message(sender, message), - &SigningMessage::RequestPartialSignature(ref message) => + &SchnorrSigningMessage::SchnorrRequestPartialSignature(ref message) => self.on_partial_signature_requested(sender, message), - &SigningMessage::PartialSignature(ref message) => + &SchnorrSigningMessage::SchnorrPartialSignature(ref message) => self.on_partial_signature(sender, message), - &SigningMessage::SigningSessionError(ref message) => + &SchnorrSigningMessage::SchnorrSigningSessionError(ref message) => self.process_node_error(Some(&sender), Error::Io(message.error.clone())), - &SigningMessage::SigningSessionCompleted(ref message) => + &SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref message) => self.on_session_completed(sender, message), - &SigningMessage::SigningSessionDelegation(ref message) => + &SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message) => self.on_session_delegated(sender, message), - &SigningMessage::SigningSessionDelegationCompleted(ref message) => + &SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref message) => self.on_session_delegation_completed(sender, message), } } /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &SigningSessionDelegation) -> Result<(), Error> { + pub fn on_session_delegated(&self, sender: &NodeId, message: &SchnorrSigningSessionDelegation) -> Result<(), Error> { debug_assert!(self.core.meta.id == *message.session); debug_assert!(self.core.access_key == *message.sub_session); @@ -340,7 +341,7 @@ impl SessionImpl { } /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &SigningSessionDelegationCompleted) -> Result<(), Error> { + pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &SchnorrSigningSessionDelegationCompleted) -> Result<(), Error> { debug_assert!(self.core.meta.id == *message.session); debug_assert!(self.core.access_key == *message.sub_session); @@ -360,7 +361,7 @@ impl SessionImpl { } /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &SigningConsensusMessage) -> Result<(), Error> { + pub fn on_consensus_message(&self, sender: &NodeId, message: &SchnorrSigningConsensusMessage) -> 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); @@ -404,7 +405,7 @@ impl SessionImpl { }), nonce: None, }); - generation_session.initialize(Public::default(), key_share.threshold, consensus_group)?; + generation_session.initialize(Public::default(), false, key_share.threshold, consensus_group.into())?; data.generation_session = Some(generation_session); data.state = SessionState::SessionKeyGeneration; @@ -412,7 +413,7 @@ impl SessionImpl { } /// When session key related message is received. - pub fn on_generation_message(&self, sender: &NodeId, message: &SigningGenerationMessage) -> Result<(), Error> { + pub fn on_generation_message(&self, sender: &NodeId, message: &SchnorrSigningGenerationMessage) -> 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); @@ -474,7 +475,7 @@ impl SessionImpl { } /// When partial signature is requested. - pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &RequestPartialSignature) -> Result<(), Error> { + pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &SchnorrRequestPartialSignature) -> 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); @@ -499,10 +500,10 @@ impl SessionImpl { .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?) .map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); - let signing_job = SigningJob::new_on_slave(self.core.meta.self_node_id.clone(), key_share.clone(), key_version, joint_public_and_secret.0, joint_public_and_secret.1)?; + let signing_job = SchnorrSigningJob::new_on_slave(self.core.meta.self_node_id.clone(), key_share.clone(), key_version, joint_public_and_secret.0, joint_public_and_secret.1)?; let signing_transport = self.core.signing_transport(); - data.consensus_session.on_job_request(sender, PartialSigningRequest { + data.consensus_session.on_job_request(sender, SchnorrPartialSigningRequest { id: message.request_id.clone().into(), message_hash: message.message_hash.clone().into(), other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), @@ -510,13 +511,13 @@ impl SessionImpl { } /// When partial signature is received. - pub fn on_partial_signature(&self, sender: &NodeId, message: &PartialSignature) -> Result<(), Error> { + pub fn on_partial_signature(&self, sender: &NodeId, message: &SchnorrPartialSignature) -> 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 { + data.consensus_session.on_job_response(sender, SchnorrPartialSigningResponse { request_id: message.request_id.clone().into(), partial_signature: message.partial_signature.clone().into(), })?; @@ -527,7 +528,7 @@ impl SessionImpl { // send compeltion signal to all nodes, except for rejected nodes for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { + self.core.cluster.send(&node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(SchnorrSigningSessionCompleted { session: self.core.meta.id.clone().into(), sub_session: self.core.access_key.clone().into(), session_nonce: self.core.nonce, @@ -541,7 +542,7 @@ impl SessionImpl { } /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &SigningSessionCompleted) -> Result<(), Error> { + pub fn on_session_completed(&self, sender: &NodeId, message: &SchnorrSigningSessionCompleted) -> 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); @@ -599,14 +600,14 @@ impl SessionImpl { if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { // error means can't communicate => ignore it let _ = match result.as_ref() { - Ok(signature) => core.cluster.send(&master, Message::Signing(SigningMessage::SigningSessionDelegationCompleted(SigningSessionDelegationCompleted { + Ok(signature) => core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(SchnorrSigningSessionDelegationCompleted { session: core.meta.id.clone().into(), sub_session: core.access_key.clone().into(), session_nonce: nonce, signature_c: signature.0.clone().into(), signature_s: signature.1.clone().into(), }))), - Err(error) => core.cluster.send(&master, Message::Signing(SigningMessage::SigningSessionError(SigningSessionError { + Err(error) => core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(SchnorrSigningSessionError { session: core.meta.id.clone().into(), sub_session: core.access_key.clone().into(), session_nonce: nonce, @@ -655,7 +656,7 @@ impl ClusterSession for SessionImpl { // error in signing session is non-fatal, if occurs on slave node // => either respond with error // => or broadcast error - let message = Message::Signing(SigningMessage::SigningSessionError(SigningSessionError { + let message = Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(SchnorrSigningSessionError { session: self.core.meta.id.clone().into(), sub_session: self.core.access_key.clone().into(), session_nonce: self.core.nonce, @@ -673,7 +674,7 @@ impl ClusterSession for SessionImpl { fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { match *message { - Message::Signing(ref message) => self.process_message(sender, message), + Message::SchnorrSigning(ref message) => self.process_message(sender, message), _ => unreachable!("cluster checks message to be correct before passing; qed"), } } @@ -682,7 +683,7 @@ impl ClusterSession for SessionImpl { impl SessionKeyGenerationTransport { fn map_message(&self, message: Message) -> Result { match message { - Message::Generation(message) => Ok(Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { + Message::Generation(message) => Ok(Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage { session: message.session_id().clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, @@ -733,7 +734,7 @@ impl SessionCore { }; let key_version = key_share.version(version).map_err(|e| Error::KeyStorage(e.into()))?.hash.clone(); - let signing_job = SigningJob::new_on_master(self.meta.self_node_id.clone(), key_share.clone(), key_version, session_public, session_secret_share, message_hash)?; + let signing_job = SchnorrSigningJob::new_on_master(self.meta.self_node_id.clone(), key_share.clone(), key_version, session_public, session_secret_share, message_hash)?; consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false) } } @@ -745,7 +746,7 @@ impl JobTransport for SigningConsensusTransport { fn send_partial_request(&self, node: &NodeId, request: Signature) -> Result<(), Error> { let version = self.version.as_ref() .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, @@ -757,7 +758,7 @@ impl JobTransport for SigningConsensusTransport { } fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { + self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, @@ -769,11 +770,11 @@ impl JobTransport for SigningConsensusTransport { } impl JobTransport for SigningJobTransport { - type PartialJobRequest=PartialSigningRequest; - type PartialJobResponse=PartialSigningResponse; + type PartialJobRequest=SchnorrPartialSigningRequest; + type PartialJobResponse=SchnorrPartialSigningResponse; - fn send_partial_request(&self, node: &NodeId, request: PartialSigningRequest) -> Result<(), Error> { - self.cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { + fn send_partial_request(&self, node: &NodeId, request: SchnorrPartialSigningRequest) -> Result<(), Error> { + self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(SchnorrRequestPartialSignature { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, @@ -783,8 +784,8 @@ impl JobTransport for SigningJobTransport { }))) } - fn send_partial_response(&self, node: &NodeId, response: PartialSigningResponse) -> Result<(), Error> { - self.cluster.send(node, Message::Signing(SigningMessage::PartialSignature(PartialSignature { + fn send_partial_response(&self, node: &NodeId, response: SchnorrPartialSigningResponse) -> Result<(), Error> { + self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(SchnorrPartialSignature { session: self.id.clone().into(), sub_session: self.access_key.clone().into(), session_nonce: self.nonce, @@ -798,7 +799,7 @@ impl JobTransport for SigningJobTransport { mod tests { use std::sync::Arc; use std::str::FromStr; - use std::collections::{BTreeMap, VecDeque}; + use std::collections::{BTreeSet, BTreeMap, VecDeque}; use ethereum_types::H256; use ethkey::{self, Random, Generator, Public, Secret, KeyPair}; use acl_storage::DummyAclStorage; @@ -807,9 +808,9 @@ mod tests { use key_server_cluster::cluster::tests::DummyCluster; use key_server_cluster::generation_session::tests::MessageLoop as KeyGenerationMessageLoop; use key_server_cluster::math; - use key_server_cluster::message::{Message, SigningMessage, SigningConsensusMessage, ConsensusMessage, ConfirmConsensusInitialization, - SigningGenerationMessage, GenerationMessage, ConfirmInitialization, InitializeSession, RequestPartialSignature}; - use key_server_cluster::signing_session::{SessionImpl, SessionState, SessionParams}; + use key_server_cluster::message::{Message, SchnorrSigningMessage, SchnorrSigningConsensusMessage, ConsensusMessage, ConfirmConsensusInitialization, + SchnorrSigningGenerationMessage, GenerationMessage, ConfirmInitialization, InitializeSession, SchnorrRequestPartialSignature}; + use key_server_cluster::signing_session_schnorr::{SessionImpl, SessionState, SessionParams}; struct Node { pub node_id: NodeId, @@ -925,7 +926,7 @@ mod tests { fn prepare_signing_sessions(threshold: usize, num_nodes: usize) -> (KeyGenerationMessageLoop, MessageLoop) { // run key generation sessions let mut gl = KeyGenerationMessageLoop::new(num_nodes); - gl.master().initialize(Public::default(), threshold, gl.nodes.keys().cloned().collect()).unwrap(); + gl.master().initialize(Public::default(), false, threshold, gl.nodes.keys().cloned().collect::>().into()).unwrap(); while let Some((from, to, message)) = gl.take_message() { gl.process_message((from, to, message)).unwrap(); } @@ -936,7 +937,7 @@ mod tests { } #[test] - fn complete_gen_sign_session() { + fn schnorr_complete_gen_sign_session() { let test_cases = [(0, 1), (0, 5), (2, 5), (3, 5)]; for &(threshold, num_nodes) in &test_cases { let (gl, mut sl) = prepare_signing_sessions(threshold, num_nodes); @@ -951,12 +952,12 @@ 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_schnorr_signature(&public, &signature, &message_hash).unwrap()); } } #[test] - fn constructs_in_cluster_of_single_node() { + fn schnorr_constructs_in_cluster_of_single_node() { let mut nodes = BTreeMap::new(); let self_node_id = Random.generate().unwrap().public().clone(); nodes.insert(self_node_id, Random.generate().unwrap().secret().clone()); @@ -990,7 +991,7 @@ mod tests { } #[test] - fn fails_to_initialize_if_does_not_have_a_share() { + fn schnorr_fails_to_initialize_if_does_not_have_a_share() { let self_node_id = Random.generate().unwrap().public().clone(); let session = SessionImpl::new(SessionParams { meta: SessionMeta { @@ -1009,7 +1010,7 @@ mod tests { } #[test] - fn fails_to_initialize_if_threshold_is_wrong() { + fn schnorr_fails_to_initialize_if_threshold_is_wrong() { let mut nodes = BTreeMap::new(); let self_node_id = Random.generate().unwrap().public().clone(); nodes.insert(self_node_id.clone(), Random.generate().unwrap().secret().clone()); @@ -1042,14 +1043,14 @@ mod tests { } #[test] - fn fails_to_initialize_when_already_initialized() { + fn schnorr_fails_to_initialize_when_already_initialized() { let (_, sl) = prepare_signing_sessions(1, 3); assert_eq!(sl.master().initialize(sl.version.clone(), 777.into()), Ok(())); assert_eq!(sl.master().initialize(sl.version.clone(), 777.into()), Err(Error::InvalidStateForRequest)); } #[test] - fn does_not_fail_when_consensus_message_received_after_consensus_established() { + fn schnorr_does_not_fail_when_consensus_message_received_after_consensus_established() { let (_, mut sl) = prepare_signing_sessions(1, 3); sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); // consensus is established @@ -1068,9 +1069,9 @@ mod tests { } #[test] - fn fails_when_consensus_message_is_received_when_not_initialized() { + fn schnorr_fails_when_consensus_message_is_received_when_not_initialized() { let (_, sl) = prepare_signing_sessions(1, 3); - assert_eq!(sl.master().on_consensus_message(sl.nodes.keys().nth(1).unwrap(), &SigningConsensusMessage { + assert_eq!(sl.master().on_consensus_message(sl.nodes.keys().nth(1).unwrap(), &SchnorrSigningConsensusMessage { session: SessionId::default().into(), sub_session: sl.master().core.access_key.clone().into(), session_nonce: 0, @@ -1081,9 +1082,9 @@ mod tests { } #[test] - fn fails_when_generation_message_is_received_when_not_initialized() { + fn schnorr_fails_when_generation_message_is_received_when_not_initialized() { let (_, sl) = prepare_signing_sessions(1, 3); - assert_eq!(sl.master().on_generation_message(sl.nodes.keys().nth(1).unwrap(), &SigningGenerationMessage { + assert_eq!(sl.master().on_generation_message(sl.nodes.keys().nth(1).unwrap(), &SchnorrSigningGenerationMessage { session: SessionId::default().into(), sub_session: sl.master().core.access_key.clone().into(), session_nonce: 0, @@ -1096,7 +1097,7 @@ mod tests { } #[test] - fn fails_when_generation_sesson_is_initialized_by_slave_node() { + fn schnorr_fails_when_generation_sesson_is_initialized_by_slave_node() { let (_, mut sl) = prepare_signing_sessions(1, 3); sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); sl.run_until(|sl| sl.master().state() == SessionState::SessionKeyGeneration).unwrap(); @@ -1104,7 +1105,7 @@ mod tests { let slave2_id = sl.nodes.keys().nth(2).unwrap().clone(); let slave1 = &sl.nodes.values().nth(1).unwrap().session; - assert_eq!(slave1.on_generation_message(&slave2_id, &SigningGenerationMessage { + assert_eq!(slave1.on_generation_message(&slave2_id, &SchnorrSigningGenerationMessage { session: SessionId::default().into(), sub_session: sl.master().core.access_key.clone().into(), session_nonce: 0, @@ -1113,6 +1114,7 @@ mod tests { session_nonce: 0, author: Public::default().into(), nodes: BTreeMap::new(), + is_zero: false, threshold: 1, derived_point: Public::default().into(), }) @@ -1120,10 +1122,10 @@ mod tests { } #[test] - fn fails_when_signature_requested_when_not_initialized() { + fn schnorr_fails_when_signature_requested_when_not_initialized() { let (_, sl) = prepare_signing_sessions(1, 3); let slave1 = &sl.nodes.values().nth(1).unwrap().session; - assert_eq!(slave1.on_partial_signature_requested(sl.nodes.keys().nth(0).unwrap(), &RequestPartialSignature { + assert_eq!(slave1.on_partial_signature_requested(sl.nodes.keys().nth(0).unwrap(), &SchnorrRequestPartialSignature { session: SessionId::default().into(), sub_session: sl.master().core.access_key.clone().into(), session_nonce: 0, @@ -1134,9 +1136,9 @@ mod tests { } #[test] - fn fails_when_signature_requested_by_slave_node() { + fn schnorr_fails_when_signature_requested_by_slave_node() { let (_, sl) = prepare_signing_sessions(1, 3); - assert_eq!(sl.master().on_partial_signature_requested(sl.nodes.keys().nth(1).unwrap(), &RequestPartialSignature { + assert_eq!(sl.master().on_partial_signature_requested(sl.nodes.keys().nth(1).unwrap(), &SchnorrRequestPartialSignature { session: SessionId::default().into(), sub_session: sl.master().core.access_key.clone().into(), session_nonce: 0, @@ -1147,7 +1149,7 @@ mod tests { } #[test] - fn failed_signing_session() { + fn schnorr_failed_signing_session() { let (_, mut sl) = prepare_signing_sessions(1, 3); sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); @@ -1161,7 +1163,7 @@ mod tests { } #[test] - fn complete_signing_session_with_single_node_failing() { + fn schnorr_complete_signing_session_with_single_node_failing() { let (_, mut sl) = prepare_signing_sessions(1, 3); sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); @@ -1182,7 +1184,7 @@ mod tests { } #[test] - fn complete_signing_session_with_acl_check_failed_on_master() { + fn schnorr_complete_signing_session_with_acl_check_failed_on_master() { let (_, mut sl) = prepare_signing_sessions(1, 3); sl.master().initialize(sl.version.clone(), 777.into()).unwrap(); @@ -1203,9 +1205,9 @@ mod tests { } #[test] - fn signing_message_fails_when_nonce_is_wrong() { + fn schnorr_signing_message_fails_when_nonce_is_wrong() { let (_, sl) = prepare_signing_sessions(1, 3); - assert_eq!(sl.master().process_message(sl.nodes.keys().nth(1).unwrap(), &SigningMessage::SigningGenerationMessage(SigningGenerationMessage { + assert_eq!(sl.master().process_message(sl.nodes.keys().nth(1).unwrap(), &SchnorrSigningMessage::SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage { session: SessionId::default().into(), sub_session: sl.master().core.access_key.clone().into(), session_nonce: 10, @@ -1218,7 +1220,7 @@ mod tests { } #[test] - fn signing_works_when_delegated_to_other_node() { + fn schnorr_signing_works_when_delegated_to_other_node() { let (_, mut sl) = prepare_signing_sessions(1, 3); // let's say node1 doesn't have a share && delegates decryption request to node0 @@ -1243,7 +1245,7 @@ mod tests { } #[test] - fn signing_works_when_share_owners_are_isolated() { + fn schnorr_signing_works_when_share_owners_are_isolated() { let (_, mut sl) = prepare_signing_sessions(1, 3); // we need 2 out of 3 nodes to agree to do a decryption @@ -1267,4 +1269,4 @@ mod tests { _ => unreachable!(), } } -} +} \ No newline at end of file diff --git a/secret_store/src/key_server_cluster/cluster.rs b/secret_store/src/key_server_cluster/cluster.rs index 0f8ebdd60..52d7f6b16 100644 --- a/secret_store/src/key_server_cluster/cluster.rs +++ b/secret_store/src/key_server_cluster/cluster.rs @@ -36,7 +36,8 @@ use key_server_cluster::message::{self, Message, ClusterMessage}; use key_server_cluster::generation_session::{SessionImpl as GenerationSession}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; use key_server_cluster::encryption_session::{SessionImpl as EncryptionSession}; -use key_server_cluster::signing_session::{SessionImpl as SigningSession}; +use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSession}; +use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSession}; use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, IsolatedSessionTransport as KeyVersionNegotiationSessionTransport, ContinueAction}; use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, read_encrypted_message, WriteMessage, write_encrypted_message}; @@ -71,7 +72,9 @@ pub trait ClusterClient: Send + Sync { /// Start new decryption session. fn new_decryption_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, is_shadow_decryption: bool) -> Result, Error>; /// Start new signing session. - fn new_signing_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, message_hash: H256) -> Result, Error>; + fn new_schnorr_signing_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, message_hash: H256) -> Result, Error>; + /// Start new ECDSA session. + fn new_ecdsa_signing_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, message_hash: H256) -> Result, Error>; /// Start new key version negotiation session. fn new_key_version_negotiation_session(&self, session_id: SessionId) -> Result>, Error>; /// Start new servers set change session. @@ -441,7 +444,9 @@ impl ClusterCore { .map(|_| ()).unwrap_or_default(), Message::Decryption(message) => Self::process_message(&data, &data.sessions.decryption_sessions, connection, Message::Decryption(message)) .map(|_| ()).unwrap_or_default(), - Message::Signing(message) => Self::process_message(&data, &data.sessions.signing_sessions, connection, Message::Signing(message)) + Message::SchnorrSigning(message) => Self::process_message(&data, &data.sessions.schnorr_signing_sessions, connection, Message::SchnorrSigning(message)) + .map(|_| ()).unwrap_or_default(), + Message::EcdsaSigning(message) => Self::process_message(&data, &data.sessions.ecdsa_signing_sessions, connection, Message::EcdsaSigning(message)) .map(|_| ()).unwrap_or_default(), Message::ServersSetChange(message) => { let message = Message::ServersSetChange(message); @@ -484,7 +489,7 @@ impl ClusterCore { data.sessions.decryption_sessions.remove(&session.id()); } }, - Some(ContinueAction::Sign(session, message_hash)) => { + Some(ContinueAction::SchnorrSign(session, message_hash)) => { let initialization_error = if data.self_key_pair.public() == &master { session.initialize(version, message_hash) } else { @@ -493,7 +498,19 @@ impl ClusterCore { if let Err(error) = initialization_error { session.on_session_error(&meta.self_node_id, error); - data.sessions.signing_sessions.remove(&session.id()); + data.sessions.schnorr_signing_sessions.remove(&session.id()); + } + }, + Some(ContinueAction::EcdsaSign(session, message_hash)) => { + let initialization_error = if data.self_key_pair.public() == &master { + session.initialize(version, message_hash) + } else { + session.delegate(master, version, message_hash) + }; + + if let Err(error) = initialization_error { + session.on_session_error(&meta.self_node_id, error); + data.sessions.ecdsa_signing_sessions.remove(&session.id()); } }, None => (), @@ -503,8 +520,12 @@ impl ClusterCore { data.sessions.decryption_sessions.remove(&session.id()); session.on_session_error(&meta.self_node_id, error); }, - Some(ContinueAction::Sign(session, _)) => { - data.sessions.signing_sessions.remove(&session.id()); + Some(ContinueAction::SchnorrSign(session, _)) => { + data.sessions.schnorr_signing_sessions.remove(&session.id()); + session.on_session_error(&meta.self_node_id, error); + }, + Some(ContinueAction::EcdsaSign(session, _)) => { + data.sessions.ecdsa_signing_sessions.remove(&session.id()); session.on_session_error(&meta.self_node_id, error); }, None => (), @@ -886,7 +907,7 @@ impl ClusterClient for ClusterClientImpl { let cluster = create_cluster_view(&self.data, true)?; let session = self.data.sessions.generation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - match session.initialize(author, threshold, connected_nodes) { + match session.initialize(author, false, threshold, connected_nodes.into()) { Ok(()) => Ok(session), Err(error) => { self.data.sessions.generation_sessions.remove(&session.id()); @@ -939,21 +960,21 @@ impl ClusterClient for ClusterClientImpl { } } - fn new_signing_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, message_hash: H256) -> Result, Error> { + fn new_schnorr_signing_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, 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 session_id = SessionIdWithSubSession::new(session_id, access_key); let cluster = create_cluster_view(&self.data, false)?; - let session = self.data.sessions.signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requestor_signature))?; + let session = self.data.sessions.schnorr_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requestor_signature))?; let initialization_result = match version { Some(version) => session.initialize(version, message_hash), None => { self.create_key_version_negotiation_session(session_id.id.clone()) .map(|version_session| { - version_session.set_continue_action(ContinueAction::Sign(session.clone(), message_hash)); + version_session.set_continue_action(ContinueAction::SchnorrSign(session.clone(), message_hash)); ClusterCore::try_continue_session(&self.data, Some(version_session)); }) }, @@ -962,7 +983,36 @@ impl ClusterClient for ClusterClientImpl { match initialization_result { Ok(()) => Ok(session), Err(error) => { - self.data.sessions.signing_sessions.remove(&session.id()); + self.data.sessions.schnorr_signing_sessions.remove(&session.id()); + Err(error) + }, + } + } + + fn new_ecdsa_signing_session(&self, session_id: SessionId, requestor_signature: Signature, version: Option, 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 session_id = SessionIdWithSubSession::new(session_id, access_key); + let cluster = create_cluster_view(&self.data, false)?; + let session = self.data.sessions.ecdsa_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requestor_signature))?; + + let initialization_result = match version { + Some(version) => session.initialize(version, message_hash), + None => { + self.create_key_version_negotiation_session(session_id.id.clone()) + .map(|version_session| { + version_session.set_continue_action(ContinueAction::EcdsaSign(session.clone(), message_hash)); + ClusterCore::try_continue_session(&self.data, Some(version_session)); + }) + }, + }; + + match initialization_result { + Ok(()) => Ok(session), + Err(error) => { + self.data.sessions.ecdsa_signing_sessions.remove(&session.id()); Err(error) }, } @@ -1047,7 +1097,8 @@ pub mod tests { use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionState as GenerationSessionState}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; use key_server_cluster::encryption_session::{SessionImpl as EncryptionSession}; - use key_server_cluster::signing_session::{SessionImpl as SigningSession}; + use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSession}; + use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSession}; use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, IsolatedSessionTransport as KeyVersionNegotiationSessionTransport}; @@ -1071,7 +1122,8 @@ pub mod tests { fn new_generation_session(&self, _session_id: SessionId, _author: Public, _threshold: usize) -> Result, Error> { unimplemented!("test-only") } fn new_encryption_session(&self, _session_id: SessionId, _requestor_signature: Signature, _common_point: Public, _encrypted_point: Public) -> Result, Error> { unimplemented!("test-only") } fn new_decryption_session(&self, _session_id: SessionId, _requestor_signature: Signature, _version: Option, _is_shadow_decryption: bool) -> Result, Error> { unimplemented!("test-only") } - fn new_signing_session(&self, _session_id: SessionId, _requestor_signature: Signature, _version: Option, _message_hash: H256) -> Result, Error> { unimplemented!("test-only") } + fn new_schnorr_signing_session(&self, _session_id: SessionId, _requestor_signature: Signature, _version: Option, _message_hash: H256) -> Result, Error> { unimplemented!("test-only") } + fn new_ecdsa_signing_session(&self, _session_id: SessionId, _requestor_signature: Signature, _version: Option, _message_hash: H256) -> Result, Error> { unimplemented!("test-only") } fn new_key_version_negotiation_session(&self, _session_id: SessionId) -> Result>, Error> { unimplemented!("test-only") } fn new_servers_set_change_session(&self, _session_id: Option, _migration_id: Option, _new_nodes_set: BTreeSet, _old_set_signature: Signature, _new_set_signature: Signature) -> Result, Error> { unimplemented!("test-only") } @@ -1329,7 +1381,7 @@ pub mod tests { } #[test] - fn signing_session_completes_if_node_does_not_have_a_share() { + fn schnorr_signing_session_completes_if_node_does_not_have_a_share() { //::logger::init_log(); let mut core = Core::new().unwrap(); let clusters = make_clusters(&core, 6028, 3); @@ -1349,19 +1401,19 @@ pub mod tests { // and try to sign message with generated key let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session0 = clusters[0].client().new_signing_session(Default::default(), signature, None, Default::default()).unwrap(); - let session = clusters[0].data.sessions.signing_sessions.first().unwrap(); + let session0 = clusters[0].client().new_schnorr_signing_session(Default::default(), signature, None, Default::default()).unwrap(); + let session = clusters[0].data.sessions.schnorr_signing_sessions.first().unwrap(); loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished() && (0..3).all(|i| - clusters[i].data.sessions.signing_sessions.is_empty())); + clusters[i].data.sessions.schnorr_signing_sessions.is_empty())); session0.wait().unwrap(); // and try to sign message with generated key using node that has no key share let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session2 = clusters[2].client().new_signing_session(Default::default(), signature, None, Default::default()).unwrap(); - let session = clusters[2].data.sessions.signing_sessions.first().unwrap(); + let session2 = clusters[2].client().new_schnorr_signing_session(Default::default(), signature, None, Default::default()).unwrap(); + let session = clusters[2].data.sessions.schnorr_signing_sessions.first().unwrap(); loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished() && (0..3).all(|i| - clusters[i].data.sessions.signing_sessions.is_empty())); + clusters[i].data.sessions.schnorr_signing_sessions.is_empty())); session2.wait().unwrap(); // now remove share from node1 @@ -1369,9 +1421,56 @@ pub mod tests { // and try to sign message with generated key let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session1 = clusters[0].client().new_signing_session(Default::default(), signature, None, Default::default()).unwrap(); - let session = clusters[0].data.sessions.signing_sessions.first().unwrap(); + let session1 = clusters[0].client().new_schnorr_signing_session(Default::default(), signature, None, Default::default()).unwrap(); + let session = clusters[0].data.sessions.schnorr_signing_sessions.first().unwrap(); loop_until(&mut core, time::Duration::from_millis(300), || session.is_finished()); session1.wait().unwrap_err(); } + + #[test] + fn ecdsa_signing_session_completes_if_node_does_not_have_a_share() { + //::logger::init_log(); + let mut core = Core::new().unwrap(); + let clusters = make_clusters(&core, 6041, 4); + run_clusters(&clusters); + loop_until(&mut core, time::Duration::from_millis(300), || clusters.iter().all(all_connections_established)); + + // 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 + || session.state() == GenerationSessionState::Failed) + && clusters[0].client().generation_session(&SessionId::default()).is_none()); + assert!(session.joint_public_and_secret().unwrap().is_ok()); + + // now remove share from node2 + assert!((0..3).all(|i| clusters[i].data.sessions.generation_sessions.is_empty())); + clusters[2].data.config.key_storage.remove(&Default::default()).unwrap(); + + // and try to sign message with generated key + let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); + let session0 = clusters[0].client().new_ecdsa_signing_session(Default::default(), signature, None, H256::random()).unwrap(); + let session = clusters[0].data.sessions.ecdsa_signing_sessions.first().unwrap(); + + loop_until(&mut core, time::Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i| + clusters[i].data.sessions.ecdsa_signing_sessions.is_empty())); + session0.wait().unwrap(); + + // and try to sign message with generated key using node that has no key share + let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); + let session2 = clusters[2].client().new_ecdsa_signing_session(Default::default(), signature, None, H256::random()).unwrap(); + let session = clusters[2].data.sessions.ecdsa_signing_sessions.first().unwrap(); + loop_until(&mut core, time::Duration::from_millis(1000), || session.is_finished() && (0..3).all(|i| + clusters[i].data.sessions.ecdsa_signing_sessions.is_empty())); + session2.wait().unwrap(); + + // now remove share from node1 + clusters[1].data.config.key_storage.remove(&Default::default()).unwrap(); + + // and try to sign message with generated key + let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); + let session1 = clusters[0].client().new_ecdsa_signing_session(Default::default(), signature, None, H256::random()).unwrap(); + let session = clusters[0].data.sessions.ecdsa_signing_sessions.first().unwrap(); + loop_until(&mut core, time::Duration::from_millis(1000), || session.is_finished()); + session1.wait().unwrap_err(); + } } diff --git a/secret_store/src/key_server_cluster/cluster_sessions.rs b/secret_store/src/key_server_cluster/cluster_sessions.rs index ff3ccca8e..a1db7d919 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions.rs @@ -28,14 +28,16 @@ use key_server_cluster::message::{self, Message}; use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl}; use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl}; -use key_server_cluster::signing_session::{SessionImpl as SigningSessionImpl}; +use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSessionImpl}; +use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSessionImpl}; use key_server_cluster::share_add_session::{SessionImpl as ShareAddSessionImpl, IsolatedSessionTransport as ShareAddTransport}; use key_server_cluster::servers_set_change_session::{SessionImpl as ServersSetChangeSessionImpl}; use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, IsolatedSessionTransport as VersionNegotiationTransport}; -use key_server_cluster::cluster_sessions_creator::{GenerationSessionCreator, EncryptionSessionCreator, DecryptionSessionCreator, SigningSessionCreator, - KeyVersionNegotiationSessionCreator, AdminSessionCreator, SessionCreatorCore, ClusterSessionCreator}; +use key_server_cluster::cluster_sessions_creator::{GenerationSessionCreator, EncryptionSessionCreator, DecryptionSessionCreator, + SchnorrSigningSessionCreator, KeyVersionNegotiationSessionCreator, AdminSessionCreator, SessionCreatorCore, + EcdsaSigningSessionCreator, ClusterSessionCreator}; /// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, /// we must treat this session as stalled && finish it with an error. @@ -125,8 +127,10 @@ pub struct ClusterSessions { pub encryption_sessions: ClusterSessionsContainer, /// Decryption sessions. pub decryption_sessions: ClusterSessionsContainer, - /// Signing sessions. - pub signing_sessions: ClusterSessionsContainer, + /// Schnorr signing sessions. + pub schnorr_signing_sessions: ClusterSessionsContainer, + /// ECDSA signing sessions. + pub ecdsa_signing_sessions: ClusterSessionsContainer, /// Key version negotiation sessions. pub negotiation_sessions: ClusterSessionsContainer, KeyVersionNegotiationSessionCreator, ()>, /// Administrative sessions. @@ -203,7 +207,10 @@ impl ClusterSessions { decryption_sessions: ClusterSessionsContainer::new(DecryptionSessionCreator { core: creator_core.clone(), }, container_state.clone()), - signing_sessions: ClusterSessionsContainer::new(SigningSessionCreator { + schnorr_signing_sessions: ClusterSessionsContainer::new(SchnorrSigningSessionCreator { + core: creator_core.clone(), + }, container_state.clone()), + ecdsa_signing_sessions: ClusterSessionsContainer::new(EcdsaSigningSessionCreator { core: creator_core.clone(), }, container_state.clone()), negotiation_sessions: ClusterSessionsContainer::new(KeyVersionNegotiationSessionCreator { @@ -240,7 +247,8 @@ impl ClusterSessions { self.generation_sessions.stop_stalled_sessions(); self.encryption_sessions.stop_stalled_sessions(); self.decryption_sessions.stop_stalled_sessions(); - self.signing_sessions.stop_stalled_sessions(); + self.schnorr_signing_sessions.stop_stalled_sessions(); + self.ecdsa_signing_sessions.stop_stalled_sessions(); self.negotiation_sessions.stop_stalled_sessions(); self.admin_sessions.stop_stalled_sessions(); } @@ -250,7 +258,8 @@ 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); + self.schnorr_signing_sessions.on_connection_timeout(node_id); + self.ecdsa_signing_sessions.on_connection_timeout(node_id); self.negotiation_sessions.on_connection_timeout(node_id); self.admin_sessions.on_connection_timeout(node_id); self.creator_core.on_connection_timeout(node_id); diff --git a/secret_store/src/key_server_cluster/cluster_sessions_creator.rs b/secret_store/src/key_server_cluster/cluster_sessions_creator.rs index 90fb09edf..00ea08014 100644 --- a/secret_store/src/key_server_cluster/cluster_sessions_creator.rs +++ b/secret_store/src/key_server_cluster/cluster_sessions_creator.rs @@ -23,14 +23,16 @@ use key_server_cluster::{Error, NodeId, SessionId, AclStorage, KeyStorage, Docum use key_server_cluster::cluster::{Cluster, ClusterConfiguration}; use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; use key_server_cluster::cluster_sessions::{ClusterSession, SessionIdWithSubSession, AdminSession, AdminSessionCreationData}; -use key_server_cluster::message::{self, Message, DecryptionMessage, SigningMessage, ConsensusMessageOfShareAdd, - ShareAddMessage, ServersSetChangeMessage, ConsensusMessage, ConsensusMessageWithServersSet}; +use key_server_cluster::message::{self, Message, DecryptionMessage, SchnorrSigningMessage, ConsensusMessageOfShareAdd, + ShareAddMessage, ServersSetChangeMessage, ConsensusMessage, ConsensusMessageWithServersSet, EcdsaSigningMessage}; use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionParams as GenerationSessionParams}; use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, SessionParams as DecryptionSessionParams}; use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams}; -use key_server_cluster::signing_session::{SessionImpl as SigningSessionImpl, - SessionParams as SigningSessionParams}; +use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSessionImpl, + SessionParams as EcdsaSigningSessionParams}; +use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSessionImpl, + SessionParams as SchnorrSigningSessionParams}; use key_server_cluster::share_add_session::{SessionImpl as ShareAddSessionImpl, SessionParams as ShareAddSessionParams, IsolatedSessionTransport as ShareAddTransport}; use key_server_cluster::servers_set_change_session::{SessionImpl as ServersSetChangeSessionImpl, @@ -240,26 +242,26 @@ impl ClusterSessionCreator for DecryptionSessi } } -/// Signing session creator. -pub struct SigningSessionCreator { +/// Schnorr signing session creator. +pub struct SchnorrSigningSessionCreator { /// Creator core. pub core: Arc, } -impl ClusterSessionCreator for SigningSessionCreator { +impl ClusterSessionCreator for SchnorrSigningSessionCreator { fn creation_data_from_message(message: &Message) -> Result, Error> { match *message { - Message::Signing(SigningMessage::SigningConsensusMessage(ref message)) => match &message.message { + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message)) => match &message.message { &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requestor_signature.clone().into())), _ => Err(Error::InvalidMessage), }, - Message::Signing(SigningMessage::SigningSessionDelegation(ref message)) => Ok(Some(message.requestor_signature.clone().into())), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message)) => Ok(Some(message.requestor_signature.clone().into())), _ => Err(Error::InvalidMessage), } } fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::Signing(message::SigningMessage::SigningSessionError(message::SigningSessionError { + message::Message::SchnorrSigning(message::SchnorrSigningMessage::SchnorrSigningSessionError(message::SchnorrSigningSessionError { session: sid.id.into(), sub_session: sid.access_key.into(), session_nonce: nonce, @@ -267,10 +269,56 @@ impl ClusterSessionCreator for SigningSessionCrea })) } - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester_signature: Option) -> Result, Error> { + fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester_signature: Option) -> Result, Error> { let encrypted_data = self.core.read_key_share(&id.id)?; let nonce = self.core.check_session_nonce(&master, nonce)?; - Ok(Arc::new(SigningSessionImpl::new(SigningSessionParams { + Ok(Arc::new(SchnorrSigningSessionImpl::new(SchnorrSigningSessionParams { + meta: SessionMeta { + id: id.id, + self_node_id: self.core.self_node_id.clone(), + master_node_id: master, + threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), + }, + access_key: id.access_key, + key_share: encrypted_data, + acl_storage: self.core.acl_storage.clone(), + cluster: cluster, + nonce: nonce, + }, requester_signature)?)) + } +} + +/// ECDSA signing session creator. +pub struct EcdsaSigningSessionCreator { + /// Creator core. + pub core: Arc, +} + +impl ClusterSessionCreator for EcdsaSigningSessionCreator { + fn creation_data_from_message(message: &Message) -> Result, Error> { + match *message { + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message)) => match &message.message { + &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requestor_signature.clone().into())), + _ => Err(Error::InvalidMessage), + }, + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message)) => Ok(Some(message.requestor_signature.clone().into())), + _ => Err(Error::InvalidMessage), + } + } + + fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { + message::Message::EcdsaSigning(message::EcdsaSigningMessage::EcdsaSigningSessionError(message::EcdsaSigningSessionError { + session: sid.id.into(), + sub_session: sid.access_key.into(), + session_nonce: nonce, + error: err.into(), + })) + } + + fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester_signature: Option) -> Result, Error> { + let encrypted_data = self.core.read_key_share(&id.id)?; + let nonce = self.core.check_session_nonce(&master, nonce)?; + Ok(Arc::new(EcdsaSigningSessionImpl::new(EcdsaSigningSessionParams { meta: SessionMeta { id: id.id, self_node_id: self.core.self_node_id.clone(), @@ -407,7 +455,8 @@ impl IntoSessionId for Message { Message::Generation(ref message) => Ok(message.session_id().clone()), Message::Encryption(ref message) => Ok(message.session_id().clone()), Message::Decryption(_) => Err(Error::InvalidMessage), - Message::Signing(_) => Err(Error::InvalidMessage), + Message::SchnorrSigning(_) => Err(Error::InvalidMessage), + Message::EcdsaSigning(_) => Err(Error::InvalidMessage), Message::ServersSetChange(ref message) => Ok(message.session_id().clone()), Message::ShareAdd(ref message) => Ok(message.session_id().clone()), Message::KeyVersionNegotiation(_) => Err(Error::InvalidMessage), @@ -422,7 +471,8 @@ impl IntoSessionId for Message { Message::Generation(_) => Err(Error::InvalidMessage), Message::Encryption(_) => Err(Error::InvalidMessage), Message::Decryption(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::Signing(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), + Message::SchnorrSigning(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), + Message::EcdsaSigning(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), Message::ServersSetChange(_) => Err(Error::InvalidMessage), Message::ShareAdd(_) => Err(Error::InvalidMessage), Message::KeyVersionNegotiation(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), diff --git a/secret_store/src/key_server_cluster/io/message.rs b/secret_store/src/key_server_cluster/io/message.rs index d0fce230f..32f0fc54a 100644 --- a/secret_store/src/key_server_cluster/io/message.rs +++ b/secret_store/src/key_server_cluster/io/message.rs @@ -24,8 +24,8 @@ use ethkey::{Secret, KeyPair}; use ethkey::math::curve_order; use ethereum_types::{H256, U256}; use key_server_cluster::Error; -use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, - DecryptionMessage, SigningMessage, ServersSetChangeMessage, ShareAddMessage, KeyVersionNegotiationMessage}; +use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, + SchnorrSigningMessage, EcdsaSigningMessage, ServersSetChangeMessage, ShareAddMessage, KeyVersionNegotiationMessage}; /// Size of serialized header. pub const MESSAGE_HEADER_SIZE: usize = 18; @@ -90,14 +90,20 @@ pub fn serialize_message(message: Message) -> Result { Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(payload)) => (156, 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(SigningMessage::SigningSessionDelegation(payload)) => (206, serde_json::to_vec(&payload)), - Message::Signing(SigningMessage::SigningSessionDelegationCompleted(payload)) => (207, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(payload)) + => (200, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(payload)) + => (201, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(payload)) + => (202, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(payload)) => (203, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(payload)) => (204, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(payload)) + => (205, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(payload)) + => (206, serde_json::to_vec(&payload)), + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(payload)) + => (207, serde_json::to_vec(&payload)), Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(payload)) => (250, serde_json::to_vec(&payload)), @@ -130,6 +136,23 @@ pub fn serialize_message(message: Message) -> Result { => (451, serde_json::to_vec(&payload)), Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(payload)) => (452, serde_json::to_vec(&payload)), + + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(payload)) => (500, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(payload)) + => (501, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(payload)) + => (502, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(payload)) + => (503, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(payload)) + => (504, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(payload)) => (505, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(payload)) => (506, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(payload)) => (507, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(payload)) => (508, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(payload)) => (509, serde_json::to_vec(&payload)), + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(payload)) + => (510, serde_json::to_vec(&payload)), }; let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; @@ -168,14 +191,14 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 156 => Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(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()))?)), - 206 => Message::Signing(SigningMessage::SigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 207 => Message::Signing(SigningMessage::SigningSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 200 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 201 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 202 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 203 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 204 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 205 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 206 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 207 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 250 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 251 => Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), @@ -198,6 +221,18 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 452 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 500 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 501 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 502 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 503 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 504 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 505 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 506 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 507 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 508 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 509 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 510 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(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/jobs/mod.rs b/secret_store/src/key_server_cluster/jobs/mod.rs index 63eb57786..817f09b71 100644 --- a/secret_store/src/key_server_cluster/jobs/mod.rs +++ b/secret_store/src/key_server_cluster/jobs/mod.rs @@ -20,5 +20,6 @@ pub mod dummy_job; pub mod job_session; pub mod key_access_job; pub mod servers_set_change_access_job; -pub mod signing_job; +pub mod signing_job_ecdsa; +pub mod signing_job_schnorr; pub mod unknown_sessions_job; diff --git a/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs b/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs new file mode 100644 index 000000000..561a8e0f8 --- /dev/null +++ b/secret_store/src/key_server_cluster/jobs/signing_job_ecdsa.rs @@ -0,0 +1,151 @@ +// 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, Signature}; +use ethereum_types::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 EcdsaSigningJob { + /// Key share. + key_share: DocumentKeyShare, + /// Key version. + key_version: H256, + /// Share of inv(nonce). + inv_nonce_share: Secret, + /// Nonce public. + nonce_public: Public, + /// Request id. + request_id: Option, + /// + inversed_nonce_coeff: Option, + /// Message hash. + message_hash: Option, +} + +/// Signing job partial request. +pub struct EcdsaPartialSigningRequest { + /// Request id. + pub id: Secret, + /// + pub inversed_nonce_coeff: Secret, + /// Message hash to sign. + pub message_hash: H256, +} + +/// Signing job partial response. +#[derive(Clone)] +pub struct EcdsaPartialSigningResponse { + /// Request id. + pub request_id: Secret, + /// Partial signature' s share. + pub partial_signature_s: Secret, +} + +impl EcdsaSigningJob { + pub fn new_on_slave(key_share: DocumentKeyShare, key_version: H256, nonce_public: Public, inv_nonce_share: Secret) -> Result { + Ok(EcdsaSigningJob { + key_share: key_share, + key_version: key_version, + nonce_public: nonce_public, + inv_nonce_share: inv_nonce_share, + request_id: None, + inversed_nonce_coeff: None, + message_hash: None, + }) + } + + pub fn new_on_master(key_share: DocumentKeyShare, key_version: H256, nonce_public: Public, inv_nonce_share: Secret, inversed_nonce_coeff: Secret, message_hash: H256) -> Result { + Ok(EcdsaSigningJob { + key_share: key_share, + key_version: key_version, + nonce_public: nonce_public, + inv_nonce_share: inv_nonce_share, + request_id: Some(math::generate_random_scalar()?), + inversed_nonce_coeff: Some(inversed_nonce_coeff), + message_hash: Some(message_hash), + }) + } +} + +impl JobExecutor for EcdsaSigningJob { + type PartialJobRequest = EcdsaPartialSigningRequest; + type PartialJobResponse = EcdsaPartialSigningResponse; + type JobResponse = Signature; + + fn prepare_partial_request(&self, _node: &NodeId, nodes: &BTreeSet) -> Result { + debug_assert!(nodes.len() == self.key_share.threshold * 2 + 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 inversed_nonce_coeff = self.inversed_nonce_coeff.as_ref() + .expect("prepare_partial_request is only called on master nodes; inversed_nonce_coeff 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"); + + Ok(EcdsaPartialSigningRequest { + id: request_id.clone(), + inversed_nonce_coeff: inversed_nonce_coeff.clone(), + message_hash: message_hash.clone(), + }) + } + + fn process_partial_request(&mut self, partial_request: EcdsaPartialSigningRequest) -> Result, Error> { + let inversed_nonce_coeff_mul_nonce = math::compute_secret_mul(&partial_request.inversed_nonce_coeff, &self.inv_nonce_share)?; + let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; + let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; + let inv_nonce_mul_secret = math::compute_secret_mul(&inversed_nonce_coeff_mul_nonce, &key_version.secret_share)?; + let partial_signature_s = math::compute_ecdsa_s_share( + &inversed_nonce_coeff_mul_nonce, + &inv_nonce_mul_secret, + &signature_r, + &math::to_scalar(partial_request.message_hash)?, + )?; + + Ok(JobPartialRequestAction::Respond(EcdsaPartialSigningResponse { + request_id: partial_request.id, + partial_signature_s: partial_signature_s, + })) + } + + fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &EcdsaPartialSigningResponse) -> Result { + if Some(&partial_response.request_id) != self.request_id.as_ref() { + return Ok(JobPartialResponseAction::Ignore); + } + // TODO [Trust]: check_ecdsa_signature_share() + + Ok(JobPartialResponseAction::Accept) + } + + fn compute_response(&self, partial_responses: &BTreeMap) -> Result { + let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; + if partial_responses.keys().any(|n| !key_version.id_numbers.contains_key(n)) { + return Err(Error::InvalidMessage); + } + + let id_numbers: Vec<_> = partial_responses.keys().map(|n| key_version.id_numbers[n].clone()).collect(); + let signature_s_shares: Vec<_> = partial_responses.values().map(|r| r.partial_signature_s.clone()).collect(); + let signature_s = math::compute_ecdsa_s(self.key_share.threshold, &signature_s_shares, &id_numbers)?; + let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; + + let signature = math::serialize_ecdsa_signature(&self.nonce_public, signature_r, signature_s); + + Ok(signature) + } +} diff --git a/secret_store/src/key_server_cluster/jobs/signing_job.rs b/secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs similarity index 81% rename from secret_store/src/key_server_cluster/jobs/signing_job.rs rename to secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs index fc6903196..6246a728d 100644 --- a/secret_store/src/key_server_cluster/jobs/signing_job.rs +++ b/secret_store/src/key_server_cluster/jobs/signing_job_schnorr.rs @@ -22,7 +22,7 @@ use key_server_cluster::math; use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; /// Signing job. -pub struct SigningJob { +pub struct SchnorrSigningJob { /// This node id. self_node_id: NodeId, /// Key share. @@ -40,7 +40,7 @@ pub struct SigningJob { } /// Signing job partial request. -pub struct PartialSigningRequest { +pub struct SchnorrPartialSigningRequest { /// Request id. pub id: Secret, /// Message hash. @@ -51,16 +51,16 @@ pub struct PartialSigningRequest { /// Signing job partial response. #[derive(Clone)] -pub struct PartialSigningResponse { +pub struct SchnorrPartialSigningResponse { /// Request id. pub request_id: Secret, /// Partial signature. pub partial_signature: Secret, } -impl SigningJob { +impl SchnorrSigningJob { pub fn new_on_slave(self_node_id: NodeId, key_share: DocumentKeyShare, key_version: H256, session_public: Public, session_secret_coeff: Secret) -> Result { - Ok(SigningJob { + Ok(SchnorrSigningJob { self_node_id: self_node_id, key_share: key_share, key_version: key_version, @@ -72,7 +72,7 @@ impl SigningJob { } pub fn new_on_master(self_node_id: NodeId, key_share: DocumentKeyShare, key_version: H256, session_public: Public, session_secret_coeff: Secret, message_hash: H256) -> Result { - Ok(SigningJob { + Ok(SchnorrSigningJob { self_node_id: self_node_id, key_share: key_share, key_version: key_version, @@ -84,12 +84,12 @@ impl SigningJob { } } -impl JobExecutor for SigningJob { - type PartialJobRequest = PartialSigningRequest; - type PartialJobResponse = PartialSigningResponse; +impl JobExecutor for SchnorrSigningJob { + type PartialJobRequest = SchnorrPartialSigningRequest; + type PartialJobResponse = SchnorrPartialSigningResponse; type JobResponse = (Secret, Secret); - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> 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() @@ -99,14 +99,14 @@ impl JobExecutor for SigningJob { let mut other_nodes_ids = nodes.clone(); other_nodes_ids.remove(node); - Ok(PartialSigningRequest { + Ok(SchnorrPartialSigningRequest { id: request_id.clone(), message_hash: message_hash.clone(), other_nodes_ids: other_nodes_ids, }) } - fn process_partial_request(&mut self, partial_request: PartialSigningRequest) -> Result, Error> { + fn process_partial_request(&mut self, partial_request: SchnorrPartialSigningRequest) -> Result, Error> { let key_version = self.key_share.version(&self.key_version).map_err(|e| Error::KeyStorage(e.into()))?; if partial_request.other_nodes_ids.len() != self.key_share.threshold || partial_request.other_nodes_ids.contains(&self.self_node_id) @@ -117,9 +117,9 @@ impl JobExecutor for SigningJob { let self_id_number = &key_version.id_numbers[&self.self_node_id]; let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &key_version.id_numbers[n]); let combined_hash = math::combine_message_hash_with_public(&partial_request.message_hash, &self.session_public)?; - Ok(JobPartialRequestAction::Respond(PartialSigningResponse { + Ok(JobPartialRequestAction::Respond(SchnorrPartialSigningResponse { request_id: partial_request.id, - partial_signature: math::compute_signature_share( + partial_signature: math::compute_schnorr_signature_share( self.key_share.threshold, &combined_hash, &self.session_secret_coeff, @@ -130,22 +130,22 @@ impl JobExecutor for SigningJob { })) } - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &PartialSigningResponse) -> Result { + fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &SchnorrPartialSigningResponse) -> Result { if Some(&partial_response.request_id) != self.request_id.as_ref() { return Ok(JobPartialResponseAction::Ignore); } - // TODO [Trust]: check_signature_share() + // TODO [Trust]: check_schnorr_signature_share() Ok(JobPartialResponseAction::Accept) } - fn compute_response(&self, partial_responses: &BTreeMap) -> Result<(Secret, Secret), Error> { + 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))?; + let signature_s = math::compute_schnorr_signature(partial_responses.values().map(|r| &r.partial_signature))?; Ok((signature_c, signature_s)) } -} +} \ No newline at end of file diff --git a/secret_store/src/key_server_cluster/math.rs b/secret_store/src/key_server_cluster/math.rs index 178033762..78c444822 100644 --- a/secret_store/src/key_server_cluster/math.rs +++ b/secret_store/src/key_server_cluster/math.rs @@ -14,11 +14,10 @@ // 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 ethkey::{Public, Secret, Signature, Random, Generator, math}; use ethereum_types::{H256, U256}; use hash::keccak; use key_server_cluster::Error; -#[cfg(test)] use ethkey::Signature; /// Encryption result. #[derive(Debug)] @@ -30,7 +29,6 @@ pub struct EncryptedSecret { } /// Create zero scalar. -#[cfg(test)] pub fn zero_scalar() -> Secret { Secret::zero() } @@ -55,13 +53,11 @@ pub fn generate_random_point() -> Result { } /// Get X coordinate of point. -#[cfg(test)] fn public_x(public: &Public) -> H256 { public[0..32].into() } /// Get Y coordinate of point. -#[cfg(test)] fn public_y(public: &Public) -> H256 { public[32..64].into() } @@ -84,6 +80,13 @@ pub fn compute_secret_sum<'a, I>(mut secrets: I) -> Result where Ok(sum) } +/// Compute secrets multiplication. +pub fn compute_secret_mul(secret1: &Secret, secret2: &Secret) -> Result { + let mut secret_mul = secret1.clone(); + secret_mul.mul(secret2)?; + Ok(secret_mul) +} + /// 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 @@ -227,7 +230,6 @@ pub fn compute_joint_secret<'a, I>(secret_coeffs: I) -> Result wh } /// Compute joint secret key from t+1 secret shares. -#[cfg(test)] pub fn compute_joint_secret_from_shares<'a>(t: usize, secret_shares: &[&'a Secret], id_numbers: &[&'a Secret]) -> Result { let secret_share_0 = secret_shares[0]; let id_number_0 = id_numbers[0]; @@ -374,7 +376,7 @@ pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> } /// Compute Schnorr signature share. -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) +pub fn compute_schnorr_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)?; @@ -388,7 +390,7 @@ pub fn compute_signature_share<'a, I>(threshold: usize, combined_hash: &Secret, } /// Check Schnorr signature share. -pub fn _check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_numbers: I) +pub fn _check_schnorr_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 [Trust]: in paper partial signature is checked using comparison: // sig[i] * T = r[i] - c * lagrange_coeff(i) * y[i] @@ -408,13 +410,13 @@ pub fn _check_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: } /// Compute Schnorr signature. -pub fn compute_signature<'a, I>(signature_shares: I) -> Result where I: Iterator { +pub fn compute_schnorr_signature<'a, I>(signature_shares: I) -> Result where I: Iterator { compute_secret_sum(signature_shares) } /// Locally compute Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Signing. #[cfg(test)] -pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &Secret) -> Result<(Secret, Secret), Error> { +pub fn local_compute_schnorr_signature(nonce: &Secret, secret: &Secret, message_hash: &Secret) -> Result<(Secret, Secret), Error> { let mut nonce_public = math::generation_point(); math::public_mul_secret(&mut nonce_public, &nonce).unwrap(); @@ -430,7 +432,7 @@ pub fn local_compute_signature(nonce: &Secret, secret: &Secret, message_hash: &S /// Verify Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. #[cfg(test)] -pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { +pub fn verify_schnorr_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(); @@ -442,13 +444,11 @@ pub fn verify_signature(public: &Public, signature: &(Secret, Secret), message_h } /// Compute R part of ECDSA signature. -#[cfg(test)] pub fn compute_ecdsa_r(nonce_public: &Public) -> Result { to_scalar(public_x(nonce_public)) } /// Compute share of S part of ECDSA signature. -#[cfg(test)] pub fn compute_ecdsa_s_share(inv_nonce_share: &Secret, inv_nonce_mul_secret: &Secret, signature_r: &Secret, message_hash: &Secret) -> Result { let mut nonce_inv_share_mul_message_hash = inv_nonce_share.clone(); nonce_inv_share_mul_message_hash.mul(&message_hash.clone().into())?; @@ -463,7 +463,6 @@ pub fn compute_ecdsa_s_share(inv_nonce_share: &Secret, inv_nonce_mul_secret: &Se } /// Compute S part of ECDSA signature from shares. -#[cfg(test)] pub fn compute_ecdsa_s(t: usize, signature_s_shares: &[Secret], id_numbers: &[Secret]) -> Result { let double_t = t * 2; debug_assert!(id_numbers.len() >= double_t + 1); @@ -475,9 +474,8 @@ pub fn compute_ecdsa_s(t: usize, signature_s_shares: &[Secret], id_numbers: &[Se } /// Serialize ECDSA signature to [r][s]v form. -#[cfg(test)] pub fn serialize_ecdsa_signature(nonce_public: &Public, signature_r: Secret, mut signature_s: Secret) -> Signature { - // compute recvery param + // compute recovery param let mut signature_v = { let nonce_public_x = public_x(nonce_public); let nonce_public_y: U256 = public_y(nonce_public).into(); @@ -507,7 +505,6 @@ pub fn serialize_ecdsa_signature(nonce_public: &Public, signature_r: Secret, mut } /// Compute share of ECDSA reversed-nonce coefficient. Result of this_coeff * secret_share gives us a share of inv(nonce). -#[cfg(test)] pub fn compute_ecdsa_inversed_secret_coeff_share(secret_share: &Secret, nonce_share: &Secret, zero_share: &Secret) -> Result { let mut coeff = secret_share.clone(); coeff.mul(nonce_share).unwrap(); @@ -516,7 +513,6 @@ pub fn compute_ecdsa_inversed_secret_coeff_share(secret_share: &Secret, nonce_sh } /// Compute ECDSA reversed-nonce coefficient from its shares. Result of this_coeff * secret_share gives us a share of inv(nonce). -#[cfg(test)] pub fn compute_ecdsa_inversed_secret_coeff_from_shares(t: usize, id_numbers: &[Secret], shares: &[Secret]) -> Result { debug_assert_eq!(shares.len(), 2 * t + 1); debug_assert_eq!(shares.len(), id_numbers.len()); @@ -805,8 +801,8 @@ pub mod tests { 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)); + let signature = local_compute_schnorr_signature(&nonce, key_pair.secret(), &message_hash).unwrap(); + assert_eq!(verify_schnorr_signature(key_pair.public(), &signature, &message_hash), Ok(true)); } #[test] @@ -837,7 +833,7 @@ pub mod tests { // step 3: compute signature shares let partial_signatures: Vec<_> = (0..n) - .map(|i| compute_signature_share( + .map(|i| compute_schnorr_signature_share( t, &combined_hash, &one_time_artifacts.polynoms1[i][0], @@ -857,7 +853,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_schnorr_signature_share(&combined_hash, &signature_share, &artifacts.public_shares[j], &one_time_artifacts.public_shares[j], @@ -869,16 +865,16 @@ pub mod tests { // step 5: compute signature let signatures: Vec<_> = (0..n) - .map(|i| (combined_hash.clone(), compute_signature(received_signatures[i].iter().chain(once(&partial_signatures[i]))).unwrap())) + .map(|i| (combined_hash.clone(), compute_schnorr_signature(received_signatures[i].iter().chain(once(&partial_signatures[i]))).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(); + let local_signature = local_compute_schnorr_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!(verify_schnorr_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 de435ab95..e0675af24 100644 --- a/secret_store/src/key_server_cluster/message.rs +++ b/secret_store/src/key_server_cluster/message.rs @@ -34,8 +34,10 @@ pub enum Message { Encryption(EncryptionMessage), /// Decryption message. Decryption(DecryptionMessage), - /// Signing message. - Signing(SigningMessage), + /// Schnorr signing message. + SchnorrSigning(SchnorrSigningMessage), + /// ECDSA signing message. + EcdsaSigning(EcdsaSigningMessage), /// Key version negotiation message. KeyVersionNegotiation(KeyVersionNegotiationMessage), /// Share add message. @@ -133,25 +135,52 @@ pub enum DecryptionMessage { DecryptionSessionDelegationCompleted(DecryptionSessionDelegationCompleted), } -/// All possible messages that can be sent during signing session. +/// All possible messages that can be sent during Schnorr signing session. #[derive(Clone, Debug)] -pub enum SigningMessage { +pub enum SchnorrSigningMessage { /// Consensus establishing message. - SigningConsensusMessage(SigningConsensusMessage), + SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage), /// Session key generation message. - SigningGenerationMessage(SigningGenerationMessage), + SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage), /// Request partial signature from node. - RequestPartialSignature(RequestPartialSignature), + SchnorrRequestPartialSignature(SchnorrRequestPartialSignature), /// Partial signature is generated. - PartialSignature(PartialSignature), + SchnorrPartialSignature(SchnorrPartialSignature), /// Signing error occured. - SigningSessionError(SigningSessionError), + SchnorrSigningSessionError(SchnorrSigningSessionError), /// Signing session completed. - SigningSessionCompleted(SigningSessionCompleted), + SchnorrSigningSessionCompleted(SchnorrSigningSessionCompleted), /// When signing session is delegated to another node. - SigningSessionDelegation(SigningSessionDelegation), + SchnorrSigningSessionDelegation(SchnorrSigningSessionDelegation), /// When delegated signing session is completed. - SigningSessionDelegationCompleted(SigningSessionDelegationCompleted), + SchnorrSigningSessionDelegationCompleted(SchnorrSigningSessionDelegationCompleted), +} + +/// All possible messages that can be sent during ECDSA signing session. +#[derive(Clone, Debug)] +pub enum EcdsaSigningMessage { + /// Consensus establishing message. + EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage), + /// Signature nonce generation message. + EcdsaSignatureNonceGenerationMessage(EcdsaSignatureNonceGenerationMessage), + /// Inversion nonce generation message. + EcdsaInversionNonceGenerationMessage(EcdsaInversionNonceGenerationMessage), + /// Inversion zero generation message. + EcdsaInversionZeroGenerationMessage(EcdsaInversionZeroGenerationMessage), + /// Inversed nonce coefficient share. + EcdsaSigningInversedNonceCoeffShare(EcdsaSigningInversedNonceCoeffShare), + /// Request partial signature from node. + EcdsaRequestPartialSignature(EcdsaRequestPartialSignature), + /// Partial signature is generated. + EcdsaPartialSignature(EcdsaPartialSignature), + /// Signing error occured. + EcdsaSigningSessionError(EcdsaSigningSessionError), + /// Signing session completed. + EcdsaSigningSessionCompleted(EcdsaSigningSessionCompleted), + /// When signing session is delegated to another node. + EcdsaSigningSessionDelegation(EcdsaSigningSessionDelegation), + /// When delegated signing session is completed. + EcdsaSigningSessionDelegationCompleted(EcdsaSigningSessionDelegationCompleted), } /// All possible messages that can be sent during servers set change session. @@ -246,6 +275,8 @@ pub struct InitializeSession { pub author: SerializablePublic, /// All session participants along with their identification numbers. pub nodes: BTreeMap, + /// Is zero secret generation session? + pub is_zero: bool, /// Decryption threshold. During decryption threshold-of-route.len() nodes must came to /// consensus to successfully decrypt message. pub threshold: usize, @@ -407,9 +438,9 @@ pub struct InitializeConsensusSessionOfShareAdd { pub new_set_signature: SerializableSignature, } -/// Consensus-related signing message. +/// Consensus-related Schnorr signing message. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SigningConsensusMessage { +pub struct SchnorrSigningConsensusMessage { /// Generation session Id. pub session: MessageSessionId, /// Signing session Id. @@ -422,7 +453,7 @@ pub struct SigningConsensusMessage { /// Session key generation message. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SigningGenerationMessage { +pub struct SchnorrSigningGenerationMessage { /// Generation session Id. pub session: MessageSessionId, /// Signing session Id. @@ -433,9 +464,9 @@ pub struct SigningGenerationMessage { pub message: GenerationMessage, } -/// Request partial signature. +/// Request partial Schnorr signature. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RequestPartialSignature { +pub struct SchnorrRequestPartialSignature { /// Generation session Id. pub session: MessageSessionId, /// Signing session Id. @@ -450,9 +481,9 @@ pub struct RequestPartialSignature { pub nodes: BTreeSet, } -/// Partial signature. +/// Partial Schnorr signature. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PartialSignature { +pub struct SchnorrPartialSignature { /// Generation session Id. pub session: MessageSessionId, /// Signing session Id. @@ -465,9 +496,9 @@ pub struct PartialSignature { pub partial_signature: SerializableSecret, } -/// When signing session error has occured. +/// When Schnorr signing session error has occured. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SigningSessionError { +pub struct SchnorrSigningSessionError { /// Encryption session Id. pub session: MessageSessionId, /// Signing session Id. @@ -478,9 +509,9 @@ pub struct SigningSessionError { pub error: String, } -/// Signing session completed. +/// Schnorr signing session completed. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SigningSessionCompleted { +pub struct SchnorrSigningSessionCompleted { /// Generation session Id. pub session: MessageSessionId, /// Signing session Id. @@ -489,9 +520,9 @@ pub struct SigningSessionCompleted { pub session_nonce: u64, } -/// When signing session is delegated to another node. +/// When Schnorr signing session is delegated to another node. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SigningSessionDelegation { +pub struct SchnorrSigningSessionDelegation { /// Encryption session Id. pub session: MessageSessionId, /// Decryption session Id. @@ -506,9 +537,9 @@ pub struct SigningSessionDelegation { pub message_hash: SerializableH256, } -/// When delegated signing session is completed. +/// When delegated Schnorr signing session is completed. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SigningSessionDelegationCompleted { +pub struct SchnorrSigningSessionDelegationCompleted { /// Encryption session Id. pub session: MessageSessionId, /// Decryption session Id. @@ -521,6 +552,157 @@ pub struct SigningSessionDelegationCompleted { pub signature_c: SerializableSecret, } +/// Consensus-related ECDSA signing message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSigningConsensusMessage { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Consensus message. + pub message: ConsensusMessage, +} + +/// ECDSA signature nonce generation message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSignatureNonceGenerationMessage { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Generation message. + pub message: GenerationMessage, +} + +/// ECDSA inversion nonce generation message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaInversionNonceGenerationMessage { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Generation message. + pub message: GenerationMessage, +} + +/// ECDSA inversed nonce share message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSigningInversedNonceCoeffShare { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Inversed nonce coefficient share. + pub inversed_nonce_coeff_share: SerializableSecret, +} + +/// ECDSA inversion zero generation message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaInversionZeroGenerationMessage { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Generation message. + pub message: GenerationMessage, +} + +/// Request partial ECDSA signature. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaRequestPartialSignature { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Request id. + pub request_id: SerializableSecret, + /// + pub inversed_nonce_coeff: SerializableSecret, + /// Message hash. + pub message_hash: SerializableMessageHash, +} + +/// Partial ECDSA signature. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaPartialSignature { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Request id. + pub request_id: SerializableSecret, + /// Partial S part of signature. + pub partial_signature_s: SerializableSecret, +} + +/// When ECDSA signing session error has occured. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSigningSessionError { + /// Encryption session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Error message. + pub error: String, +} + +/// ECDSA signing session completed. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSigningSessionCompleted { + /// Generation session Id. + pub session: MessageSessionId, + /// Signing session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, +} + +/// When ECDSA signing session is delegated to another node. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSigningSessionDelegation { + /// Encryption session Id. + pub session: MessageSessionId, + /// Decryption session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Requestor signature. + pub requestor_signature: SerializableSignature, + /// Key version. + pub version: SerializableH256, + /// Message hash. + pub message_hash: SerializableH256, +} + +/// When delegated ECDSA signing session is completed. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EcdsaSigningSessionDelegationCompleted { + /// Encryption session Id. + pub session: MessageSessionId, + /// Decryption session Id. + pub sub_session: SerializableSecret, + /// Session-level nonce. + pub session_nonce: u64, + /// Signature. + pub signature: SerializableSignature, +} + /// Consensus-related decryption message. #[derive(Clone, Debug, Serialize, Deserialize)] pub struct DecryptionConsensusMessage { @@ -861,7 +1043,11 @@ impl Message { ConsensusMessage::InitializeConsensusSession(_) => true, _ => false }, - Message::Signing(SigningMessage::SigningConsensusMessage(ref msg)) => match msg.message { + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg)) => match msg.message { + ConsensusMessage::InitializeConsensusSession(_) => true, + _ => false + }, + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg)) => match msg.message { ConsensusMessage::InitializeConsensusSession(_) => true, _ => false }, @@ -881,7 +1067,8 @@ impl Message { pub fn is_delegation_message(&self) -> bool { match *self { Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(_)) => true, - Message::Signing(SigningMessage::SigningSessionDelegation(_)) => true, + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(_)) => true, + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(_)) => true, _ => false, } } @@ -891,7 +1078,8 @@ impl Message { Message::Generation(GenerationMessage::SessionError(_)) => true, Message::Encryption(EncryptionMessage::EncryptionSessionError(_)) => true, Message::Decryption(DecryptionMessage::DecryptionSessionError(_)) => true, - Message::Signing(SigningMessage::SigningConsensusMessage(_)) => true, + Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(_)) => true, + Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(_)) => true, Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(_)) => true, Message::ShareAdd(ShareAddMessage::ShareAddError(_)) => true, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(_)) => true, @@ -912,7 +1100,8 @@ impl Message { Message::Generation(ref message) => Some(message.session_nonce()), Message::Encryption(ref message) => Some(message.session_nonce()), Message::Decryption(ref message) => Some(message.session_nonce()), - Message::Signing(ref message) => Some(message.session_nonce()), + Message::SchnorrSigning(ref message) => Some(message.session_nonce()), + Message::EcdsaSigning(ref message) => Some(message.session_nonce()), Message::ShareAdd(ref message) => Some(message.session_nonce()), Message::ServersSetChange(ref message) => Some(message.session_nonce()), Message::KeyVersionNegotiation(ref message) => Some(message.session_nonce()), @@ -1002,43 +1191,93 @@ impl DecryptionMessage { } } -impl SigningMessage { +impl SchnorrSigningMessage { pub fn session_id(&self) -> &SessionId { 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, - SigningMessage::SigningSessionDelegation(ref msg) => &msg.session, - SigningMessage::SigningSessionDelegationCompleted(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => &msg.session, + SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => &msg.session, } } pub fn sub_session_id(&self) -> &Secret { 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, - SigningMessage::SigningSessionDelegation(ref msg) => &msg.sub_session, - SigningMessage::SigningSessionDelegationCompleted(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => &msg.sub_session, + SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => &msg.sub_session, } } pub fn session_nonce(&self) -> u64 { match *self { - SigningMessage::SigningConsensusMessage(ref msg) => msg.session_nonce, - SigningMessage::SigningGenerationMessage(ref msg) => msg.session_nonce, - SigningMessage::RequestPartialSignature(ref msg) => msg.session_nonce, - SigningMessage::PartialSignature(ref msg) => msg.session_nonce, - SigningMessage::SigningSessionError(ref msg) => msg.session_nonce, - SigningMessage::SigningSessionCompleted(ref msg) => msg.session_nonce, - SigningMessage::SigningSessionDelegation(ref msg) => msg.session_nonce, - SigningMessage::SigningSessionDelegationCompleted(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => msg.session_nonce, + SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => msg.session_nonce, + } + } +} + +impl EcdsaSigningMessage { + pub fn session_id(&self) -> &SessionId { + match *self { + EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => &msg.session, + EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => &msg.session, + } + } + + pub fn sub_session_id(&self) -> &Secret { + match *self { + EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => &msg.sub_session, + EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => &msg.sub_session, + } + } + + pub fn session_nonce(&self) -> u64 { + match *self { + EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => msg.session_nonce, + EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => msg.session_nonce, } } } @@ -1130,7 +1369,8 @@ 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), + Message::SchnorrSigning(ref message) => write!(f, "SchnorrSigning.{}", message), + Message::EcdsaSigning(ref message) => write!(f, "EcdsaSigning.{}", message), Message::ServersSetChange(ref message) => write!(f, "ServersSetChange.{}", message), Message::ShareAdd(ref message) => write!(f, "ShareAdd.{}", message), Message::KeyVersionNegotiation(ref message) => write!(f, "KeyVersionNegotiation.{}", message), @@ -1214,17 +1454,35 @@ impl fmt::Display for DecryptionMessage { } } -impl fmt::Display for SigningMessage { +impl fmt::Display for SchnorrSigningMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 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"), - SigningMessage::SigningSessionDelegation(_) => write!(f, "SigningSessionDelegation"), - SigningMessage::SigningSessionDelegationCompleted(_) => write!(f, "SigningSessionDelegationCompleted"), + SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref m) => write!(f, "SchnorrSigningConsensusMessage.{}", m.message), + SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref m) => write!(f, "SchnorrSigningGenerationMessage.{}", m.message), + SchnorrSigningMessage::SchnorrRequestPartialSignature(_) => write!(f, "SchnorrRequestPartialSignature"), + SchnorrSigningMessage::SchnorrPartialSignature(_) => write!(f, "SchnorrPartialSignature"), + SchnorrSigningMessage::SchnorrSigningSessionError(_) => write!(f, "SchnorrSigningSessionError"), + SchnorrSigningMessage::SchnorrSigningSessionCompleted(_) => write!(f, "SchnorrSigningSessionCompleted"), + SchnorrSigningMessage::SchnorrSigningSessionDelegation(_) => write!(f, "SchnorrSigningSessionDelegation"), + SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(_) => write!(f, "SchnorrSigningSessionDelegationCompleted"), + } + } +} + +impl fmt::Display for EcdsaSigningMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref m) => write!(f, "EcdsaSigningConsensusMessage.{}", m.message), + EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref m) => write!(f, "EcdsaSignatureNonceGenerationMessage.{}", m.message), + EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref m) => write!(f, "EcdsaInversionNonceGenerationMessage.{}", m.message), + EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref m) => write!(f, "EcdsaInversionZeroGenerationMessage.{}", m.message), + EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(_) => write!(f, "EcdsaSigningInversedNonceCoeffShare"), + EcdsaSigningMessage::EcdsaRequestPartialSignature(_) => write!(f, "EcdsaRequestPartialSignature"), + EcdsaSigningMessage::EcdsaPartialSignature(_) => write!(f, "EcdsaPartialSignature"), + EcdsaSigningMessage::EcdsaSigningSessionError(_) => write!(f, "EcdsaSigningSessionError"), + EcdsaSigningMessage::EcdsaSigningSessionCompleted(_) => write!(f, "EcdsaSigningSessionCompleted"), + EcdsaSigningMessage::EcdsaSigningSessionDelegation(_) => write!(f, "EcdsaSigningSessionDelegation"), + EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(_) => write!(f, "EcdsaSigningSessionDelegationCompleted"), } } } diff --git a/secret_store/src/key_server_cluster/mod.rs b/secret_store/src/key_server_cluster/mod.rs index feafd442c..5f5cb2bb9 100644 --- a/secret_store/src/key_server_cluster/mod.rs +++ b/secret_store/src/key_server_cluster/mod.rs @@ -182,7 +182,8 @@ pub use self::admin_sessions::share_change_session; pub use self::client_sessions::decryption_session; pub use self::client_sessions::encryption_session; pub use self::client_sessions::generation_session; -pub use self::client_sessions::signing_session; +pub use self::client_sessions::signing_session_ecdsa; +pub use self::client_sessions::signing_session_schnorr; mod cluster; mod cluster_sessions; diff --git a/secret_store/src/listener/http_listener.rs b/secret_store/src/listener/http_listener.rs index 61595e150..4d34f984b 100644 --- a/secret_store/src/listener/http_listener.rs +++ b/secret_store/src/listener/http_listener.rs @@ -38,7 +38,8 @@ use types::all::{Error, Public, MessageHash, NodeAddress, RequestSignature, Serv /// 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} +/// To generate Schnorr signature with server key: GET /schnorr/{server_key_id}/{signature}/{message_hash} +/// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash} /// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids pub struct KeyServerHttpListener { @@ -61,8 +62,10 @@ enum Request { GetDocumentKey(ServerKeyId, RequestSignature), /// Request shadow of encryption key of given document for given requestor. GetDocumentKeyShadow(ServerKeyId, RequestSignature), - /// Sign message. - SignMessage(ServerKeyId, RequestSignature, MessageHash), + /// Generate Schnorr signature for the message. + SchnorrSignMessage(ServerKeyId, RequestSignature, MessageHash), + /// Generate ECDSA signature for the message. + EcdsaSignMessage(ServerKeyId, RequestSignature, MessageHash), /// Change servers set. ChangeServersSet(RequestSignature, RequestSignature, BTreeSet), } @@ -159,10 +162,17 @@ impl HttpHandler for KeyServerHttpHandler { err })); }, - Request::SignMessage(document, signature, message_hash) => { - return_message_signature(req, res, self.handler.key_server.sign_message(&document, &signature, message_hash) + Request::SchnorrSignMessage(document, signature, message_hash) => { + return_message_signature(req, res, self.handler.key_server.sign_message_schnorr(&document, &signature, message_hash) .map_err(|err| { - warn!(target: "secretstore", "SignMessage request {} has failed with: {}", req_uri, err); + warn!(target: "secretstore", "SchnorrSignMessage request {} has failed with: {}", req_uri, err); + err + })); + }, + Request::EcdsaSignMessage(document, signature, message_hash) => { + return_message_signature(req, res, self.handler.key_server.sign_message_ecdsa(&document, &signature, message_hash) + .map_err(|err| { + warn!(target: "secretstore", "EcdsaSignMessage request {} has failed with: {}", req_uri, err); err })); }, @@ -263,7 +273,8 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &str) -> Request { return parse_admin_request(method, path, body); } - let (is_shadow_request, args_offset) = if &path[0] == "shadow" { (true, 1) } else { (false, 0) }; + let (prefix, args_offset) = if &path[0] == "shadow" || &path[0] == "schnorr" || &path[0] == "ecdsa" + { (&*path[0], 1) } else { ("", 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; @@ -282,19 +293,21 @@ fn parse_request(method: &HttpMethod, uri_path: &str, body: &str) -> Request { 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)), _, _, _) => + match (prefix, args_count, method, threshold, message_hash, common_point, encrypted_key) { + ("shadow", 3, &HttpMethod::Post, Some(Ok(threshold)), _, _, _) => Request::GenerateServerKey(document, signature, threshold), - (true, 4, &HttpMethod::Post, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) => + ("shadow", 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)), _, _, _) => + ("", 3, &HttpMethod::Post, Some(Ok(threshold)), _, _, _) => Request::GenerateDocumentKey(document, signature, threshold), - (false, 2, &HttpMethod::Get, _, _, _, _) => + ("", 2, &HttpMethod::Get, _, _, _, _) => Request::GetDocumentKey(document, signature), - (true, 2, &HttpMethod::Get, _, _, _, _) => + ("shadow", 2, &HttpMethod::Get, _, _, _, _) => Request::GetDocumentKeyShadow(document, signature), - (false, 3, &HttpMethod::Get, _, Some(Ok(message_hash)), _, _) => - Request::SignMessage(document, signature, message_hash), + ("schnorr", 3, &HttpMethod::Get, _, Some(Ok(message_hash)), _, _) => + Request::SchnorrSignMessage(document, signature, message_hash), + ("ecdsa", 3, &HttpMethod::Get, _, Some(Ok(message_hash)), _, _) => + Request::EcdsaSignMessage(document, signature, message_hash), _ => Request::Invalid, } } @@ -370,9 +383,14 @@ mod tests { assert_eq!(parse_request(&HttpMethod::Get, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), Request::GetDocumentKeyShadow("0000000000000000000000000000000000000000000000000000000000000001".into(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /{server_key_id}/{signature}/{message_hash} => sign message with server key - assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::SignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(), + // GET /schnorr/{server_key_id}/{signature}/{message_hash} => schnorr-sign message with server key + assert_eq!(parse_request(&HttpMethod::Get, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), + Request::SchnorrSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(), + "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), + "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); + // GET /ecdsa/{server_key_id}/{signature}/{message_hash} => ecdsa-sign message with server key + assert_eq!(parse_request(&HttpMethod::Get, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), + Request::EcdsaSignMessage("0000000000000000000000000000000000000000000000000000000000000001".into(), "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); // POST /admin/servers_set_change/{old_set_signature}/{new_set_signature} + body @@ -398,7 +416,8 @@ mod tests { assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid); assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid); assert_eq!(parse_request(&HttpMethod::Get, "/a/b", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::Get, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); + assert_eq!(parse_request(&HttpMethod::Get, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); + assert_eq!(parse_request(&HttpMethod::Get, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); assert_eq!(parse_request(&HttpMethod::Post, "/admin/servers_set_change/xxx/yyy", &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#), diff --git a/secret_store/src/listener/mod.rs b/secret_store/src/listener/mod.rs index a29ee5cfd..df96c583d 100644 --- a/secret_store/src/listener/mod.rs +++ b/secret_store/src/listener/mod.rs @@ -68,8 +68,12 @@ impl DocumentKeyServer for Listener { } impl MessageSigner for Listener { - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { - self.key_server.sign_message(key_id, signature, message) + fn sign_message_schnorr(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + self.key_server.sign_message_schnorr(key_id, signature, message) + } + + fn sign_message_ecdsa(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result { + self.key_server.sign_message_ecdsa(key_id, signature, message) } } diff --git a/secret_store/src/traits.rs b/secret_store/src/traits.rs index 4880a74d2..d486cfb10 100644 --- a/secret_store/src/traits.rs +++ b/secret_store/src/traits.rs @@ -76,12 +76,19 @@ pub trait DocumentKeyServer: ServerKeyGenerator { /// Message signer. pub trait MessageSigner: ServerKeyGenerator { - /// Sign message with previously generated SK. + /// Generate Schnorr signature for 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, encrypted with caller public key. - fn sign_message(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; + fn sign_message_schnorr(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; + /// Generate ECDSA signature for message with previously generated SK. + /// WARNING: only possible when SK was generated using t <= 2 * N. + /// `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, encrypted with caller public key. + fn sign_message_ecdsa(&self, key_id: &ServerKeyId, signature: &RequestSignature, message: MessageHash) -> Result; } /// Administrative sessions server.