diff --git a/src/key_server_cluster/client_sessions/generation_session.rs b/src/key_server_cluster/client_sessions/generation_session.rs index e035db9..0a0a091 100644 --- a/src/key_server_cluster/client_sessions/generation_session.rs +++ b/src/key_server_cluster/client_sessions/generation_session.rs @@ -19,14 +19,15 @@ use std::fmt::{Debug, Formatter, Error as FmtError}; use std::sync::Arc; use futures::Oneshot; use parking_lot::Mutex; -use ethereum_types::Address; +use ethereum_types::{H256, Address}; use crypto::publickey::{Public, Secret}; use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; use key_server_cluster::math; use key_server_cluster::cluster::Cluster; use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, - KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; +use key_server_cluster::random_point_generation_session::{SessionImpl as RandomPointGenerationSession, SessionTransport as RandomPointGenerationSessionTransport}; +use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, + DerivedPointGeneration, RandomPointGenerationMessage, KeysDissemination, PublicKeyShare, SessionError, SessionCompleted, JointPublicKey}; /// Distributed key generation session. /// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: @@ -89,8 +90,8 @@ struct SessionData { /// Threshold value for this DKG. Only `threshold + 1` will be able to collectively recreate joint secret, /// and thus - decrypt message, encrypted with joint public. threshold: Option, - /// Random point, jointly generated by every node in the cluster. - derived_point: Option, + /// Derived point generation session. + derived_point_generation: RandomPointGenerationSession, /// Nodes-specific data. nodes: BTreeMap, @@ -101,6 +102,12 @@ struct SessionData { secret_coeff: Option, // === Values, filled during KG phase === + /// Hash of all (ordered) publics of all nodes that were used in computation. + publics_footprint: Option, + /// Joint public that we have computed locally. + joint_public: Option, + /// Hash of all (ordered) public shares received from all nodes. + joint_public_footprint: Option, /// Secret share, which this node holds. Persistent + private. secret_share: Option, @@ -114,6 +121,8 @@ struct SessionData { /// Mutable node-specific data. #[derive(Debug, Clone)] struct NodeData { + /// True if node has confirmed initialization. + pub initialized: bool, /// Random unique scalar. Persistent. pub id_number: Secret, @@ -130,6 +139,8 @@ struct NodeData { pub public_share: Option, // === Values, filled during completion phase === + /// Flags marking that node has confirmed key joint public compution. + pub joint_computed: bool, /// Flags marking that node has confirmed session completion (generated key is stored). pub completion_confirmed: bool, } @@ -145,17 +156,26 @@ pub struct EveryOtherNodeVisitor { in_progress: BTreeSet, } +/// Derived point generation session transport. +struct DerivedPointGenerationTransport { + /// Unique session id. + id: SessionId, + /// Session-level nonce. + nonce: u64, + /// Cluster which allows this node to send messages to other nodes in the cluster. + cluster: Arc, +} + /// Distributed key generation session state. #[derive(Debug, Clone, PartialEq)] pub enum SessionState { // === Initialization states === /// Every node starts in this state. WaitingForInitialization, - /// Master node asks every other node to confirm initialization. - /// Derived point is generated by all nodes in the cluster. - WaitingForInitializationConfirm(EveryOtherNodeVisitor), - /// Slave nodes are in this state until initialization completion is reported by master node. - WaitingForInitializationComplete, + /// Master node waits for initialization confirmation from all other nodes. + WaitingForInitializationConfirm, + /// Waiting for random derived point generation. + WaitingForDerivedPointGeneration, // === KD phase states === /// Node is waiting for generated keys from every other node. @@ -164,6 +184,8 @@ pub enum SessionState { // === KG phase states === /// Node is waiting for joint public key share to be received from every other node. WaitingForPublicKeyShare, + /// Waiting for joint public key confirmation. + WaitingForJointPublic, // === Generation phase states === /// Node is waiting for session completion/session completion confirmation. @@ -206,14 +228,15 @@ impl SessionImpl { /// Create new generation session. pub fn new(params: SessionParams) -> (Self, Oneshot>) { let (completed, oneshot) = CompletionSignal::new(); + let nonce = params.nonce.unwrap_or_default(); (SessionImpl { id: params.id, self_node_id: params.self_node_id, key_storage: params.key_storage, - cluster: params.cluster, + cluster: params.cluster.clone(), // when nonce.is_nonce(), generation session is wrapped // => nonce is checked somewhere else && we can pass any value - nonce: params.nonce.unwrap_or_default(), + nonce, completed, data: Mutex::new(SessionData { state: SessionState::WaitingForInitialization, @@ -223,10 +246,20 @@ impl SessionImpl { origin: None, is_zero: None, threshold: None, - derived_point: None, + derived_point_generation: RandomPointGenerationSession::new( + params.self_node_id, + Arc::new(DerivedPointGenerationTransport { + id: params.id, + nonce, + cluster: params.cluster, + }), + ), nodes: BTreeMap::new(), polynom1: None, secret_coeff: None, + publics_footprint: None, + joint_public: None, + joint_public_footprint: None, secret_share: None, key_share: None, joint_public_and_secret: None, @@ -239,12 +272,6 @@ impl SessionImpl { &self.self_node_id } - /// Get derived point. - #[cfg(test)] - pub fn derived_point(&self) -> Option { - self.data.lock().derived_point.clone() - } - /// Simulate faulty generation session behaviour. pub fn simulate_faulty_behaviour(&self) { self.data.lock().simulate_faulty_behaviour = true; @@ -294,51 +321,51 @@ impl SessionImpl { 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)); + data.nodes.insert(node_id, NodeData::with_id_number(node_id == self.self_node_id, 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)); + data.nodes.insert(node_id, NodeData::with_id_number(node_id == self.self_node_id, node_id_number)); } }, } - let mut visit_policy = EveryOtherNodeVisitor::new(self.node(), data.nodes.keys().cloned()); - let derived_point = math::generate_random_point()?; - match visit_policy.next_node() { - Some(next_node) => { - data.state = SessionState::WaitingForInitializationConfirm(visit_policy); - - // start initialization - self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - origin: origin.map(Into::into), - 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(), - }))) - }, - None => { - drop(data); - self.complete_initialization(derived_point)?; - self.disseminate_keys()?; - self.verify_keys()?; - self.complete_generation()?; - - let mut data = self.data.lock(); - let result = data.joint_public_and_secret.clone() - .expect("session is instantly completed on a single node; qed") - .map(|(p, _, _)| p); - data.state = SessionState::Finished; - self.completed.send(result); + // if we are single node + if data.nodes.len() == 1 { + let participants = data.nodes.keys().cloned().collect(); + data.derived_point_generation.start(participants)?; + drop(data); + self.disseminate_keys()?; + self.verify_keys()?; + self.compute_joint_public()?; + self.complete_generation()?; + + let mut data = self.data.lock(); + let result = data.joint_public_and_secret.clone() + .expect("session is instantly completed on a single node; qed") + .map(|(p, _, _)| p); + data.state = SessionState::Finished; + self.completed.send(result); - Ok(()) - } + return Ok(()); } + + // initialize session on other nodes + data.state = SessionState::WaitingForInitializationConfirm; + self.cluster.broadcast(Message::Generation(GenerationMessage::InitializeSession( + InitializeSession { + session: self.id.clone().into(), + session_nonce: self.nonce, + origin: origin.map(Into::into), + 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"), + }, + )))?; + + Ok(()) } /// Process single message. @@ -352,12 +379,14 @@ impl SessionImpl { self.on_initialize_session(sender.clone(), message), &GenerationMessage::ConfirmInitialization(ref message) => self.on_confirm_initialization(sender.clone(), message), - &GenerationMessage::CompleteInitialization(ref message) => - self.on_complete_initialization(sender.clone(), message), + &GenerationMessage::DerivedPointGeneration(ref message) => + self.on_derived_point_generation(sender.clone(), message), &GenerationMessage::KeysDissemination(ref message) => self.on_keys_dissemination(sender.clone(), message), &GenerationMessage::PublicKeyShare(ref message) => self.on_public_key_share(sender.clone(), message), + &GenerationMessage::JointPublicKey(ref message) => + self.on_joint_public_key(sender.clone(), message), &GenerationMessage::SessionError(ref message) => { self.on_session_error(sender, message.error.clone()); Ok(()) @@ -384,22 +413,17 @@ impl SessionImpl { return Err(Error::InvalidStateForRequest); } - // update derived point with random scalar - let mut derived_point = message.derived_point.clone().into(); - math::update_random_point(&mut derived_point)?; - // send confirmation back to master node self.cluster.send(&sender, Message::Generation(GenerationMessage::ConfirmInitialization(ConfirmInitialization { session: self.id.clone().into(), session_nonce: self.nonce, - derived_point: derived_point.into(), })))?; // update state data.master = Some(sender); data.author = Some(message.author.clone().into()); - data.state = SessionState::WaitingForInitializationComplete; - data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); + data.state = SessionState::WaitingForDerivedPointGeneration; + data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(true, number.clone().into()))).collect(); data.origin = message.origin.clone().map(Into::into); data.is_zero = Some(message.is_zero); data.threshold = Some(message.threshold); @@ -407,65 +431,68 @@ impl SessionImpl { Ok(()) } - /// When session initialization confirmation message is reeived. + /// When session initialization confirmation message is received. pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); // check state && select new node to be initialized - let next_receiver = match data.state { - SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { - if !visit_policy.mark_visited(&sender) { - return Err(Error::InvalidStateForRequest); - } - - visit_policy.next_node() - }, + match data.state { + SessionState::WaitingForInitializationConfirm => (), _ => return Err(Error::InvalidStateForRequest), - }; + } - // proceed message - if let Some(next_receiver) = next_receiver { - return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - origin: data.origin.clone().map(Into::into), - author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), - nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), - 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(), - }))); + // update node data + { + let node_data = data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; + if node_data.initialized { + return Err(Error::InvalidMessage); + } + node_data.initialized = true; } - // now it is time for keys dissemination (KD) phase - drop(data); - self.complete_initialization(message.derived_point.clone().into())?; - self.disseminate_keys() + // if all nodes have confirmed initialization, start derived point generation + if data.nodes.values().any(|nd| !nd.initialized) { + return Ok(()); + } + + let participants = data.nodes.keys().cloned().collect(); + data.state = SessionState::WaitingForDerivedPointGeneration; + data.derived_point_generation.start(participants) } - /// When session initialization completion message is received. - pub fn on_complete_initialization(&self, sender: NodeId, message: &CompleteInitialization) -> Result<(), Error> { + /// When derived point generation message is received. + pub fn on_derived_point_generation( + &self, + sender: NodeId, + message: &DerivedPointGeneration, + ) -> Result<(), Error> { debug_assert!(self.id == *message.session); debug_assert!(&sender != self.node()); let mut data = self.data.lock(); // check state - if data.state != SessionState::WaitingForInitializationComplete { - return Err(Error::InvalidStateForRequest); + match data.state { + SessionState::WaitingForDerivedPointGeneration => (), + _ => return Err(Error::InvalidStateForRequest), } - if data.master != Some(sender) { - return Err(Error::InvalidMessage); + + // process message + if !data.derived_point_generation.is_started() { + let participants = data.nodes.keys().cloned().collect(); + data.derived_point_generation.start(participants)?; } + data.derived_point_generation.process_message(&sender, &message.message)?; - // remember passed data - data.derived_point = Some(message.derived_point.clone().into()); + // if derived point generation has completed, proceed to the next state + if data.derived_point_generation.generated_point().is_none() { + return Ok(()); + } - // now it is time for keys dissemination (KD) phase + // start keys dissemination drop(data); self.disseminate_keys() } @@ -483,11 +510,10 @@ impl SessionImpl { } // check state - if data.state != SessionState::WaitingForKeysDissemination { - match data.state { - SessionState::WaitingForInitializationComplete | SessionState::WaitingForInitializationConfirm(_) => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } + match data.state { + SessionState::WaitingForDerivedPointGeneration => return Err(Error::TooEarlyForRequest), + SessionState::WaitingForKeysDissemination => (), + _ => return Err(Error::InvalidStateForRequest), } debug_assert!(data.nodes.contains_key(&sender)); @@ -511,7 +537,7 @@ impl SessionImpl { } // check if we have received keys from every other node - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && (node_data.publics.is_none() || node_data.secret1.is_none() || node_data.secret2.is_none())) { + if data.nodes.values().any(|node_data| node_data.publics.is_none() || node_data.secret1.is_none() || node_data.secret2.is_none()) { return Ok(()) } @@ -526,24 +552,87 @@ impl SessionImpl { // check state if data.state != SessionState::WaitingForPublicKeyShare { match data.state { - SessionState::WaitingForInitializationComplete | + SessionState::WaitingForDerivedPointGeneration | SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), _ => return Err(Error::InvalidStateForRequest), } } + // check that the node was working with the same publics that we know of + let publics_footprint = message.publics_footprint.clone().into(); + let expected_publics_footprint = data.publics_footprint.as_ref() + .expect("computed n KD phase; we're in KG phase; KG phase follows KD; qed"); + if *expected_publics_footprint != publics_footprint { + return Err(Error::InvalidMessage); + } + // update node data with received public share + let self_id_number = data.nodes[self.node()].id_number.clone(); + let threshold = data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"); + let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); { let node_data = &mut data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; if node_data.public_share.is_some() { return Err(Error::InvalidMessage); } - node_data.public_share = Some(message.public_share.clone().into()); + // verify public share proof + let is_share_proof_valid = if !is_zero { + math::share_proof_verification( + threshold, + &self_id_number, + node_data.secret1.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"), + &message.public_share_proof.iter().cloned().map(Into::into).collect::>(), + )? + } else { + true + }; + if !is_share_proof_valid { + return Err(Error::InvalidMessage); + } + + node_data.public_share = Some(message.public_share_proof[0].clone().into()); } // if there's also nodes, which has not sent us their public shares - do nothing - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && node_data.public_share.is_none()) { + if data.nodes.values().any(|node_data| node_data.public_share.is_none()) { + return Ok(()); + } + + drop(data); + self.compute_joint_public() + } + + /// When public key is computed. + pub fn on_joint_public_key(&self, sender: NodeId, message: &JointPublicKey) -> Result<(), Error> { + let mut data = self.data.lock(); + + // check state + match data.state { + SessionState::WaitingForPublicKeyShare => return Err(Error::TooEarlyForRequest), + SessionState::WaitingForJointPublic => (), + _ => return Err(Error::InvalidStateForRequest), + } + + // check that we both have received same input data from other nodes + let expected_joint_public_footprint = data.joint_public_footprint.as_ref() + .expect("computed in WaitingForPublicKeyShare state; we have already passed this state; qed"); + if *expected_joint_public_footprint != message.joint_public_footprint.clone().into() { + return Err(Error::InvalidMessage); + } + + // remember that the node has confirmed generation + { + let node_data = &mut data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; + if node_data.joint_computed { + return Err(Error::InvalidMessage); + } + + node_data.joint_computed = true; + } + + // if there's also nodes, which have not yet confirmed joint publice - do nothing + if data.nodes.values().any(|node_data| !node_data.joint_computed) { return Ok(()); } @@ -560,11 +649,10 @@ impl SessionImpl { debug_assert!(data.nodes.contains_key(&sender)); // check state - if data.state != SessionState::WaitingForGenerationConfirmation { - match data.state { - SessionState::WaitingForPublicKeyShare => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } + match data.state { + SessionState::WaitingForJointPublic => return Err(Error::TooEarlyForRequest), + SessionState::WaitingForGenerationConfirmation => (), + _ => return Err(Error::InvalidStateForRequest), } // if we are not masters, save result and respond with confirmation @@ -633,23 +721,6 @@ impl SessionImpl { Ok(()) } - /// Complete initialization (when all other nodex has responded with confirmation) - fn complete_initialization(&self, mut derived_point: Public) -> Result<(), Error> { - // update point once again to make sure that derived point is not generated by last node - math::update_random_point(&mut derived_point)?; - - // remember derived point - let mut data = self.data.lock(); - data.derived_point = Some(derived_point.clone().into()); - - // broadcast derived point && other session paraeters to every other node - self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - derived_point: derived_point.into(), - }))) - } - /// Keys dissemination (KD) phase fn disseminate_keys(&self) -> Result<(), Error> { let mut data = self.data.lock(); @@ -668,7 +739,7 @@ impl SessionImpl { // compute t+1 public values 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"), + &data.derived_point_generation.generated_point().expect("keys dissemination occurs after derived point is agreed; qed"), &polynom1, &polynom2)?, true => Default::default(), @@ -710,9 +781,9 @@ impl SessionImpl { let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KV phase follows initialization phase; qed"); let self_public_share = { if !is_zero { - let derived_point = data.derived_point.clone().expect("derived point generated on initialization phase; KV phase follows initialization phase; qed"); + let derived_point = data.derived_point_generation.generated_point().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()) { + 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"); @@ -745,9 +816,29 @@ impl SessionImpl { math::compute_secret_share(secret_values_iter)? }; + // prepare publics footprint and public share proof + let publics_footprint = math::compute_publics_footprint( + data + .nodes + .iter() + .map(|(node, node_data)| ( + node.clone(), + node_data.publics.clone().expect("keys received on KD phase; KG phase follows KD phase; qed"), + )) + .collect(), + )?; + let public_share_proof = if !is_zero { + math::prepare_share_proof( + data.polynom1.as_ref().expect("polynom1 is generated on KD phase; KG phase follows KD phase; qed"), + )? + } else { + vec![self_public_share] + }; + // update state data.state = SessionState::WaitingForPublicKeyShare; data.secret_share = Some(self_secret_share); + data.publics_footprint = Some(publics_footprint); let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); self_node.public_share = Some(self_public_share.clone()); @@ -755,12 +846,13 @@ impl SessionImpl { self.cluster.broadcast(Message::Generation(GenerationMessage::PublicKeyShare(PublicKeyShare { session: self.id.clone().into(), session_nonce: self.nonce, - public_share: self_public_share.into(), + publics_footprint: publics_footprint.into(), + public_share_proof: public_share_proof.into_iter().map(Into::into).collect(), }))) } - /// Complete generation - fn complete_generation(&self) -> Result<(), Error> { + /// Compute joint public key. + fn compute_joint_public(&self) -> Result<(), Error> { let mut data = self.data.lock(); // calculate joint public key @@ -772,12 +864,48 @@ impl SessionImpl { Default::default() }; + // compute joint public footprint + let joint_public_footprint = math::compute_publics_footprint( + data.nodes + .iter() + .map(|(id, node_data)| ( + id.clone(), + vec![ + node_data + .public_share + .clone() + .expect("keys received on KD phase; KG phase follows KD phase; qed"), + ] + )) + .collect(), + )?; + + // + let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); + self_node.joint_computed = true; + + // broadcast joint public key to be sure that all nodes have used the same input data + data.state = SessionState::WaitingForJointPublic; + data.joint_public = Some(joint_public); + data.joint_public_footprint = Some(joint_public_footprint); + self.cluster.broadcast(Message::Generation(GenerationMessage::JointPublicKey(JointPublicKey { + session: self.id.clone().into(), + session_nonce: self.nonce, + joint_public_footprint: joint_public_footprint.into(), + }))) + } + + /// Complete generation. + fn complete_generation(&self) -> Result<(), Error> { + let mut data = self.data.lock(); + // prepare key data + let joint_public = data.joint_public.clone().expect("joint public is filled in the beginning of KG phase; we're at the end of KG phase; qed"); 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"), - public: joint_public.clone(), + public: joint_public, common_point: None, encrypted_point: None, versions: vec![DocumentKeyShareVersion::new( @@ -889,39 +1017,16 @@ impl ClusterSession for SessionImpl { } } -impl EveryOtherNodeVisitor { - pub fn new(self_id: &NodeId, nodes: I) -> Self where I: Iterator { - EveryOtherNodeVisitor { - visited: BTreeSet::new(), - unvisited: nodes.filter(|n| n != self_id).collect(), - in_progress: BTreeSet::new(), - } - } - - pub fn next_node(&mut self) -> Option { - let next_node = self.unvisited.pop_front(); - if let Some(ref next_node) = next_node { - self.in_progress.insert(next_node.clone()); - } - next_node - } - - pub fn mark_visited(&mut self, node: &NodeId) -> bool { - if !self.in_progress.remove(node) { - return false; - } - self.visited.insert(node.clone()) - } -} - impl NodeData { - fn with_id_number(node_id_number: Secret) -> Self { + fn with_id_number(initialized: bool, node_id_number: Secret) -> Self { NodeData { + initialized, id_number: node_id_number, secret1: None, secret2: None, publics: None, public_share: None, + joint_computed: false, completion_confirmed: false, } } @@ -947,14 +1052,25 @@ fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Err Ok(()) } +impl RandomPointGenerationSessionTransport for DerivedPointGenerationTransport { + fn send(&self, node: &NodeId, message: RandomPointGenerationMessage) -> Result<(), Error> { + self.cluster.send(node, Message::Generation(GenerationMessage::DerivedPointGeneration( + DerivedPointGeneration { + session: self.id.into(), + session_nonce: self.nonce, + message, + } + ))) + } +} + #[cfg(test)] pub mod tests { use std::sync::Arc; use ethereum_types::H256; use crypto::publickey::{Random, Generator, KeyPair, Secret}; use key_server_cluster::{NodeId, Error, KeyStorage, SessionId}; - use key_server_cluster::message::{self, Message, GenerationMessage, KeysDissemination, - PublicKeyShare, ConfirmInitialization}; + use key_server_cluster::message::{self, KeysDissemination, PublicKeyShare, ConfirmInitialization}; use key_server_cluster::cluster::tests::{MessageLoop as ClusterMessageLoop, make_clusters_and_preserve_sessions}; use key_server_cluster::cluster_sessions::ClusterSession; use key_server_cluster::generation_session::{SessionImpl, SessionState}; @@ -983,30 +1099,6 @@ pub mod tests { self.0.sessions_of(node).generation_sessions.first().unwrap() } - pub fn take_message_confirm_initialization(&self) -> (NodeId, NodeId, ConfirmInitialization) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn take_message_keys_dissemination(&self) -> (NodeId, NodeId, KeysDissemination) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::KeysDissemination(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn take_message_public_key_share(&self) -> (NodeId, NodeId, PublicKeyShare) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::PublicKeyShare(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - pub fn nodes_id_numbers(&self) -> Vec { let session = self.session_at(0); let session_data = session.data.lock(); @@ -1070,34 +1162,21 @@ pub mod tests { ); } - #[test] - fn slave_updates_derived_point_on_initialization() { - let ml = MessageLoop::new(2).init(0).unwrap(); - let original_point = match ml.0.take_message().unwrap() { - (from, to, Message::Generation(GenerationMessage::InitializeSession(msg))) => { - let original_point = msg.derived_point.clone(); - let msg = Message::Generation(GenerationMessage::InitializeSession(msg)); - ml.0.process_message(from, to, msg); - original_point - }, - _ => panic!("unexpected"), - }; - - match ml.0.take_message().unwrap() { - (_, _, Message::Generation(GenerationMessage::ConfirmInitialization(msg))) => - assert!(original_point != msg.derived_point), - _ => panic!("unexpected"), - } - } - #[test] fn fails_to_accept_initialization_confirmation_if_already_accepted_from_the_same_node() { let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); - - let (from, to, msg) = ml.take_message_confirm_initialization(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg.clone()))); - assert_eq!(ml.session_of(&to).on_confirm_initialization(from, &msg), Err(Error::InvalidStateForRequest)); + ml.session_of(&ml.0.node(0)).data.lock().state = SessionState::WaitingForInitializationConfirm; + ml.session_of(&ml.0.node(0)).data.lock().nodes.get_mut(&ml.0.node(1)).unwrap().initialized = true; + assert_eq!( + ml.session_of(&ml.0.node(0)) + .on_confirm_initialization( + ml.0.node(1), + &ConfirmInitialization { + session: SessionId::from([1u8; 32]).into(), + session_nonce: 0, + }), + Err(Error::InvalidMessage), + ); } #[test] @@ -1108,52 +1187,9 @@ pub mod tests { assert_eq!(ml.session_at(0).on_confirm_initialization(ml.0.node(1), &message::ConfirmInitialization { session: [1u8; 32].into(), session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn master_updates_derived_point_on_initialization_completion() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - let original_point = match ml.0.take_message().unwrap() { - (from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg))) => { - let original_point = msg.derived_point.clone(); - let msg = Message::Generation(GenerationMessage::ConfirmInitialization(msg)); - ml.session_of(&to).on_message(&from, &msg).unwrap(); - original_point - }, - _ => panic!("unexpected"), - }; - - assert!(ml.session_at(0).derived_point().unwrap() != original_point.into()); - } - - #[test] - fn fails_to_complete_initialization_if_not_waiting_for_it() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(0).on_complete_initialization(ml.0.node(1), &message::CompleteInitialization { - session: [1u8; 32].into(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), }), Err(Error::InvalidStateForRequest)); } - #[test] - fn fails_to_complete_initialization_from_non_master_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(1).on_complete_initialization(ml.0.node(2), &message::CompleteInitialization { - session: [1u8; 32].into(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidMessage)); - } - #[test] fn fails_to_accept_keys_dissemination_if_not_waiting_for_it() { let ml = MessageLoop::new(2).init(0).unwrap(); @@ -1163,37 +1199,49 @@ pub mod tests { secret1: math::generate_random_scalar().unwrap().into(), secret2: math::generate_random_scalar().unwrap().into(), publics: vec![math::generate_random_point().unwrap().into()], - }), Err(Error::TooEarlyForRequest)); + }), Err(Error::InvalidStateForRequest)); } #[test] fn fails_to_accept_keys_dissemination_if_wrong_number_of_publics_passed() { let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - - let (from, to, mut msg) = ml.take_message_keys_dissemination(); - msg.publics.clear(); - assert_eq!(ml.session_of(&to).on_keys_dissemination(from, &msg), Err(Error::InvalidMessage)); + ml.session_of(&ml.0.node(0)).data.lock().state = SessionState::WaitingForKeysDissemination; + assert_eq!( + ml.session_of(&ml.0.node(0)) + .on_keys_dissemination( + ml.0.node(1), + &KeysDissemination { + session: SessionId::from([1u8; 32]).into(), + session_nonce: 0, + secret1: [1u8; 32].into(), + secret2: [1u8; 32].into(), + publics: vec![], + }), + Err(Error::InvalidMessage), + ); } #[test] fn fails_to_accept_keys_dissemination_second_time_from_the_same_node() { let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - - let (from, to, msg) = ml.take_message_keys_dissemination(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::KeysDissemination(msg.clone()))); - assert_eq!(ml.session_of(&to).on_keys_dissemination(from, &msg), Err(Error::InvalidStateForRequest)); + ml.session_of(&ml.0.node(0)).data.lock().state = SessionState::WaitingForKeysDissemination; + ml.session_of(&ml.0.node(0)).data.lock().nodes.get_mut(&ml.0.node(1)).unwrap().secret1 = Some([1u8; 32].into()); + assert_eq!( + ml.session_of(&ml.0.node(0)) + .on_keys_dissemination( + ml.0.node(1), + &KeysDissemination { + session: SessionId::from([1u8; 32]).into(), + session_nonce: 0, + secret1: [1u8; 32].into(), + secret2: [1u8; 32].into(), + publics: vec![ + NodeId::from([1u8; 64]).into(), + NodeId::from([2u8; 64]).into(), + ], + }), + Err(Error::InvalidMessage), + ); } #[test] @@ -1202,29 +1250,29 @@ pub mod tests { assert_eq!(ml.session_at(0).on_public_key_share(ml.0.node(1), &message::PublicKeyShare { session: [1u8; 32].into(), session_nonce: 0, - public_share: math::generate_random_point().unwrap().into(), + publics_footprint: H256::default().into(), + public_share_proof: Vec::new(), }), Err(Error::InvalidStateForRequest)); } #[test] fn should_not_accept_public_key_share_when_receiving_twice() { let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - ml.0.take_and_process_message(); // m -> s1: KeysDissemination - ml.0.take_and_process_message(); // m -> s2: KeysDissemination - ml.0.take_and_process_message(); // s1 -> m: KeysDissemination - ml.0.take_and_process_message(); // s1 -> s2: KeysDissemination - ml.0.take_and_process_message(); // s2 -> m: KeysDissemination - ml.0.take_and_process_message(); // s2 -> s1: KeysDissemination - - let (from, to, msg) = ml.take_message_public_key_share(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::PublicKeyShare(msg.clone()))); - assert_eq!(ml.session_of(&to).on_public_key_share(from, &msg), Err(Error::InvalidMessage)); + ml.session_of(&ml.0.node(0)).data.lock().state = SessionState::WaitingForPublicKeyShare; + ml.session_of(&ml.0.node(0)).data.lock().publics_footprint = Some(H256::default()); + ml.session_of(&ml.0.node(0)).data.lock().nodes.get_mut(&ml.0.node(1)).unwrap().public_share = Some(Default::default()); + assert_eq!( + ml.session_of(&ml.0.node(0)) + .on_public_key_share( + ml.0.node(1), + &PublicKeyShare { + session: SessionId::from([1u8; 32]).into(), + session_nonce: 0, + publics_footprint: H256::default().into(), + public_share_proof: Vec::new(), + }), + Err(Error::InvalidMessage), + ); } #[test] diff --git a/src/key_server_cluster/client_sessions/mod.rs b/src/key_server_cluster/client_sessions/mod.rs index a3df26b..bb995a1 100644 --- a/src/key_server_cluster/client_sessions/mod.rs +++ b/src/key_server_cluster/client_sessions/mod.rs @@ -17,5 +17,6 @@ pub mod decryption_session; pub mod encryption_session; pub mod generation_session; +pub mod random_point_generation_session; pub mod signing_session_ecdsa; pub mod signing_session_schnorr; diff --git a/src/key_server_cluster/client_sessions/random_point_generation_session.rs b/src/key_server_cluster/client_sessions/random_point_generation_session.rs new file mode 100644 index 0000000..16fd6dc --- /dev/null +++ b/src/key_server_cluster/client_sessions/random_point_generation_session.rs @@ -0,0 +1,399 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Secret Store. + +// Parity Secret Store 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 Secret Store 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 Secret Store. If not, see . + +use std::{ + collections::{BTreeMap, BTreeSet}, + fmt::{Debug, Formatter, Error as FmtError}, + sync::Arc, +}; +use crypto::publickey::{Generator, Random, Public, KeyPair}; +use key_server_cluster::{ + {Error, NodeId}, + math, + io::{encrypt_data, decrypt_data}, + message::{ + RandomPointGenerationMessage, + RandomPointGenerationEncryptedShare, RandomPointGenerationDecryptionKey, + }, +}; + +/// Random point generation session transport. +/// +/// This session is always wrapped, so we don't care about nonces and session IDs. +pub trait SessionTransport: Send + Sync { + /// Send message to given node. + fn send(&self, node: &NodeId, message: RandomPointGenerationMessage) -> Result<(), Error>; +} + +/// Random point generation session. +/// Based on EC-Rand() from "Secure Multi-Party Computation for Elliptic Curves": +/// 1) all nodes (i within [1; n]) generate random points Pi and random key pair Ei; +/// 2) all nodes encrypt Pi with Ei.Public and broadcast encrypted(Pi); +/// 3) when node has received encrypted(Pi) from all other nodes, it broadcasts its Ei.private; +/// 4) when node receives Ei.private from all nodes, it decrypts shares Ei; +/// 5) Sum(Ei) is the random EC point with corresponding provate key unknown to all other nodes. +/// +/// The broadcast of Sum(Ei) (and verification that it is the same on all nodes) is supposed to +/// happen outside of this session. +pub struct SessionImpl { + /// This node id. + self_node_id: NodeId, + /// Session transport. + transport: Arc, + /// Current state of the session. + state: SessionState, + /// Nodes-specific data. + nodes: BTreeMap, + /// Session result. + result: Option, +} + +/// Mutable node-specific data. +#[derive(Debug, Default)] +struct NodeData { + /// Encrypted (ECIES) share. + encrypted_share: Option>, + /// Decryption key pair. + decryption_key: Option, +} + +/// Distributed key generation session state. +#[derive(Debug, Clone, PartialEq)] +enum SessionState { + /// Session isn't yet started. + WaitingForStart, + /// Node has broadcasted encrypted share and waits for other nodes encrypted shares. + WaitingForEncryptedShares, + /// Node has broadcasted decryption key and waits for other nodes decryption keys. + WaitingForDecryptionKeys, + /// Session has completed successfully. + Finished, +} + +impl SessionImpl { + /// Create new generation session. + pub fn new(self_node_id: NodeId, transport: Arc) -> Self { + SessionImpl { + self_node_id, + transport, + state: SessionState::WaitingForStart, + nodes: BTreeMap::new(), + result: None, + } + } + + /// Returns true if session has been started. + pub fn is_started(&self) -> bool { + self.state != SessionState::WaitingForStart + } + + /// Returns generated random point. + pub fn generated_point(&self) -> Option { + self.result.clone() + } + + /// Starts this session. + pub fn start(&mut self, nodes: BTreeSet) -> Result<(), Error> { + match self.state { + SessionState::WaitingForStart => (), + _ => return Err(Error::InvalidStateForRequest), + } + + // fill nodes + assert!(nodes.contains(&self.self_node_id)); + self.nodes = nodes + .into_iter() + .map(|node_id| (node_id, NodeData { + encrypted_share: None, + decryption_key: None, + })) + .collect(); + + // generate and encrypt own share + let self_decryption_key = Random.generate(); + let self_share = math::generate_random_point()?; + let self_encrypted_share = encrypt_data( + &self_decryption_key, + self_share.as_bytes(), + )?; + let self_node_data = self.nodes + .get_mut(&self.self_node_id) + .expect("inserted above; qed"); + self_node_data.encrypted_share = Some(self_encrypted_share.clone()); + self_node_data.decryption_key = Some(self_decryption_key); + + // if we are single node, just compute generated point + if self.nodes.len() == 1 { + self.state = SessionState::WaitingForDecryptionKeys; + return self.compute_generated_point(); + } + + // else broadcast encrypted share + for node in self.nodes.keys().filter(|n| **n != self.self_node_id) { + self.transport.send(&node, RandomPointGenerationMessage::EncryptedShare( + RandomPointGenerationEncryptedShare { + encrypted_share: self_encrypted_share.clone(), + } + ))?; + } + + self.state = SessionState::WaitingForEncryptedShares; + + Ok(()) + } + + /// Process single message. + pub fn process_message(&mut self, sender: &NodeId, message: &RandomPointGenerationMessage) -> Result<(), Error> { + match message { + &RandomPointGenerationMessage::EncryptedShare(ref message) => + self.on_encrypted_share(sender, message), + &RandomPointGenerationMessage::DecryptionKey(ref message) => + self.on_decryption_key(sender, message), + } + } + + /// When encrypted share is received. + pub fn on_encrypted_share(&mut self, sender: &NodeId, message: &RandomPointGenerationEncryptedShare) -> Result<(), Error> { + match self.state { + SessionState::WaitingForStart => return Err(Error::TooEarlyForRequest), + SessionState::WaitingForEncryptedShares => (), + _ => return Err(Error::InvalidStateForRequest), + } + + { + let node_data = self.nodes.get_mut(sender).ok_or(Error::InvalidMessage)?; + if node_data.encrypted_share.is_some() { + return Err(Error::InvalidMessage); + } + + node_data.encrypted_share = Some(message.encrypted_share.clone()); + } + + if self.nodes.values().any(|n| n.encrypted_share.is_none()) { + return Ok(()); + } + + let self_decryption_key = self.nodes + .get(&self.self_node_id) + .expect("inserted when session is started and never deleted; qed") + .decryption_key + .clone() + .expect("initialized when session is started and never deleted; qed"); + self.state = SessionState::WaitingForDecryptionKeys; + for node in self.nodes.keys().filter(|n| **n != self.self_node_id) { + self.transport.send(&node, RandomPointGenerationMessage::DecryptionKey( + RandomPointGenerationDecryptionKey { + decryption_key: self_decryption_key.secret().clone().into(), + } + ))?; + } + + Ok(()) + } + + /// When decryption key is received. + pub fn on_decryption_key(&mut self, sender: &NodeId, message: &RandomPointGenerationDecryptionKey) -> Result<(), Error> { + match self.state { + SessionState::WaitingForStart | SessionState::WaitingForEncryptedShares => + return Err(Error::TooEarlyForRequest), + SessionState::WaitingForDecryptionKeys => (), + _ => return Err(Error::InvalidStateForRequest), + } + + { + let node_data = self.nodes.get_mut(sender).ok_or(Error::InvalidMessage)?; + if node_data.decryption_key.is_some() { + return Err(Error::InvalidMessage); + } + + node_data.decryption_key = Some(KeyPair::from_secret(message.decryption_key.clone().into())?); + } + + if self.nodes.values().any(|n| n.decryption_key.is_none()) { + return Ok(()); + } + + self.compute_generated_point() + } + + /// Compute generated point. + fn compute_generated_point(&mut self) -> Result<(), Error> { + let mut shares = Vec::with_capacity(self.nodes.len()); + for (node_id,node_data) in &self.nodes { + let raw_share = decrypt_data( + node_data.decryption_key.as_ref().expect("checked above; qed"), + &node_data.encrypted_share.as_ref().expect("we are in WaitingForDecryptionKeys state; + WaitingForDecryptionKeys follows WaitingForEncryptedShares state; + WaitingForEncryptedShares -> WaitingForDecryptionKeys only happens when all encrypted shares are reqceived; + qed"), + )?; + if raw_share.len() != 64 { + return Err(Error::Internal(format!("Invalid share is provided by {}", node_id))); + } + shares.push(Public::from_slice(&raw_share)); + } + + self.result = Some(math::compute_public_sum(shares.iter())?); + self.state = SessionState::Finished; + + Ok(()) + } +} + +impl Debug for SessionImpl { + fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { + write!(f, "Random point generation session on {}", self.self_node_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct DummyTransport; + + impl SessionTransport for DummyTransport { + fn send(&self, _: &NodeId, _: RandomPointGenerationMessage) -> Result<(), Error> { + Ok(()) + } + } + + fn node(index: u8) -> NodeId { + [index; 64].into() + } + + fn single_node() -> BTreeSet { + vec![node(1)].into_iter().collect() + } + + fn dummy_nodes() -> BTreeSet { + vec![node(1), node(2), node(3)].into_iter().collect() + } + + fn dummy_session(nodes: BTreeSet) -> SessionImpl { + SessionImpl::new(nodes.into_iter().next().unwrap(), Arc::new(DummyTransport)) + } + + #[test] + fn rpg_session_rejects_to_start_twice() { + let mut session = dummy_session(dummy_nodes()); + assert_eq!(session.start(dummy_nodes()), Ok(())); + assert_eq!(session.start(dummy_nodes()), Err(Error::InvalidStateForRequest)); + } + + #[test] + fn rpg_session_on_single_node_completes_instantly() { + let mut session = dummy_session(single_node()); + assert_eq!(session.start(single_node()), Ok(())); + assert_eq!(session.state, SessionState::Finished); + assert!(session.generated_point().is_some()); + } + + #[test] + fn rpg_session_rejects_encrypted_share() { + let mut session = dummy_session(dummy_nodes()); + let message = RandomPointGenerationMessage::EncryptedShare( + RandomPointGenerationEncryptedShare { + encrypted_share: vec![42], + }, + ); + // before start + assert_eq!(session.process_message(&node(2), &message), Err(Error::TooEarlyForRequest)); + // after receiving all shares + session.state = SessionState::WaitingForDecryptionKeys; + assert_eq!(session.process_message(&node(2), &message), Err(Error::InvalidStateForRequest)); + // after finish + session.state = SessionState::Finished; + assert_eq!(session.process_message(&node(2), &message), Err(Error::InvalidStateForRequest)); + // after receving share from the same node + session.state = SessionState::WaitingForEncryptedShares; + session.nodes.entry(node(2)).or_default().encrypted_share = Some(vec![42]); + assert_eq!(session.process_message(&node(2), &message), Err(Error::InvalidMessage)); + } + + #[test] + fn rpg_session_accepts_encrypted_shares() { + let mut session = dummy_session(dummy_nodes()); + let message = RandomPointGenerationMessage::EncryptedShare( + RandomPointGenerationEncryptedShare { + encrypted_share: vec![42], + }, + ); + session.start(dummy_nodes()).unwrap(); + assert_eq!(session.state, SessionState::WaitingForEncryptedShares); + + assert_eq!(session.process_message(&node(2), &message), Ok(())); + assert_eq!(session.state, SessionState::WaitingForEncryptedShares); + assert_eq!(session.process_message(&node(3), &message), Ok(())); + assert_eq!(session.state, SessionState::WaitingForDecryptionKeys); + } + + #[test] + fn rpg_session_rejects_decryption_keys() { + let mut session = dummy_session(dummy_nodes()); + let message = RandomPointGenerationMessage::DecryptionKey( + RandomPointGenerationDecryptionKey { + decryption_key: [1u8; 32].into(), + }, + ); + + // before start + assert_eq!(session.process_message(&node(2), &message), Err(Error::TooEarlyForRequest)); + // when receiving shares + session.state = SessionState::WaitingForEncryptedShares; + assert_eq!(session.process_message(&node(2), &message), Err(Error::TooEarlyForRequest)); + // after finish + session.state = SessionState::Finished; + assert_eq!(session.process_message(&node(2), &message), Err(Error::InvalidStateForRequest)); + // after receving key from the same node + session.state = SessionState::WaitingForDecryptionKeys; + session.nodes.entry(node(2)).or_default().decryption_key = Some(KeyPair::from_secret_slice(&[1u8; 32]).unwrap()); + assert_eq!(session.process_message(&node(2), &message), Err(Error::InvalidMessage)); + } + + #[test] + fn rpg_session_works() { + let decryption_key = Random.generate(); + let share = math::generate_random_point().unwrap(); + let encrypted_share = encrypt_data(&decryption_key, share.as_bytes()).unwrap(); + + let mut session = dummy_session(dummy_nodes()); + let message = RandomPointGenerationMessage::EncryptedShare( + RandomPointGenerationEncryptedShare { + encrypted_share, + }, + ); + session.start(dummy_nodes()).unwrap(); + assert_eq!(session.state, SessionState::WaitingForEncryptedShares); + + assert_eq!(session.process_message(&node(2), &message), Ok(())); + assert_eq!(session.state, SessionState::WaitingForEncryptedShares); + assert_eq!(session.process_message(&node(3), &message), Ok(())); + assert_eq!(session.state, SessionState::WaitingForDecryptionKeys); + + let message = RandomPointGenerationMessage::DecryptionKey( + RandomPointGenerationDecryptionKey { + decryption_key: decryption_key.secret().clone().into(), + }, + ); + + assert_eq!(session.process_message(&node(2), &message), Ok(())); + assert_eq!(session.state, SessionState::WaitingForDecryptionKeys); + assert_eq!(session.process_message(&node(3), &message), Ok(())); + assert_eq!(session.state, SessionState::Finished); + assert!(session.generated_point().is_some()); + } +} diff --git a/src/key_server_cluster/client_sessions/signing_session_schnorr.rs b/src/key_server_cluster/client_sessions/signing_session_schnorr.rs index 40b0d93..b16493a 100644 --- a/src/key_server_cluster/client_sessions/signing_session_schnorr.rs +++ b/src/key_server_cluster/client_sessions/signing_session_schnorr.rs @@ -998,7 +998,6 @@ mod tests { message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { session: SessionId::from([1u8; 32]).into(), session_nonce: 0, - derived_point: Public::default().into(), }), }), Err(Error::InvalidStateForRequest)); } @@ -1024,7 +1023,6 @@ mod tests { nodes: BTreeMap::new(), is_zero: false, threshold: 1, - derived_point: Public::default().into(), }) }), Err(Error::InvalidMessage)); } @@ -1106,7 +1104,6 @@ mod tests { message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { session: SessionId::from([1u8; 32]).into(), session_nonce: 0, - derived_point: Public::default().into(), }), }); assert_eq!(session.process_message(&ml.0.node(1), &msg), Err(Error::ReplayProtection)); diff --git a/src/key_server_cluster/io/message.rs b/src/key_server_cluster/io/message.rs index fec5174..43f37c6 100644 --- a/src/key_server_cluster/io/message.rs +++ b/src/key_server_cluster/io/message.rs @@ -30,7 +30,7 @@ use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, En /// Size of serialized header. pub const MESSAGE_HEADER_SIZE: usize = 18; /// Current header version. -pub const CURRENT_HEADER_VERSION: u64 = 1; +pub const CURRENT_HEADER_VERSION: u64 = 2; /// Message header. #[derive(Debug, PartialEq)] @@ -71,11 +71,12 @@ pub fn serialize_message(message: Message) -> Result { Message::Generation(GenerationMessage::InitializeSession(payload)) => (50, serde_json::to_vec(&payload)), Message::Generation(GenerationMessage::ConfirmInitialization(payload)) => (51, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::CompleteInitialization(payload)) => (52, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::DerivedPointGeneration(payload)) => (52, serde_json::to_vec(&payload)), Message::Generation(GenerationMessage::KeysDissemination(payload)) => (53, serde_json::to_vec(&payload)), Message::Generation(GenerationMessage::PublicKeyShare(payload)) => (54, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::SessionError(payload)) => (55, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::SessionCompleted(payload)) => (56, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::JointPublicKey(payload)) => (55, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::SessionError(payload)) => (56, serde_json::to_vec(&payload)), + Message::Generation(GenerationMessage::SessionCompleted(payload)) => (57, serde_json::to_vec(&payload)), Message::Encryption(EncryptionMessage::InitializeEncryptionSession(payload)) => (100, serde_json::to_vec(&payload)), Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(payload)) => (101, serde_json::to_vec(&payload)), @@ -173,11 +174,12 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Message::Generation(GenerationMessage::InitializeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 51 => Message::Generation(GenerationMessage::ConfirmInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 52 => Message::Generation(GenerationMessage::CompleteInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 52 => Message::Generation(GenerationMessage::DerivedPointGeneration(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 53 => Message::Generation(GenerationMessage::KeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 54 => Message::Generation(GenerationMessage::PublicKeyShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 55 => Message::Generation(GenerationMessage::SessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 56 => Message::Generation(GenerationMessage::SessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 55 => Message::Generation(GenerationMessage::JointPublicKey(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 56 => Message::Generation(GenerationMessage::SessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), + 57 => Message::Generation(GenerationMessage::SessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 100 => Message::Encryption(EncryptionMessage::InitializeEncryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), 101 => Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), @@ -241,7 +243,7 @@ pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result Result { let mut header: Vec<_> = message.into(); let payload = header.split_off(MESSAGE_HEADER_SIZE); - let encrypted_payload = ecies::encrypt(key.public(), &[], &payload)?; + let encrypted_payload = encrypt_data(key, &payload)?; let header = deserialize_header(&header)?; build_serialized_message(header, encrypted_payload) @@ -249,7 +251,17 @@ pub fn encrypt_message(key: &KeyPair, message: SerializedMessage) -> Result) -> Result, Error> { - Ok(ecies::decrypt(key.secret(), &[], &payload)?) + decrypt_data(key, &payload) +} + +/// Encrypt data. +pub fn encrypt_data(key: &KeyPair, data: &[u8]) -> Result, Error> { + Ok(ecies::encrypt(key.public(), &[], data)?) +} + +/// Decrypt data. +pub fn decrypt_data(key: &KeyPair, data: &[u8]) -> Result, Error> { + Ok(ecies::decrypt(key.secret(), &[], data)?) } /// Fix shared encryption key. diff --git a/src/key_server_cluster/io/mod.rs b/src/key_server_cluster/io/mod.rs index 8df9bb9..23e8e42 100644 --- a/src/key_server_cluster/io/mod.rs +++ b/src/key_server_cluster/io/mod.rs @@ -26,7 +26,7 @@ mod write_message; pub use self::deadline::{deadline, Deadline, DeadlineStatus}; pub use self::handshake::{handshake, accept_handshake, Handshake, HandshakeResult}; pub use self::message::{MessageHeader, SerializedMessage, serialize_message, deserialize_message, - encrypt_message, fix_shared_key}; + encrypt_message, fix_shared_key, encrypt_data, decrypt_data}; pub use self::read_header::{read_header, ReadHeader}; pub use self::read_payload::{read_payload, read_encrypted_payload, ReadPayload}; pub use self::read_message::{read_message, read_encrypted_message, ReadMessage}; diff --git a/src/key_server_cluster/math.rs b/src/key_server_cluster/math.rs index a8e7429..d89b98a 100644 --- a/src/key_server_cluster/math.rs +++ b/src/key_server_cluster/math.rs @@ -14,9 +14,11 @@ // You should have received a copy of the GNU General Public License // along with Parity Secret Store. If not, see . +use std::collections::BTreeMap; use crypto::publickey::{Public, Secret, Signature, Random, Generator, ec_math_utils}; use ethereum_types::{H256, U256, BigEndianHash}; use hash::keccak; +use tiny_keccak::Keccak; use key_server_cluster::Error; /// Encryption result. @@ -118,11 +120,6 @@ pub fn compute_shadow_mul<'a, I>(coeff: &Secret, self_secret: &Secret, mut other Ok(shadow_mul) } -/// Update point by multiplying to random scalar -pub fn update_random_point(point: &mut Public) -> Result<(), Error> { - Ok(ec_math_utils::public_mul_secret(point, &generate_random_scalar()?)?) -} - /// Generate random polynom of threshold degree pub fn generate_random_polynom(threshold: usize) -> Result, Error> { (0..threshold + 1) @@ -203,6 +200,36 @@ pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &S Ok(left == right) } +/// Verifies that public share proof is valid. +pub fn share_proof_verification(threshold: usize, number_id: &Secret, secret1: &Secret, share_proof: &[Public]) -> Result { + // calculate left part + let mut left = ec_math_utils::generation_point(); + ec_math_utils::public_mul_secret(&mut left, secret1)?; + + // calculate right part + let mut right = share_proof[0].clone(); + for i in 1..threshold + 1 { + let mut secret_pow = number_id.clone(); + secret_pow.pow(i)?; + + let mut public_k = share_proof[i].clone(); + ec_math_utils::public_mul_secret(&mut public_k, &secret_pow)?; + + ec_math_utils::public_add(&mut right, &public_k)?; + } + + Ok(left == right) +} + +/// Prepares public share proof. +pub fn prepare_share_proof(polynom1: &[Secret]) -> Result, Error> { + let mut share_proof = Vec::with_capacity(polynom1.len()); + for coeff in polynom1 { + share_proof.push(compute_public_share(coeff)?); + } + Ok(share_proof) +} + /// Compute secret subshare from passed secret value. pub fn compute_secret_subshare<'a, I>(threshold: usize, secret_value: &Secret, sender_id_number: &Secret, other_id_numbers: I) -> Result where I: Iterator { let mut subshare = compute_shadow_mul(secret_value, sender_id_number, other_id_numbers)?; @@ -536,6 +563,21 @@ pub fn compute_ecdsa_inversed_secret_coeff_from_shares(t: usize, id_numbers: &[S Ok(u_inv) } +/// Computes footprint of all publics received from all nodes. +pub fn compute_publics_footprint(nodes: BTreeMap>) -> Result { + let mut publics_keccak = Keccak::new_keccak256(); + for (_, publics) in nodes { + for public in publics { + publics_keccak.update(public.as_bytes()); + } + } + + let mut publics_keccak_value = [0u8; 32]; + publics_keccak.finalize(&mut publics_keccak_value); + + Ok(publics_keccak_value.into()) +} + #[cfg(test)] pub mod tests { use std::iter::once; @@ -1098,5 +1140,4 @@ pub mod tests { Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap() ); } - } diff --git a/src/key_server_cluster/message.rs b/src/key_server_cluster/message.rs index 6628efd..28c12b2 100644 --- a/src/key_server_cluster/message.rs +++ b/src/key_server_cluster/message.rs @@ -67,18 +67,29 @@ pub enum GenerationMessage { InitializeSession(InitializeSession), /// Confirm DKG session initialization. ConfirmInitialization(ConfirmInitialization), - /// Broadcast data, calculated during session initialization phase. - CompleteInitialization(CompleteInitialization), + /// Confirm DKG session initialization. + DerivedPointGeneration(DerivedPointGeneration), /// Generated keys are sent to every node. KeysDissemination(KeysDissemination), /// Broadcast self public key portion. PublicKeyShare(PublicKeyShare), + /// Confirm that the joint public key has been computed. + JointPublicKey(JointPublicKey), /// When session error has occured. SessionError(SessionError), /// When session is completed. SessionCompleted(SessionCompleted), } +/// Random point generation message. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub enum RandomPointGenerationMessage { + /// Encrypted share from the node. + EncryptedShare(RandomPointGenerationEncryptedShare), + /// Share decryption key from the node. + DecryptionKey(RandomPointGenerationDecryptionKey), +} + /// All possible messages that can be sent during encryption session. #[derive(Clone, Debug)] pub enum EncryptionMessage { @@ -283,11 +294,6 @@ pub struct InitializeSession { /// Decryption threshold. During decryption threshold-of-route.len() nodes must came to /// consensus to successfully decrypt message. pub threshold: usize, - /// Derived generation point. Starting from originator, every node must multiply this - /// point by random scalar (unknown by other nodes). At the end of initialization - /// `point` will be some (k1 * k2 * ... * kn) * G = `point` where `(k1 * k2 * ... * kn)` - /// is unknown for every node. - pub derived_point: SerializablePublic, } /// Confirm DKG session initialization. @@ -297,19 +303,18 @@ pub struct ConfirmInitialization { pub session: MessageSessionId, /// Session-level nonce. pub session_nonce: u64, - /// Derived generation point. - pub derived_point: SerializablePublic, } + /// Broadcast generated point to every other node. #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CompleteInitialization { +pub struct DerivedPointGeneration { /// Session Id. pub session: MessageSessionId, /// Session-level nonce. pub session_nonce: u64, - /// Derived generation point. - pub derived_point: SerializablePublic, + /// Wrapped random point generation message. + pub message: RandomPointGenerationMessage, } /// Generated keys are sent to every node. @@ -334,8 +339,21 @@ pub struct PublicKeyShare { pub session: MessageSessionId, /// Session-level nonce. pub session_nonce: u64, - /// Public key share. - pub public_share: SerializablePublic, + /// Hash of all publics received from all nodes. + pub publics_footprint: SerializableH256, + /// Public key share proof. + pub public_share_proof: Vec, +} + +/// Node is sharing joint public key that it has computed. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct JointPublicKey { + /// Session Id. + pub session: MessageSessionId, + /// Session-level nonce. + pub session_nonce: u64, + /// Public key that has been computed from all nodes public shares. + pub joint_public_footprint: SerializableH256, } /// When session error has occured. @@ -1062,6 +1080,20 @@ pub enum FailedKeyVersionContinueAction { Decrypt(Option, SerializableAddress), } +/// Encrypted share from the node. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RandomPointGenerationEncryptedShare { + /// Encrypted share. + pub encrypted_share: Vec, +} + +/// Share decryption key from the node. +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RandomPointGenerationDecryptionKey { + /// Decryption key. + pub decryption_key: SerializableSecret, +} + impl Message { pub fn is_initialization_message(&self) -> bool { match *self { @@ -1143,9 +1175,10 @@ impl GenerationMessage { match *self { GenerationMessage::InitializeSession(ref msg) => &msg.session, GenerationMessage::ConfirmInitialization(ref msg) => &msg.session, - GenerationMessage::CompleteInitialization(ref msg) => &msg.session, + GenerationMessage::DerivedPointGeneration(ref msg) => &msg.session, GenerationMessage::KeysDissemination(ref msg) => &msg.session, GenerationMessage::PublicKeyShare(ref msg) => &msg.session, + GenerationMessage::JointPublicKey(ref msg) => &msg.session, GenerationMessage::SessionError(ref msg) => &msg.session, GenerationMessage::SessionCompleted(ref msg) => &msg.session, } @@ -1155,9 +1188,10 @@ impl GenerationMessage { match *self { GenerationMessage::InitializeSession(ref msg) => msg.session_nonce, GenerationMessage::ConfirmInitialization(ref msg) => msg.session_nonce, - GenerationMessage::CompleteInitialization(ref msg) => msg.session_nonce, + GenerationMessage::DerivedPointGeneration(ref msg) => msg.session_nonce, GenerationMessage::KeysDissemination(ref msg) => msg.session_nonce, GenerationMessage::PublicKeyShare(ref msg) => msg.session_nonce, + GenerationMessage::JointPublicKey(ref msg) => msg.session_nonce, GenerationMessage::SessionError(ref msg) => msg.session_nonce, GenerationMessage::SessionCompleted(ref msg) => msg.session_nonce, } @@ -1423,9 +1457,10 @@ impl fmt::Display for GenerationMessage { match *self { GenerationMessage::InitializeSession(_) => write!(f, "InitializeSession"), GenerationMessage::ConfirmInitialization(_) => write!(f, "ConfirmInitialization"), - GenerationMessage::CompleteInitialization(_) => write!(f, "CompleteInitialization"), + GenerationMessage::DerivedPointGeneration(ref msg) => write!(f, "DerivedPointGeneration({})", msg.message), GenerationMessage::KeysDissemination(_) => write!(f, "KeysDissemination"), GenerationMessage::PublicKeyShare(_) => write!(f, "PublicKeyShare"), + GenerationMessage::JointPublicKey(_) => write!(f, "JointPublicKey"), GenerationMessage::SessionError(ref msg) => write!(f, "SessionError({})", msg.error), GenerationMessage::SessionCompleted(_) => write!(f, "SessionCompleted"), } @@ -1555,3 +1590,12 @@ impl fmt::Display for KeyVersionNegotiationMessage { } } } + +impl fmt::Display for RandomPointGenerationMessage { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + RandomPointGenerationMessage::EncryptedShare(_) => write!(f, "EncryptedShare"), + RandomPointGenerationMessage::DecryptionKey(_) => write!(f, "DecryptionKey"), + } + } +} diff --git a/src/key_server_cluster/mod.rs b/src/key_server_cluster/mod.rs index 009a65b..fa44559 100644 --- a/src/key_server_cluster/mod.rs +++ b/src/key_server_cluster/mod.rs @@ -67,6 +67,7 @@ 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::random_point_generation_session; pub use self::client_sessions::signing_session_ecdsa; pub use self::client_sessions::signing_session_schnorr;