SecretStore: use random key to encrypt channel + session-level nonce (#6470)

* generate random channel encryption key on restart

* session-level nonces

* fixed warning after rebase

* session_nonce -> nonce
This commit is contained in:
Svyatoslav Nikolsky 2017-09-14 20:29:01 +03:00 committed by Gav Wood
parent 75b6a31e87
commit e3fc3ccada
10 changed files with 556 additions and 103 deletions

View File

@ -330,13 +330,13 @@ impl ClusterCore {
finished(Ok(())).boxed() finished(Ok(())).boxed()
}, },
Ok((_, Err(err))) => { Ok((_, Err(err))) => {
warn!(target: "secretstore_net", "{}: protocol error {} when reading message from node {}", data.self_key_pair.public(), err, connection.node_id()); warn!(target: "secretstore_net", "{}: protocol error '{}' when reading message from node {}", data.self_key_pair.public(), err, connection.node_id());
// continue serving connection // continue serving connection
data.spawn(ClusterCore::process_connection_messages(data.clone(), connection)); data.spawn(ClusterCore::process_connection_messages(data.clone(), connection));
finished(Err(err)).boxed() finished(Err(err)).boxed()
}, },
Err(err) => { Err(err) => {
warn!(target: "secretstore_net", "{}: network error {} when reading message from node {}", data.self_key_pair.public(), err, connection.node_id()); warn!(target: "secretstore_net", "{}: network error '{}' when reading message from node {}", data.self_key_pair.public(), err, connection.node_id());
// close connection // close connection
data.connections.remove(connection.node_id(), connection.is_inbound()); data.connections.remove(connection.node_id(), connection.is_inbound());
failed(err).boxed() failed(err).boxed()
@ -381,7 +381,7 @@ impl ClusterCore {
} }
}, },
Ok(DeadlineStatus::Meet(Err(err))) => { Ok(DeadlineStatus::Meet(Err(err))) => {
warn!(target: "secretstore_net", "{}: protocol error {} when establishing {} connection{}", warn!(target: "secretstore_net", "{}: protocol error '{}' when establishing {} connection{}",
data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" },
outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default());
finished(Ok(())).boxed() finished(Ok(())).boxed()
@ -393,7 +393,7 @@ impl ClusterCore {
finished(Ok(())).boxed() finished(Ok(())).boxed()
}, },
Err(err) => { Err(err) => {
warn!(target: "secretstore_net", "{}: network error {} when establishing {} connection{}", warn!(target: "secretstore_net", "{}: network error '{}' when establishing {} connection{}",
data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" },
outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default());
finished(Ok(())).boxed() finished(Ok(())).boxed()
@ -417,6 +417,7 @@ impl ClusterCore {
/// Process single generation message from the connection. /// Process single generation message from the connection.
fn process_generation_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: GenerationMessage) { fn process_generation_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: GenerationMessage) {
let session_id = message.session_id().clone(); let session_id = message.session_id().clone();
let session_nonce = message.session_nonce();
let mut sender = connection.node_id().clone(); let mut sender = connection.node_id().clone();
let session = match message { let session = match message {
GenerationMessage::InitializeSession(_) => { GenerationMessage::InitializeSession(_) => {
@ -424,7 +425,19 @@ impl ClusterCore {
connected_nodes.insert(data.self_key_pair.public().clone()); connected_nodes.insert(data.self_key_pair.public().clone());
let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes));
data.sessions.new_generation_session(sender.clone(), session_id.clone(), cluster) match data.sessions.new_generation_session(sender.clone(), session_id.clone(), Some(session_nonce), cluster) {
Ok(session) => Ok(session),
Err(err) => {
// this is new session => it is not yet in container
warn!(target: "secretstore_net", "{}: generation session initialization error '{}' when requested for new session from node {}", data.self_key_pair.public(), err, sender);
data.spawn(connection.send_message(Message::Generation(GenerationMessage::SessionError(message::SessionError {
session: session_id.into(),
session_nonce: session_nonce,
error: format!("{:?}", err),
}))));
return;
},
}
}, },
_ => { _ => {
data.sessions.generation_sessions.get(&session_id) data.sessions.generation_sessions.get(&session_id)
@ -462,9 +475,10 @@ impl ClusterCore {
break; break;
}, },
Err(err) => { Err(err) => {
warn!(target: "secretstore_net", "{}: generation session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); warn!(target: "secretstore_net", "{}: generation session error '{}' when processing message {} from node {}", data.self_key_pair.public(), err, message, sender);
data.sessions.respond_with_generation_error(&session_id, message::SessionError { data.sessions.respond_with_generation_error(&session_id, message::SessionError {
session: session_id.clone().into(), session: session_id.clone().into(),
session_nonce: session_nonce,
error: format!("{:?}", err), error: format!("{:?}", err),
}); });
if err != Error::InvalidSessionId { if err != Error::InvalidSessionId {
@ -479,6 +493,7 @@ impl ClusterCore {
/// Process single encryption message from the connection. /// Process single encryption message from the connection.
fn process_encryption_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: EncryptionMessage) { fn process_encryption_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: EncryptionMessage) {
let session_id = message.session_id().clone(); let session_id = message.session_id().clone();
let session_nonce = message.session_nonce();
let mut sender = connection.node_id().clone(); let mut sender = connection.node_id().clone();
let session = match message { let session = match message {
EncryptionMessage::InitializeEncryptionSession(_) => { EncryptionMessage::InitializeEncryptionSession(_) => {
@ -486,7 +501,19 @@ impl ClusterCore {
connected_nodes.insert(data.self_key_pair.public().clone()); connected_nodes.insert(data.self_key_pair.public().clone());
let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes));
data.sessions.new_encryption_session(sender.clone(), session_id.clone(), cluster) match data.sessions.new_encryption_session(sender.clone(), session_id.clone(), Some(session_nonce), cluster) {
Ok(session) => Ok(session),
Err(err) => {
// this is new session => it is not yet in container
warn!(target: "secretstore_net", "{}: encryption session initialization error '{}' when requested for new session from node {}", data.self_key_pair.public(), err, sender);
data.spawn(connection.send_message(Message::Encryption(EncryptionMessage::EncryptionSessionError(message::EncryptionSessionError {
session: session_id.into(),
session_nonce: session_nonce,
error: format!("{:?}", err),
}))));
return;
},
}
}, },
_ => { _ => {
data.sessions.encryption_sessions.get(&session_id) data.sessions.encryption_sessions.get(&session_id)
@ -531,9 +558,10 @@ impl ClusterCore {
break; break;
}, },
Err(err) => { Err(err) => {
warn!(target: "secretstore_net", "{}: encryption session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); warn!(target: "secretstore_net", "{}: encryption session error '{}' when processing message {} from node {}", data.self_key_pair.public(), err, message, sender);
data.sessions.respond_with_encryption_error(&session_id, message::EncryptionSessionError { data.sessions.respond_with_encryption_error(&session_id, message::EncryptionSessionError {
session: session_id.clone().into(), session: session_id.clone().into(),
session_nonce: session_nonce,
error: format!("{:?}", err), error: format!("{:?}", err),
}); });
if err != Error::InvalidSessionId { if err != Error::InvalidSessionId {
@ -549,6 +577,7 @@ impl ClusterCore {
fn process_decryption_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: DecryptionMessage) { fn process_decryption_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: DecryptionMessage) {
let session_id = message.session_id().clone(); let session_id = message.session_id().clone();
let sub_session_id = message.sub_session_id().clone(); let sub_session_id = message.sub_session_id().clone();
let session_nonce = message.session_nonce();
let decryption_session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone()); let decryption_session_id = DecryptionSessionId::new(session_id.clone(), sub_session_id.clone());
let mut sender = connection.node_id().clone(); let mut sender = connection.node_id().clone();
let session = match message { let session = match message {
@ -560,7 +589,20 @@ impl ClusterCore {
connected_nodes.insert(data.self_key_pair.public().clone()); connected_nodes.insert(data.self_key_pair.public().clone());
let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes));
data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster, None) match data.sessions.new_decryption_session(sender.clone(), session_id.clone(), sub_session_id.clone(), Some(session_nonce), cluster, None) {
Ok(session) => Ok(session),
Err(err) => {
// this is new session => it is not yet in container
warn!(target: "secretstore_net", "{}: decryption session initialization error '{}' when requested for new session from node {}", data.self_key_pair.public(), err, sender);
data.spawn(connection.send_message(Message::Decryption(DecryptionMessage::DecryptionSessionError(message::DecryptionSessionError {
session: session_id.into(),
sub_session: sub_session_id.clone().into(),
session_nonce: session_nonce,
error: format!("{:?}", err),
}))));
return;
},
}
}, },
_ => { _ => {
data.sessions.decryption_sessions.get(&decryption_session_id) data.sessions.decryption_sessions.get(&decryption_session_id)
@ -589,10 +631,11 @@ impl ClusterCore {
} }
}, },
Err(err) => { Err(err) => {
warn!(target: "secretstore_net", "{}: decryption session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); warn!(target: "secretstore_net", "{}: decryption session error '{}' when processing message {} from node {}", data.self_key_pair.public(), err, message, sender);
data.sessions.respond_with_decryption_error(&session_id, &sub_session_id, &sender, message::DecryptionSessionError { data.sessions.respond_with_decryption_error(&session_id, &sub_session_id, &sender, message::DecryptionSessionError {
session: session_id.clone().into(), session: session_id.clone().into(),
sub_session: sub_session_id.clone().into(), sub_session: sub_session_id.clone().into(),
session_nonce: session_nonce,
error: format!("{:?}", err), error: format!("{:?}", err),
}); });
if err != Error::InvalidSessionId { if err != Error::InvalidSessionId {
@ -608,6 +651,7 @@ impl ClusterCore {
fn process_signing_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: SigningMessage) { fn process_signing_message(data: Arc<ClusterData>, connection: Arc<Connection>, mut message: SigningMessage) {
let session_id = message.session_id().clone(); let session_id = message.session_id().clone();
let sub_session_id = message.sub_session_id().clone(); let sub_session_id = message.sub_session_id().clone();
let session_nonce = message.session_nonce();
let signing_session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone()); let signing_session_id = SigningSessionId::new(session_id.clone(), sub_session_id.clone());
let mut sender = connection.node_id().clone(); let mut sender = connection.node_id().clone();
let session = match message { let session = match message {
@ -619,7 +663,20 @@ impl ClusterCore {
connected_nodes.insert(data.self_key_pair.public().clone()); connected_nodes.insert(data.self_key_pair.public().clone());
let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes)); let cluster = Arc::new(ClusterView::new(data.clone(), connected_nodes));
data.sessions.new_signing_session(sender.clone(), session_id.clone(), sub_session_id.clone(), cluster, None) match data.sessions.new_signing_session(sender.clone(), session_id.clone(), sub_session_id.clone(), Some(session_nonce), cluster, None) {
Ok(session) => Ok(session),
Err(err) => {
// this is new session => it is not yet in container
warn!(target: "secretstore_net", "{}: signing session initialization error '{}' when requested for new session from node {}", data.self_key_pair.public(), err, sender);
data.spawn(connection.send_message(Message::Signing(SigningMessage::SigningSessionError(message::SigningSessionError {
session: session_id.into(),
sub_session: sub_session_id.clone().into(),
session_nonce: session_nonce,
error: format!("{:?}", err),
}))));
return;
},
}
}, },
_ => { _ => {
data.sessions.signing_sessions.get(&signing_session_id) data.sessions.signing_sessions.get(&signing_session_id)
@ -654,10 +711,11 @@ impl ClusterCore {
break; break;
}, },
Err(err) => { Err(err) => {
warn!(target: "secretstore_net", "{}: signing session error {} when processing message {} from node {}", data.self_key_pair.public(), err, message, sender); warn!(target: "secretstore_net", "{}: signing session error '{}' when processing message {} from node {}", data.self_key_pair.public(), err, message, sender);
data.sessions.respond_with_signing_error(&session_id, &sub_session_id, &sender, message::SigningSessionError { data.sessions.respond_with_signing_error(&session_id, &sub_session_id, &sender, message::SigningSessionError {
session: session_id.clone().into(), session: session_id.clone().into(),
sub_session: sub_session_id.clone().into(), sub_session: sub_session_id.clone().into(),
session_nonce: session_nonce,
error: format!("{:?}", err), error: format!("{:?}", err),
}); });
if err != Error::InvalidSessionId { if err != Error::InvalidSessionId {
@ -929,7 +987,7 @@ impl ClusterClient for ClusterClientImpl {
connected_nodes.insert(self.data.self_key_pair.public().clone()); connected_nodes.insert(self.data.self_key_pair.public().clone());
let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone()));
let session = self.data.sessions.new_generation_session(self.data.self_key_pair.public().clone(), session_id, cluster)?; let session = self.data.sessions.new_generation_session(self.data.self_key_pair.public().clone(), session_id, None, cluster)?;
session.initialize(author, threshold, connected_nodes)?; session.initialize(author, threshold, connected_nodes)?;
Ok(GenerationSessionWrapper::new(Arc::downgrade(&self.data), session_id, session)) Ok(GenerationSessionWrapper::new(Arc::downgrade(&self.data), session_id, session))
} }
@ -939,7 +997,7 @@ impl ClusterClient for ClusterClientImpl {
connected_nodes.insert(self.data.self_key_pair.public().clone()); connected_nodes.insert(self.data.self_key_pair.public().clone());
let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone()));
let session = self.data.sessions.new_encryption_session(self.data.self_key_pair.public().clone(), session_id, cluster)?; let session = self.data.sessions.new_encryption_session(self.data.self_key_pair.public().clone(), session_id, None, cluster)?;
session.initialize(requestor_signature, common_point, encrypted_point)?; session.initialize(requestor_signature, common_point, encrypted_point)?;
Ok(EncryptionSessionWrapper::new(Arc::downgrade(&self.data), session_id, session)) Ok(EncryptionSessionWrapper::new(Arc::downgrade(&self.data), session_id, session))
} }
@ -950,7 +1008,7 @@ impl ClusterClient for ClusterClientImpl {
let access_key = Random.generate()?.secret().clone(); let access_key = Random.generate()?.secret().clone();
let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone()));
let session = self.data.sessions.new_decryption_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster, Some(requestor_signature))?; let session = self.data.sessions.new_decryption_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), None, cluster, Some(requestor_signature))?;
session.initialize(is_shadow_decryption)?; session.initialize(is_shadow_decryption)?;
Ok(DecryptionSessionWrapper::new(Arc::downgrade(&self.data), DecryptionSessionId::new(session_id, access_key), session)) Ok(DecryptionSessionWrapper::new(Arc::downgrade(&self.data), DecryptionSessionId::new(session_id, access_key), session))
} }
@ -961,7 +1019,7 @@ impl ClusterClient for ClusterClientImpl {
let access_key = Random.generate()?.secret().clone(); let access_key = Random.generate()?.secret().clone();
let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone())); let cluster = Arc::new(ClusterView::new(self.data.clone(), connected_nodes.clone()));
let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), cluster, Some(requestor_signature))?; let session = self.data.sessions.new_signing_session(self.data.self_key_pair.public().clone(), session_id, access_key.clone(), None, cluster, Some(requestor_signature))?;
session.initialize(message_hash)?; session.initialize(message_hash)?;
Ok(SigningSessionWrapper::new(Arc::downgrade(&self.data), SigningSessionId::new(session_id, access_key), session)) Ok(SigningSessionWrapper::new(Arc::downgrade(&self.data), SigningSessionId::new(session_id, access_key), session))
} }
@ -1147,6 +1205,7 @@ pub mod tests {
#[test] #[test]
fn generation_session_is_removed_when_succeeded() { fn generation_session_is_removed_when_succeeded() {
//::logger::init_log();
let mut core = Core::new().unwrap(); let mut core = Core::new().unwrap();
let clusters = make_clusters(&core, 6019, 3); let clusters = make_clusters(&core, 6019, 3);
run_clusters(&clusters); run_clusters(&clusters);
@ -1154,14 +1213,16 @@ pub mod tests {
// start && wait for generation session to complete // start && wait for generation session to complete
let session = clusters[0].client().new_generation_session(SessionId::default(), Public::default(), 1).unwrap(); 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); loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished
|| session.state() == GenerationSessionState::Failed);
assert!(session.joint_public_and_secret().unwrap().is_ok()); assert!(session.joint_public_and_secret().unwrap().is_ok());
// check that session is either removed from all nodes, or nonexistent (already removed) // check that session is either removed from all nodes, or nonexistent (already removed)
assert!(clusters[0].client().generation_session(&SessionId::default()).is_none()); assert!(clusters[0].client().generation_session(&SessionId::default()).is_none());
for i in 1..3 { for i in 1..3 {
if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) { if let Some(session) = clusters[i].client().generation_session(&SessionId::default()) {
loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished); loop_until(&mut core, time::Duration::from_millis(300), || session.state() == GenerationSessionState::Finished
|| session.state() == GenerationSessionState::Failed);
assert!(session.joint_public_and_secret().unwrap().is_err()); assert!(session.joint_public_and_secret().unwrap().is_err());
assert!(clusters[i].client().generation_session(&SessionId::default()).is_none()); assert!(clusters[i].client().generation_session(&SessionId::default()).is_none());
} }

View File

@ -16,7 +16,7 @@
use std::time; use std::time;
use std::sync::{Arc, Weak}; use std::sync::{Arc, Weak};
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::collections::{VecDeque, BTreeSet, BTreeMap}; use std::collections::{VecDeque, BTreeSet, BTreeMap};
use parking_lot::RwLock; use parking_lot::RwLock;
use ethkey::{Public, Secret, Signature}; use ethkey::{Public, Secret, Signature};
@ -68,6 +68,18 @@ pub struct ClusterSessions {
acl_storage: Arc<AclStorage>, acl_storage: Arc<AclStorage>,
/// Make faulty generation sessions. /// Make faulty generation sessions.
make_faulty_generation_sessions: AtomicBool, make_faulty_generation_sessions: AtomicBool,
/// Always-increasing sessions counter. Is used as session nonce to prevent replay attacks:
/// 1) during handshake, KeyServers generate new random key to encrypt messages
/// => there's no way to use messages from previous connections for replay attacks
/// 2) when session (of any type) is started, master node increases its own session counter and broadcasts it
/// 3) when slave KeyServer receives session initialization message, it checks that new nonce is larger than previous (from the same master)
/// => there's no way to use messages from previous sessions for replay attacks
/// 4) KeyServer checks that each session message contains the same nonce that initialization message
/// Given that: (A) handshake is secure and (B) session itself is initially replay-protected
/// => this guarantees that sessions are replay-protected.
session_counter: AtomicUsize,
/// Maximal session nonce, received from given connection.
max_nonce: RwLock<BTreeMap<NodeId, u64>>,
} }
/// Active sessions container. /// Active sessions container.
@ -143,6 +155,8 @@ impl ClusterSessions {
decryption_sessions: ClusterSessionsContainer::new(), decryption_sessions: ClusterSessionsContainer::new(),
signing_sessions: ClusterSessionsContainer::new(), signing_sessions: ClusterSessionsContainer::new(),
make_faulty_generation_sessions: AtomicBool::new(false), make_faulty_generation_sessions: AtomicBool::new(false),
session_counter: AtomicUsize::new(0),
max_nonce: RwLock::new(BTreeMap::new()),
} }
} }
@ -152,11 +166,12 @@ impl ClusterSessions {
} }
/// Create new generation session. /// Create new generation session.
pub fn new_generation_session(&self, master: NodeId, session_id: SessionId, cluster: Arc<ClusterView>) -> Result<Arc<GenerationSessionImpl>, Error> { pub fn new_generation_session(&self, master: NodeId, session_id: SessionId, nonce: Option<u64>, cluster: Arc<ClusterView>) -> Result<Arc<GenerationSessionImpl>, Error> {
// check that there's no finished encryption session with the same id // check that there's no finished encryption session with the same id
if self.key_storage.contains(&session_id) { if self.key_storage.contains(&session_id) {
return Err(Error::DuplicateSessionId); return Err(Error::DuplicateSessionId);
} }
// communicating to all other nodes is crucial for encryption session // communicating to all other nodes is crucial for encryption session
// => check that we have connections to all cluster nodes // => check that we have connections to all cluster nodes
if self.nodes.iter().any(|n| !cluster.is_connected(n)) { if self.nodes.iter().any(|n| !cluster.is_connected(n)) {
@ -164,12 +179,14 @@ impl ClusterSessions {
} }
// check that there's no active encryption session with the same id // check that there's no active encryption session with the same id
let nonce = self.check_session_nonce(&master, nonce)?;
self.generation_sessions.insert(master, session_id, cluster.clone(), move || self.generation_sessions.insert(master, session_id, cluster.clone(), move ||
Ok(GenerationSessionImpl::new(GenerationSessionParams { Ok(GenerationSessionImpl::new(GenerationSessionParams {
id: session_id.clone(), id: session_id.clone(),
self_node_id: self.self_node_id.clone(), self_node_id: self.self_node_id.clone(),
key_storage: Some(self.key_storage.clone()), key_storage: Some(self.key_storage.clone()),
cluster: cluster, cluster: cluster,
nonce: Some(nonce),
}))) })))
.map(|session| { .map(|session| {
if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { if self.make_faulty_generation_sessions.load(Ordering::Relaxed) {
@ -192,14 +209,17 @@ impl ClusterSessions {
} }
/// Create new encryption session. /// Create new encryption session.
pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, cluster: Arc<ClusterView>) -> Result<Arc<EncryptionSessionImpl>, Error> { pub fn new_encryption_session(&self, master: NodeId, session_id: SessionId, nonce: Option<u64>, cluster: Arc<ClusterView>) -> Result<Arc<EncryptionSessionImpl>, Error> {
let encrypted_data = self.read_key_share(&session_id, &cluster)?; let encrypted_data = self.read_key_share(&session_id, &cluster)?;
let nonce = self.check_session_nonce(&master, nonce)?;
self.encryption_sessions.insert(master, session_id, cluster.clone(), move || EncryptionSessionImpl::new(EncryptionSessionParams { self.encryption_sessions.insert(master, session_id, cluster.clone(), move || EncryptionSessionImpl::new(EncryptionSessionParams {
id: session_id.clone(), id: session_id.clone(),
self_node_id: self.self_node_id.clone(), self_node_id: self.self_node_id.clone(),
encrypted_data: encrypted_data, encrypted_data: encrypted_data,
key_storage: self.key_storage.clone(), key_storage: self.key_storage.clone(),
cluster: cluster, cluster: cluster,
nonce: nonce,
})) }))
} }
@ -216,9 +236,10 @@ impl ClusterSessions {
} }
/// Create new decryption session. /// Create new decryption session.
pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc<ClusterView>, requester_signature: Option<Signature>) -> Result<Arc<DecryptionSessionImpl>, Error> { pub fn new_decryption_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, nonce: Option<u64>, cluster: Arc<ClusterView>, requester_signature: Option<Signature>) -> Result<Arc<DecryptionSessionImpl>, Error> {
let session_id = DecryptionSessionId::new(session_id, sub_session_id); let session_id = DecryptionSessionId::new(session_id, sub_session_id);
let encrypted_data = self.read_key_share(&session_id.id, &cluster)?; let encrypted_data = self.read_key_share(&session_id.id, &cluster)?;
let nonce = self.check_session_nonce(&master, nonce)?;
self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), move || DecryptionSessionImpl::new(DecryptionSessionParams { self.decryption_sessions.insert(master, session_id.clone(), cluster.clone(), move || DecryptionSessionImpl::new(DecryptionSessionParams {
meta: SessionMeta { meta: SessionMeta {
@ -231,6 +252,7 @@ impl ClusterSessions {
key_share: encrypted_data, key_share: encrypted_data,
acl_storage: self.acl_storage.clone(), acl_storage: self.acl_storage.clone(),
cluster: cluster, cluster: cluster,
nonce: nonce,
}, requester_signature)) }, requester_signature))
} }
@ -253,9 +275,10 @@ impl ClusterSessions {
} }
/// Create new signing session. /// Create new signing session.
pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, cluster: Arc<ClusterView>, requester_signature: Option<Signature>) -> Result<Arc<SigningSessionImpl>, Error> { pub fn new_signing_session(&self, master: NodeId, session_id: SessionId, sub_session_id: Secret, nonce: Option<u64>, cluster: Arc<ClusterView>, requester_signature: Option<Signature>) -> Result<Arc<SigningSessionImpl>, Error> {
let session_id = SigningSessionId::new(session_id, sub_session_id); let session_id = SigningSessionId::new(session_id, sub_session_id);
let encrypted_data = self.read_key_share(&session_id.id, &cluster)?; let encrypted_data = self.read_key_share(&session_id.id, &cluster)?;
let nonce = self.check_session_nonce(&master, nonce)?;
self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), move || SigningSessionImpl::new(SigningSessionParams { self.signing_sessions.insert(master, session_id.clone(), cluster.clone(), move || SigningSessionImpl::new(SigningSessionParams {
meta: SessionMeta { meta: SessionMeta {
@ -268,6 +291,7 @@ impl ClusterSessions {
key_share: encrypted_data, key_share: encrypted_data,
acl_storage: self.acl_storage.clone(), acl_storage: self.acl_storage.clone(),
cluster: cluster, cluster: cluster,
nonce: nonce,
}, requester_signature)) }, requester_signature))
} }
@ -303,6 +327,7 @@ impl ClusterSessions {
self.encryption_sessions.on_connection_timeout(node_id); self.encryption_sessions.on_connection_timeout(node_id);
self.decryption_sessions.on_connection_timeout(node_id); self.decryption_sessions.on_connection_timeout(node_id);
self.signing_sessions.on_connection_timeout(node_id); self.signing_sessions.on_connection_timeout(node_id);
self.max_nonce.write().remove(node_id);
} }
/// Read key share && remove disconnected nodes. /// Read key share && remove disconnected nodes.
@ -317,6 +342,21 @@ impl ClusterSessions {
} }
Ok(encrypted_data) Ok(encrypted_data)
} }
/// Check or generate new session nonce.
fn check_session_nonce(&self, master: &NodeId, nonce: Option<u64>) -> Result<u64, Error> {
// if we're master node of the session, then nonce should be generated
// if we're slave node of the session, then nonce should be passed from outside
debug_assert!((master == &self.self_node_id) == nonce.is_none());
match nonce {
Some(nonce) => match nonce > *self.max_nonce.write().entry(master.clone()).or_insert(0) {
true => Ok(nonce),
false => Err(Error::ReplayProtection),
},
None => Ok(self.session_counter.fetch_add(1, Ordering::Relaxed) as u64 + 1),
}
}
} }
impl<K, V, M> ClusterSessionsContainer<K, V, M> where K: Clone + Ord, V: ClusterSession { impl<K, V, M> ClusterSessionsContainer<K, V, M> where K: Clone + Ord, V: ClusterSession {

View File

@ -59,6 +59,8 @@ struct SessionCore {
pub key_share: DocumentKeyShare, pub key_share: DocumentKeyShare,
/// Cluster which allows this node to send messages to other nodes in the cluster. /// Cluster which allows this node to send messages to other nodes in the cluster.
pub cluster: Arc<Cluster>, pub cluster: Arc<Cluster>,
/// Session-level nonce.
pub nonce: u64,
/// SessionImpl completion condvar. /// SessionImpl completion condvar.
pub completed: Condvar, pub completed: Condvar,
} }
@ -95,8 +97,10 @@ pub struct SessionParams {
pub key_share: DocumentKeyShare, pub key_share: DocumentKeyShare,
/// ACL storage. /// ACL storage.
pub acl_storage: Arc<AclStorage>, pub acl_storage: Arc<AclStorage>,
/// Cluster /// Cluster.
pub cluster: Arc<Cluster>, pub cluster: Arc<Cluster>,
/// Session nonce.
pub nonce: u64,
} }
/// Decryption consensus transport. /// Decryption consensus transport.
@ -105,6 +109,8 @@ struct DecryptionConsensusTransport {
id: SessionId, id: SessionId,
/// Session access key. /// Session access key.
access_key: Secret, access_key: Secret,
/// Session-level nonce.
nonce: u64,
/// Cluster. /// Cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
} }
@ -115,6 +121,8 @@ struct DecryptionJobTransport {
id: SessionId, id: SessionId,
//// Session access key. //// Session access key.
access_key: Secret, access_key: Secret,
/// Session-level nonce.
nonce: u64,
/// Cluster. /// Cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
} }
@ -140,6 +148,7 @@ impl SessionImpl {
let consensus_transport = DecryptionConsensusTransport { let consensus_transport = DecryptionConsensusTransport {
id: params.meta.id.clone(), id: params.meta.id.clone(),
access_key: params.access_key.clone(), access_key: params.access_key.clone(),
nonce: params.nonce,
cluster: params.cluster.clone(), cluster: params.cluster.clone(),
}; };
@ -149,6 +158,7 @@ impl SessionImpl {
access_key: params.access_key, access_key: params.access_key,
key_share: params.key_share, key_share: params.key_share,
cluster: params.cluster, cluster: params.cluster,
nonce: params.nonce,
completed: Condvar::new(), completed: Condvar::new(),
}, },
data: Mutex::new(SessionData { data: Mutex::new(SessionData {
@ -213,6 +223,10 @@ impl SessionImpl {
/// Process decryption message. /// Process decryption message.
pub fn process_message(&self, sender: &NodeId, message: &DecryptionMessage) -> Result<(), Error> { pub fn process_message(&self, sender: &NodeId, message: &DecryptionMessage) -> Result<(), Error> {
if self.core.nonce != message.session_nonce() {
return Err(Error::ReplayProtection);
}
match message { match message {
&DecryptionMessage::DecryptionConsensusMessage(ref message) => &DecryptionMessage::DecryptionConsensusMessage(ref message) =>
self.on_consensus_message(sender, message), self.on_consensus_message(sender, message),
@ -286,6 +300,7 @@ impl SessionImpl {
self.core.cluster.send(&node, Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { self.core.cluster.send(&node, Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted {
session: self.core.meta.id.clone().into(), session: self.core.meta.id.clone().into(),
sub_session: self.core.access_key.clone().into(), sub_session: self.core.access_key.clone().into(),
session_nonce: self.core.nonce,
})))?; })))?;
} }
@ -380,6 +395,7 @@ impl SessionCore {
DecryptionJobTransport { DecryptionJobTransport {
id: self.meta.id.clone(), id: self.meta.id.clone(),
access_key: self.access_key.clone(), access_key: self.access_key.clone(),
nonce: self.nonce,
cluster: self.cluster.clone() cluster: self.cluster.clone()
} }
} }
@ -399,6 +415,7 @@ impl JobTransport for DecryptionConsensusTransport {
self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession {
requestor_signature: request.into(), requestor_signature: request.into(),
}) })
@ -409,6 +426,7 @@ impl JobTransport for DecryptionConsensusTransport {
self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization {
is_confirmed: response, is_confirmed: response,
}) })
@ -424,6 +442,7 @@ impl JobTransport for DecryptionJobTransport {
self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
request_id: request.id.into(), request_id: request.id.into(),
is_shadow_decryption: request.is_shadow_decryption, is_shadow_decryption: request.is_shadow_decryption,
nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(),
@ -434,6 +453,7 @@ impl JobTransport for DecryptionJobTransport {
self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
request_id: response.request_id.into(), request_id: response.request_id.into(),
shadow_point: response.shadow_point.into(), shadow_point: response.shadow_point.into(),
decrypt_shadow: response.decrypt_shadow, decrypt_shadow: response.decrypt_shadow,
@ -535,7 +555,8 @@ mod tests {
access_key: access_key.clone(), access_key: access_key.clone(),
key_share: encrypted_datas[i].clone(), key_share: encrypted_datas[i].clone(),
acl_storage: acl_storages[i].clone(), acl_storage: acl_storages[i].clone(),
cluster: clusters[i].clone() cluster: clusters[i].clone(),
nonce: 0,
}, if i == 0 { signature.clone() } else { None }).unwrap()).collect(); }, if i == 0 { signature.clone() } else { None }).unwrap()).collect();
(requester, clusters, acl_storages, sessions) (requester, clusters, acl_storages, sessions)
@ -584,6 +605,7 @@ mod tests {
}, },
acl_storage: Arc::new(DummyAclStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()),
cluster: Arc::new(DummyCluster::new(self_node_id.clone())), cluster: Arc::new(DummyCluster::new(self_node_id.clone())),
nonce: 0,
}, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) {
Ok(_) => (), Ok(_) => (),
_ => panic!("unexpected"), _ => panic!("unexpected"),
@ -614,6 +636,7 @@ mod tests {
}, },
acl_storage: Arc::new(DummyAclStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()),
cluster: Arc::new(DummyCluster::new(self_node_id.clone())), cluster: Arc::new(DummyCluster::new(self_node_id.clone())),
nonce: 0,
}, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) {
Err(Error::InvalidNodesConfiguration) => (), Err(Error::InvalidNodesConfiguration) => (),
_ => panic!("unexpected"), _ => panic!("unexpected"),
@ -644,6 +667,7 @@ mod tests {
}, },
acl_storage: Arc::new(DummyAclStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()),
cluster: Arc::new(DummyCluster::new(self_node_id.clone())), cluster: Arc::new(DummyCluster::new(self_node_id.clone())),
nonce: 0,
}, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) {
Err(Error::InvalidThreshold) => (), Err(Error::InvalidThreshold) => (),
_ => panic!("unexpected"), _ => panic!("unexpected"),
@ -664,6 +688,7 @@ mod tests {
assert_eq!(sessions[0].on_consensus_message(sessions[1].node(), &message::DecryptionConsensusMessage { assert_eq!(sessions[0].on_consensus_message(sessions[1].node(), &message::DecryptionConsensusMessage {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(), sub_session: sessions[0].access_key().clone().into(),
session_nonce: 0,
message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession {
requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(),
}), }),
@ -676,6 +701,7 @@ mod tests {
assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(), sub_session: sessions[0].access_key().clone().into(),
session_nonce: 0,
message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession {
requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(),
}), }),
@ -683,6 +709,7 @@ mod tests {
assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node(), &message::RequestPartialDecryption { assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node(), &message::RequestPartialDecryption {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(), sub_session: sessions[0].access_key().clone().into(),
session_nonce: 0,
request_id: Random.generate().unwrap().secret().clone().into(), request_id: Random.generate().unwrap().secret().clone().into(),
is_shadow_decryption: false, is_shadow_decryption: false,
nodes: sessions.iter().map(|s| s.node().clone().into()).take(4).collect(), nodes: sessions.iter().map(|s| s.node().clone().into()).take(4).collect(),
@ -695,6 +722,7 @@ mod tests {
assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(), sub_session: sessions[0].access_key().clone().into(),
session_nonce: 0,
message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession {
requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(), requestor_signature: ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap().into(),
}), }),
@ -702,6 +730,7 @@ mod tests {
assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node(), &message::RequestPartialDecryption { assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node(), &message::RequestPartialDecryption {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(), sub_session: sessions[0].access_key().clone().into(),
session_nonce: 0,
request_id: Random.generate().unwrap().secret().clone().into(), request_id: Random.generate().unwrap().secret().clone().into(),
is_shadow_decryption: false, is_shadow_decryption: false,
nodes: sessions.iter().map(|s| s.node().clone().into()).take(2).collect(), nodes: sessions.iter().map(|s| s.node().clone().into()).take(2).collect(),
@ -714,6 +743,7 @@ mod tests {
assert_eq!(sessions[0].on_partial_decryption(sessions[1].node(), &message::PartialDecryption { assert_eq!(sessions[0].on_partial_decryption(sessions[1].node(), &message::PartialDecryption {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(), sub_session: sessions[0].access_key().clone().into(),
session_nonce: 0,
request_id: Random.generate().unwrap().secret().clone().into(), request_id: Random.generate().unwrap().secret().clone().into(),
shadow_point: Random.generate().unwrap().public().clone().into(), shadow_point: Random.generate().unwrap().public().clone().into(),
decrypt_shadow: None, decrypt_shadow: None,
@ -937,7 +967,14 @@ mod tests {
} }
#[test] #[test]
fn decryption_session_works_over_network() { fn decryption_message_fails_when_nonce_is_wrong() {
// TODO let (_, _, _, sessions) = prepare_decryption_sessions();
assert_eq!(sessions[1].process_message(sessions[0].node(), &message::DecryptionMessage::DecryptionSessionCompleted(
message::DecryptionSessionCompleted {
session: SessionId::default().into(),
sub_session: sessions[0].access_key().clone().into(),
session_nonce: 10,
}
)), Err(Error::ReplayProtection));
} }
} }

View File

@ -53,6 +53,8 @@ pub struct SessionImpl {
key_storage: Arc<KeyStorage>, key_storage: Arc<KeyStorage>,
/// Cluster which allows this node to send messages to other nodes in the cluster. /// Cluster which allows this node to send messages to other nodes in the cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
/// Session nonce.
nonce: u64,
/// SessionImpl completion condvar. /// SessionImpl completion condvar.
completed: Condvar, completed: Condvar,
/// Mutable session data. /// Mutable session data.
@ -71,6 +73,8 @@ pub struct SessionParams {
pub key_storage: Arc<KeyStorage>, pub key_storage: Arc<KeyStorage>,
/// Cluster /// Cluster
pub cluster: Arc<Cluster>, pub cluster: Arc<Cluster>,
/// Session nonce.
pub nonce: u64,
} }
/// Mutable data of encryption (distributed key generation) session. /// Mutable data of encryption (distributed key generation) session.
@ -119,6 +123,7 @@ impl SessionImpl {
encrypted_data: params.encrypted_data, encrypted_data: params.encrypted_data,
key_storage: params.key_storage, key_storage: params.key_storage,
cluster: params.cluster, cluster: params.cluster,
nonce: params.nonce,
completed: Condvar::new(), completed: Condvar::new(),
data: Mutex::new(SessionData { data: Mutex::new(SessionData {
state: SessionState::WaitingForInitialization, state: SessionState::WaitingForInitialization,
@ -169,6 +174,7 @@ impl SessionImpl {
if self.encrypted_data.id_numbers.len() > 1 { if self.encrypted_data.id_numbers.len() > 1 {
self.cluster.broadcast(Message::Encryption(EncryptionMessage::InitializeEncryptionSession(InitializeEncryptionSession { self.cluster.broadcast(Message::Encryption(EncryptionMessage::InitializeEncryptionSession(InitializeEncryptionSession {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
requestor_signature: requestor_signature.into(), requestor_signature: requestor_signature.into(),
common_point: common_point.into(), common_point: common_point.into(),
encrypted_point: encrypted_point.into(), encrypted_point: encrypted_point.into(),
@ -187,6 +193,8 @@ impl SessionImpl {
debug_assert!(self.id == *message.session); debug_assert!(self.id == *message.session);
debug_assert!(&sender != self.node()); debug_assert!(&sender != self.node());
self.check_nonce(message.session_nonce)?;
let mut data = self.data.lock(); let mut data = self.data.lock();
// check state // check state
@ -213,6 +221,7 @@ impl SessionImpl {
// send confirmation back to master node // send confirmation back to master node
self.cluster.send(&sender, Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(ConfirmEncryptionInitialization { self.cluster.send(&sender, Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(ConfirmEncryptionInitialization {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
}))) })))
} }
@ -221,6 +230,8 @@ impl SessionImpl {
debug_assert!(self.id == *message.session); debug_assert!(self.id == *message.session);
debug_assert!(&sender != self.node()); debug_assert!(&sender != self.node());
self.check_nonce(message.session_nonce)?;
let mut data = self.data.lock(); let mut data = self.data.lock();
debug_assert!(data.nodes.contains_key(&sender)); debug_assert!(data.nodes.contains_key(&sender));
@ -242,6 +253,8 @@ impl SessionImpl {
/// When error has occured on another node. /// When error has occured on another node.
pub fn on_session_error(&self, sender: NodeId, message: &EncryptionSessionError) -> Result<(), Error> { pub fn on_session_error(&self, sender: NodeId, message: &EncryptionSessionError) -> Result<(), Error> {
self.check_nonce(message.session_nonce)?;
let mut data = self.data.lock(); let mut data = self.data.lock();
warn!("{}: encryption session failed with error: {} from {}", self.node(), message.error, sender); warn!("{}: encryption session failed with error: {} from {}", self.node(), message.error, sender);
@ -252,6 +265,14 @@ impl SessionImpl {
Ok(()) Ok(())
} }
/// Check session nonce.
fn check_nonce(&self, message_session_nonce: u64) -> Result<(), Error> {
match self.nonce == message_session_nonce {
true => Ok(()),
false => Err(Error::ReplayProtection),
}
}
} }
impl ClusterSession for SessionImpl { impl ClusterSession for SessionImpl {

View File

@ -54,6 +54,8 @@ pub struct SessionImpl {
key_storage: Option<Arc<KeyStorage>>, key_storage: Option<Arc<KeyStorage>>,
/// Cluster which allows this node to send messages to other nodes in the cluster. /// Cluster which allows this node to send messages to other nodes in the cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
/// Session-level nonce.
nonce: u64,
/// SessionImpl completion condvar. /// SessionImpl completion condvar.
completed: Condvar, completed: Condvar,
/// Mutable session data. /// Mutable session data.
@ -70,6 +72,8 @@ pub struct SessionParams {
pub key_storage: Option<Arc<KeyStorage>>, pub key_storage: Option<Arc<KeyStorage>>,
/// Cluster /// Cluster
pub cluster: Arc<Cluster>, pub cluster: Arc<Cluster>,
/// Session nonce.
pub nonce: Option<u64>,
} }
/// Mutable data of distributed key generation session. /// Mutable data of distributed key generation session.
@ -187,6 +191,9 @@ impl SessionImpl {
self_node_id: params.self_node_id, self_node_id: params.self_node_id,
key_storage: params.key_storage, key_storage: params.key_storage,
cluster: params.cluster, cluster: params.cluster,
// when nonce.is_nonce(), generation session is wrapped
// => nonce is checked somewhere else && we can pass any value
nonce: params.nonce.unwrap_or_default(),
completed: Condvar::new(), completed: Condvar::new(),
data: Mutex::new(SessionData { data: Mutex::new(SessionData {
state: SessionState::WaitingForInitialization, state: SessionState::WaitingForInitialization,
@ -251,6 +258,7 @@ impl SessionImpl {
// start initialization // start initialization
self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
author: author.into(), author: author.into(),
nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(),
threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"),
@ -269,6 +277,10 @@ impl SessionImpl {
/// Process single message. /// Process single message.
pub fn process_message(&self, sender: &NodeId, message: &GenerationMessage) -> Result<(), Error> { pub fn process_message(&self, sender: &NodeId, message: &GenerationMessage) -> Result<(), Error> {
if self.nonce != message.session_nonce() {
return Err(Error::ReplayProtection);
}
match message { match message {
&GenerationMessage::InitializeSession(ref message) => &GenerationMessage::InitializeSession(ref message) =>
self.on_initialize_session(sender.clone(), message), self.on_initialize_session(sender.clone(), message),
@ -311,6 +323,7 @@ impl SessionImpl {
// send confirmation back to master node // send confirmation back to master node
self.cluster.send(&sender, Message::Generation(GenerationMessage::ConfirmInitialization(ConfirmInitialization { self.cluster.send(&sender, Message::Generation(GenerationMessage::ConfirmInitialization(ConfirmInitialization {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
derived_point: derived_point.into(), derived_point: derived_point.into(),
})))?; })))?;
@ -348,6 +361,7 @@ impl SessionImpl {
if let Some(next_receiver) = next_receiver { if let Some(next_receiver) = next_receiver {
return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(),
nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(),
threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"),
@ -506,6 +520,7 @@ impl SessionImpl {
data.state = SessionState::Finished; data.state = SessionState::Finished;
return self.cluster.send(&sender, Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { return self.cluster.send(&sender, Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
}))); })));
} }
@ -557,6 +572,7 @@ impl SessionImpl {
// broadcast derived point && other session paraeters to every other node // broadcast derived point && other session paraeters to every other node
self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization { self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
derived_point: derived_point.into(), derived_point: derived_point.into(),
}))) })))
} }
@ -589,6 +605,7 @@ impl SessionImpl {
self.cluster.send(&node, Message::Generation(GenerationMessage::KeysDissemination(KeysDissemination { self.cluster.send(&node, Message::Generation(GenerationMessage::KeysDissemination(KeysDissemination {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
secret1: secret1.into(), secret1: secret1.into(),
secret2: secret2.into(), secret2: secret2.into(),
publics: publics.iter().cloned().map(Into::into).collect(), publics: publics.iter().cloned().map(Into::into).collect(),
@ -649,6 +666,7 @@ impl SessionImpl {
// broadcast self public key share // broadcast self public key share
self.cluster.broadcast(Message::Generation(GenerationMessage::PublicKeyShare(PublicKeyShare { self.cluster.broadcast(Message::Generation(GenerationMessage::PublicKeyShare(PublicKeyShare {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
public_share: self_public_share.into(), public_share: self_public_share.into(),
}))) })))
} }
@ -691,6 +709,7 @@ impl SessionImpl {
// then distribute encrypted data to every other node // then distribute encrypted data to every other node
self.cluster.broadcast(Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { self.cluster.broadcast(Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted {
session: self.id.clone().into(), session: self.id.clone().into(),
session_nonce: self.nonce,
})))?; })))?;
// then wait for confirmation from all other nodes // then wait for confirmation from all other nodes
@ -871,6 +890,7 @@ pub mod tests {
self_node_id: node_id.clone(), self_node_id: node_id.clone(),
key_storage: Some(key_storage.clone()), key_storage: Some(key_storage.clone()),
cluster: cluster.clone(), cluster: cluster.clone(),
nonce: Some(0),
}); });
nodes.insert(node_id, Node { cluster: cluster, key_storage: key_storage, session: session }); nodes.insert(node_id, Node { cluster: cluster, key_storage: key_storage, session: session });
} }
@ -960,6 +980,7 @@ pub mod tests {
self_node_id: node_id.clone(), self_node_id: node_id.clone(),
key_storage: Some(Arc::new(DummyKeyStorage::default())), key_storage: Some(Arc::new(DummyKeyStorage::default())),
cluster: cluster, cluster: cluster,
nonce: Some(0),
}); });
let cluster_nodes: BTreeSet<_> = (0..2).map(|_| math::generate_random_point().unwrap()).collect(); 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(), 0, cluster_nodes).unwrap_err(), Error::InvalidNodesConfiguration);
@ -1013,6 +1034,7 @@ pub mod tests {
l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap();
assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization { assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization {
session: sid.into(), session: sid.into(),
session_nonce: 0,
derived_point: math::generate_random_point().unwrap().into(), derived_point: math::generate_random_point().unwrap().into(),
}).unwrap_err(), Error::InvalidStateForRequest); }).unwrap_err(), Error::InvalidStateForRequest);
} }
@ -1024,6 +1046,7 @@ pub mod tests {
l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap();
assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization { assert_eq!(l.master().on_confirm_initialization(s, &message::ConfirmInitialization {
session: sid.into(), session: sid.into(),
session_nonce: 0,
derived_point: math::generate_random_point().unwrap().into(), derived_point: math::generate_random_point().unwrap().into(),
}).unwrap_err(), Error::InvalidStateForRequest); }).unwrap_err(), Error::InvalidStateForRequest);
} }
@ -1052,6 +1075,7 @@ pub mod tests {
nodes.insert(math::generate_random_point().unwrap(), math::generate_random_scalar().unwrap()); nodes.insert(math::generate_random_point().unwrap(), math::generate_random_scalar().unwrap());
assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession {
session: sid.into(), session: sid.into(),
session_nonce: 0,
author: Public::default().into(), author: Public::default().into(),
nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(),
threshold: 0, threshold: 0,
@ -1067,6 +1091,7 @@ pub mod tests {
nodes.insert(s, math::generate_random_scalar().unwrap()); nodes.insert(s, math::generate_random_scalar().unwrap());
assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession { assert_eq!(l.first_slave().on_initialize_session(m, &message::InitializeSession {
session: sid.into(), session: sid.into(),
session_nonce: 0,
author: Public::default().into(), author: Public::default().into(),
nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), nodes: nodes.into_iter().map(|(k, v)| (k.into(), v.into())).collect(),
threshold: 2, threshold: 2,
@ -1079,6 +1104,7 @@ pub mod tests {
let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap(); let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap();
assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization { assert_eq!(l.first_slave().on_complete_initialization(m, &message::CompleteInitialization {
session: sid.into(), session: sid.into(),
session_nonce: 0,
derived_point: math::generate_random_point().unwrap().into(), derived_point: math::generate_random_point().unwrap().into(),
}).unwrap_err(), Error::InvalidStateForRequest); }).unwrap_err(), Error::InvalidStateForRequest);
} }
@ -1092,6 +1118,7 @@ pub mod tests {
l.take_and_process_message().unwrap(); l.take_and_process_message().unwrap();
assert_eq!(l.first_slave().on_complete_initialization(l.second_slave().node().clone(), &message::CompleteInitialization { assert_eq!(l.first_slave().on_complete_initialization(l.second_slave().node().clone(), &message::CompleteInitialization {
session: sid.into(), session: sid.into(),
session_nonce: 0,
derived_point: math::generate_random_point().unwrap().into(), derived_point: math::generate_random_point().unwrap().into(),
}).unwrap_err(), Error::InvalidMessage); }).unwrap_err(), Error::InvalidMessage);
} }
@ -1101,6 +1128,7 @@ pub mod tests {
let (sid, _, s, l) = make_simple_cluster(0, 2).unwrap(); let (sid, _, s, l) = make_simple_cluster(0, 2).unwrap();
assert_eq!(l.master().on_keys_dissemination(s, &message::KeysDissemination { assert_eq!(l.master().on_keys_dissemination(s, &message::KeysDissemination {
session: sid.into(), session: sid.into(),
session_nonce: 0,
secret1: math::generate_random_scalar().unwrap().into(), secret1: math::generate_random_scalar().unwrap().into(),
secret2: math::generate_random_scalar().unwrap().into(), secret2: math::generate_random_scalar().unwrap().into(),
publics: vec![math::generate_random_point().unwrap().into()], publics: vec![math::generate_random_point().unwrap().into()],
@ -1119,6 +1147,7 @@ pub mod tests {
l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination
assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination { assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination {
session: sid.into(), session: sid.into(),
session_nonce: 0,
secret1: math::generate_random_scalar().unwrap().into(), secret1: math::generate_random_scalar().unwrap().into(),
secret2: math::generate_random_scalar().unwrap().into(), secret2: math::generate_random_scalar().unwrap().into(),
publics: vec![math::generate_random_point().unwrap().into(), math::generate_random_point().unwrap().into()], publics: vec![math::generate_random_point().unwrap().into(), math::generate_random_point().unwrap().into()],
@ -1137,6 +1166,7 @@ pub mod tests {
l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination l.take_and_process_message().unwrap(); // m -> s1: KeysDissemination
assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination { assert_eq!(l.first_slave().on_keys_dissemination(m, &message::KeysDissemination {
session: sid.into(), session: sid.into(),
session_nonce: 0,
secret1: math::generate_random_scalar().unwrap().into(), secret1: math::generate_random_scalar().unwrap().into(),
secret2: math::generate_random_scalar().unwrap().into(), secret2: math::generate_random_scalar().unwrap().into(),
publics: vec![math::generate_random_point().unwrap().into()], publics: vec![math::generate_random_point().unwrap().into()],
@ -1148,6 +1178,7 @@ pub mod tests {
let (sid, _, s, l) = make_simple_cluster(1, 3).unwrap(); let (sid, _, s, l) = make_simple_cluster(1, 3).unwrap();
assert_eq!(l.master().on_public_key_share(s, &message::PublicKeyShare { assert_eq!(l.master().on_public_key_share(s, &message::PublicKeyShare {
session: sid.into(), session: sid.into(),
session_nonce: 0,
public_share: math::generate_random_point().unwrap().into(), public_share: math::generate_random_point().unwrap().into(),
}).unwrap_err(), Error::InvalidStateForRequest); }).unwrap_err(), Error::InvalidStateForRequest);
} }
@ -1176,6 +1207,7 @@ pub mod tests {
l.process_message((f, t, Message::Generation(GenerationMessage::PublicKeyShare(msg.clone())))).unwrap(); l.process_message((f, t, Message::Generation(GenerationMessage::PublicKeyShare(msg.clone())))).unwrap();
assert_eq!(l.second_slave().on_public_key_share(m, &message::PublicKeyShare { assert_eq!(l.second_slave().on_public_key_share(m, &message::PublicKeyShare {
session: sid.into(), session: sid.into(),
session_nonce: 0,
public_share: math::generate_random_point().unwrap().into(), public_share: math::generate_random_point().unwrap().into(),
}).unwrap_err(), Error::InvalidMessage); }).unwrap_err(), Error::InvalidMessage);
} }
@ -1253,4 +1285,16 @@ pub mod tests {
loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_and_secret().is_some()); loop_until(&mut core, time::Duration::from_millis(1000), || session.joint_public_and_secret().is_some());
} }
} }
#[test]
fn generation_message_fails_when_nonce_is_wrong() {
let (sid, m, _, l) = make_simple_cluster(0, 2).unwrap();
assert_eq!(l.first_slave().process_message(&m, &message::GenerationMessage::KeysDissemination(message::KeysDissemination {
session: sid.into(),
session_nonce: 10,
secret1: math::generate_random_scalar().unwrap().into(),
secret2: math::generate_random_scalar().unwrap().into(),
publics: vec![math::generate_random_point().unwrap().into()],
})).unwrap_err(), Error::ReplayProtection);
}
} }

View File

@ -14,12 +14,31 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>. // along with Parity. If not, see <http://www.gnu.org/licenses/>.
///! Given: two nodes each holding its own `self_key_pair`.
///!
///! Handshake process:
///! 1) both nodes are generating random `KeyPair` (`session_key_pair`), which will be used for channel encryption
///! 2) both nodes are generating random H256 (`confirmation_plain`)
///! 3) both nodes are signing `confirmation_plain` using `session_key_pair` to receive `confirmation_signed_session`
///! 4) nodes exchange with `NodePublicKey` messages, containing: `self_key_pair.public`, `confirmation_plain`, `confirmation_signed_session`
///! 5) both nodes are checking that they're configured to communicate to server with received `message.self_key_pair.public`. Connection is closed otherwise
///! 6) both nodes are recovering peer' `session_key_pair.public` from `message.confirmation_plain` and `message.confirmation_signed_session`
///! 7) both nodes are computing shared session key pair using self' `session_key_pair.secret` && peer' `session_key_pair.public`. All following messages are encrypted using this key_pair.
///! 8) both nodes are signing `message.confirmation_plain` with their own `self_key_pair.private` to receive `confirmation_signed`
///! 9) nodes exchange with `NodePrivateKeySignature` messages, containing `confirmation_signed`
///! 10) both nodes are checking that `confirmation_signed` is actually signed with the owner of peer' `self_key_pair.secret`
///!
///! Result of handshake is:
///! 1) belief, that we are connected to the KS from our KS-set
///! 2) session key pair, which is used to enrypt all connection messages
use std::io; use std::io;
use std::sync::Arc; use std::sync::Arc;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use futures::{Future, Poll, Async}; use futures::{Future, Poll, Async};
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use ethkey::{Random, Generator, KeyPair, verify_public}; use ethcrypto::ecdh::agree;
use ethkey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover};
use bigint::hash::H256; use bigint::hash::H256;
use key_server_cluster::{NodeId, Error, NodeKeyPair}; use key_server_cluster::{NodeId, Error, NodeKeyPair};
use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature};
@ -28,16 +47,20 @@ use key_server_cluster::io::{write_message, write_encrypted_message, WriteMessag
/// Start handshake procedure with another node from the cluster. /// Start handshake procedure with another node from the cluster.
pub fn handshake<A>(a: A, self_key_pair: Arc<NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> where A: AsyncWrite + AsyncRead { pub fn handshake<A>(a: A, self_key_pair: Arc<NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> where A: AsyncWrite + AsyncRead {
let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let init_data = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into)
handshake_with_plain_confirmation(a, self_confirmation_plain, self_key_pair, trusted_nodes) .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into));
handshake_with_init_data(a, init_data, self_key_pair, trusted_nodes)
} }
/// Start handshake procedure with another node from the cluster and given plain confirmation. /// Start handshake procedure with another node from the cluster and given plain confirmation + session key pair.
pub fn handshake_with_plain_confirmation<A>(a: A, self_confirmation_plain: Result<H256, Error>, self_key_pair: Arc<NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> where A: AsyncWrite + AsyncRead { pub fn handshake_with_init_data<A>(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc<NodeKeyPair>, trusted_nodes: BTreeSet<NodeId>) -> Handshake<A> where A: AsyncWrite + AsyncRead {
let (error, state) = match self_confirmation_plain.clone() let handshake_input_data = init_data
.and_then(|c| Handshake::<A>::make_public_key_message(self_key_pair.public().clone(), c)) { .and_then(|(cp, kp)| sign(kp.secret(), &cp).map(|sp| (cp, kp, sp)).map_err(Into::into))
Ok(message) => (None, HandshakeState::SendPublicKey(write_message(a, message))), .and_then(|(cp, kp, sp)| Handshake::<A>::make_public_key_message(self_key_pair.public().clone(), cp.clone(), sp).map(|msg| (cp, kp, msg)));
Err(err) => (Some((a, Err(err))), HandshakeState::Finished),
let (error, cp, kp, state) = match handshake_input_data {
Ok((cp, kp, msg)) => (None, cp, Some(kp), HandshakeState::SendPublicKey(write_message(a, msg))),
Err(err) => (Some((a, Err(err))), Default::default(), None, HandshakeState::Finished),
}; };
Handshake { Handshake {
@ -45,10 +68,12 @@ pub fn handshake_with_plain_confirmation<A>(a: A, self_confirmation_plain: Resul
error: error, error: error,
state: state, state: state,
self_key_pair: self_key_pair, self_key_pair: self_key_pair,
self_confirmation_plain: self_confirmation_plain.unwrap_or(Default::default()), self_session_key_pair: kp,
self_confirmation_plain: cp,
trusted_nodes: Some(trusted_nodes), trusted_nodes: Some(trusted_nodes),
other_node_id: None, peer_node_id: None,
other_confirmation_plain: None, peer_session_public: None,
peer_confirmation_plain: None,
shared_key: None, shared_key: None,
} }
} }
@ -56,9 +81,12 @@ pub fn handshake_with_plain_confirmation<A>(a: A, self_confirmation_plain: Resul
/// Wait for handshake procedure to be started by another node from the cluster. /// Wait for handshake procedure to be started by another node from the cluster.
pub fn accept_handshake<A>(a: A, self_key_pair: Arc<NodeKeyPair>) -> Handshake<A> where A: AsyncWrite + AsyncRead { pub fn accept_handshake<A>(a: A, self_key_pair: Arc<NodeKeyPair>) -> Handshake<A> where A: AsyncWrite + AsyncRead {
let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into);
let (error, state) = match self_confirmation_plain.clone() { let handshake_input_data = self_confirmation_plain
Ok(_) => (None, HandshakeState::ReceivePublicKey(read_message(a))), .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into));
Err(err) => (Some((a, Err(err))), HandshakeState::Finished),
let (error, cp, kp, state) = match handshake_input_data {
Ok((cp, kp)) => (None, cp, Some(kp), HandshakeState::ReceivePublicKey(read_message(a))),
Err(err) => (Some((a, Err(err))), Default::default(), None, HandshakeState::Finished),
}; };
Handshake { Handshake {
@ -66,10 +94,12 @@ pub fn accept_handshake<A>(a: A, self_key_pair: Arc<NodeKeyPair>) -> Handshake<A
error: error, error: error,
state: state, state: state,
self_key_pair: self_key_pair, self_key_pair: self_key_pair,
self_confirmation_plain: self_confirmation_plain.unwrap_or(Default::default()), self_session_key_pair: kp,
self_confirmation_plain: cp,
trusted_nodes: None, trusted_nodes: None,
other_node_id: None, peer_node_id: None,
other_confirmation_plain: None, peer_session_public: None,
peer_confirmation_plain: None,
shared_key: None, shared_key: None,
} }
} }
@ -89,10 +119,12 @@ pub struct Handshake<A> {
error: Option<(A, Result<HandshakeResult, Error>)>, error: Option<(A, Result<HandshakeResult, Error>)>,
state: HandshakeState<A>, state: HandshakeState<A>,
self_key_pair: Arc<NodeKeyPair>, self_key_pair: Arc<NodeKeyPair>,
self_session_key_pair: Option<KeyPair>,
self_confirmation_plain: H256, self_confirmation_plain: H256,
trusted_nodes: Option<BTreeSet<NodeId>>, trusted_nodes: Option<BTreeSet<NodeId>>,
other_node_id: Option<NodeId>, peer_node_id: Option<NodeId>,
other_confirmation_plain: Option<H256>, peer_session_public: Option<Public>,
peer_confirmation_plain: Option<H256>,
shared_key: Option<KeyPair>, shared_key: Option<KeyPair>,
} }
@ -111,10 +143,16 @@ impl<A> Handshake<A> where A: AsyncRead + AsyncWrite {
self.self_confirmation_plain = self_confirmation_plain; self.self_confirmation_plain = self_confirmation_plain;
} }
pub fn make_public_key_message(self_node_id: NodeId, confirmation_plain: H256) -> Result<Message, Error> { #[cfg(test)]
pub fn set_self_session_key_pair(&mut self, self_session_key_pair: KeyPair) {
self.self_session_key_pair = Some(self_session_key_pair);
}
pub fn make_public_key_message(self_node_id: NodeId, confirmation_plain: H256, confirmation_signed_session: Signature) -> Result<Message, Error> {
Ok(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey { Ok(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey {
node_id: self_node_id.into(), node_id: self_node_id.into(),
confirmation_plain: confirmation_plain.into(), confirmation_plain: confirmation_plain.into(),
confirmation_signed_session: confirmation_signed_session.into(),
}))) })))
} }
@ -123,6 +161,12 @@ impl<A> Handshake<A> where A: AsyncRead + AsyncWrite {
confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(),
}))) })))
} }
fn compute_shared_key(self_session_key_pair: &KeyPair, peer_session_public: &Public) -> Result<KeyPair, Error> {
agree(self_session_key_pair.secret(), peer_session_public)
.map_err(Into::into)
.and_then(|s| fix_shared_key(&s))
}
} }
impl<A> Future for Handshake<A> where A: AsyncRead + AsyncWrite { impl<A> Future for Handshake<A> where A: AsyncRead + AsyncWrite {
@ -143,20 +187,25 @@ impl<A> Future for Handshake<A> where A: AsyncRead + AsyncWrite {
read_message(stream) read_message(stream)
), Async::NotReady) ), Async::NotReady)
} else { } else {
self.shared_key = match self.self_key_pair.compute_shared_key( let shared_key = Self::compute_shared_key(
self.other_node_id.as_ref().expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; other_node_id is filled in ReceivePublicKey; qed") self.self_session_key_pair.as_ref().expect(
).map_err(Into::into).and_then(|sk| fix_shared_key(sk.secret())) { "self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"),
self.peer_session_public.as_ref().expect(
"we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_session_public is filled in ReceivePublicKey; qed"),
);
self.shared_key = match shared_key {
Ok(shared_key) => Some(shared_key), Ok(shared_key) => Some(shared_key),
Err(err) => return Ok((stream, Err(err.into())).into()), Err(err) => return Ok((stream, Err(err)).into()),
}; };
let message = match Handshake::<A>::make_private_key_signature_message( let peer_confirmation_plain = self.peer_confirmation_plain.as_ref()
&*self.self_key_pair, .expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_confirmation_plain is filled in ReceivePublicKey; qed");
self.other_confirmation_plain.as_ref().expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; other_confirmation_plain is filled in ReceivePublicKey; qed") let message = match Handshake::<A>::make_private_key_signature_message(&*self.self_key_pair, peer_confirmation_plain) {
) {
Ok(message) => message, Ok(message) => message,
Err(err) => return Ok((stream, Err(err)).into()), Err(err) => return Ok((stream, Err(err)).into()),
}; };
(HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream, (HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream,
self.shared_key.as_ref().expect("filled couple of lines above; qed"), self.shared_key.as_ref().expect("filled couple of lines above; qed"),
message)), Async::NotReady) message)), Async::NotReady)
@ -177,28 +226,44 @@ impl<A> Future for Handshake<A> where A: AsyncRead + AsyncWrite {
return Ok((stream, Err(Error::InvalidNodeId)).into()); return Ok((stream, Err(Error::InvalidNodeId)).into());
} }
self.other_node_id = Some(message.node_id.into()); self.peer_node_id = Some(message.node_id.into());
self.other_confirmation_plain = Some(message.confirmation_plain.into()); self.peer_session_public = Some(match recover(&message.confirmation_signed_session, &message.confirmation_plain) {
Ok(peer_session_public) => peer_session_public,
Err(err) => return Ok((stream, Err(err.into())).into()),
});
self.peer_confirmation_plain = Some(message.confirmation_plain.into());
if self.is_active { if self.is_active {
self.shared_key = match self.self_key_pair.compute_shared_key( let shared_key = Self::compute_shared_key(
self.other_node_id.as_ref().expect("filled couple of lines above; qed") self.self_session_key_pair.as_ref().expect(
).map_err(Into::into).and_then(|sk| fix_shared_key(sk.secret())) { "self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"),
self.peer_session_public.as_ref().expect(
"we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_session_public is filled in ReceivePublicKey; qed"),
);
self.shared_key = match shared_key {
Ok(shared_key) => Some(shared_key), Ok(shared_key) => Some(shared_key),
Err(err) => return Ok((stream, Err(err.into())).into()), Err(err) => return Ok((stream, Err(err)).into()),
}; };
let message = match Handshake::<A>::make_private_key_signature_message( let peer_confirmation_plain = self.peer_confirmation_plain.as_ref()
&*self.self_key_pair, .expect("filled couple of lines above; qed");
self.other_confirmation_plain.as_ref().expect("filled couple of lines above; qed") let message = match Handshake::<A>::make_private_key_signature_message(&*self.self_key_pair, peer_confirmation_plain) {
) {
Ok(message) => message, Ok(message) => message,
Err(err) => return Ok((stream, Err(err)).into()), Err(err) => return Ok((stream, Err(err)).into()),
}; };
(HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream, (HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream,
self.shared_key.as_ref().expect("filled couple of lines above; qed"), self.shared_key.as_ref().expect("filled couple of lines above; qed"),
message)), Async::NotReady) message)), Async::NotReady)
} else { } else {
let message = match Handshake::<A>::make_public_key_message(self.self_key_pair.public().clone(), self.self_confirmation_plain.clone()) { let self_session_key_pair = self.self_session_key_pair.as_ref()
.expect("self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed");
let confirmation_signed_session = match sign(self_session_key_pair.secret(), &self.self_confirmation_plain).map_err(Into::into) {
Ok(confirmation_signed_session) => confirmation_signed_session,
Err(err) => return Ok((stream, Err(err)).into()),
};
let message = match Handshake::<A>::make_public_key_message(self.self_key_pair.public().clone(), self.self_confirmation_plain.clone(), confirmation_signed_session) {
Ok(message) => message, Ok(message) => message,
Err(err) => return Ok((stream, Err(err)).into()), Err(err) => return Ok((stream, Err(err)).into()),
}; };
@ -225,13 +290,13 @@ impl<A> Future for Handshake<A> where A: AsyncRead + AsyncWrite {
Err(err) => return Ok((stream, Err(err.into())).into()), Err(err) => return Ok((stream, Err(err.into())).into()),
}; };
let other_node_public = self.other_node_id.as_ref().expect("other_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"); let peer_public = self.peer_node_id.as_ref().expect("peer_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed");
if !verify_public(other_node_public, &*message.confirmation_signed, &self.self_confirmation_plain).unwrap_or(false) { if !verify_public(peer_public, &*message.confirmation_signed, &self.self_confirmation_plain).unwrap_or(false) {
return Ok((stream, Err(Error::InvalidMessage)).into()); return Ok((stream, Err(Error::InvalidMessage)).into());
} }
(HandshakeState::Finished, Async::Ready((stream, Ok(HandshakeResult { (HandshakeState::Finished, Async::Ready((stream, Ok(HandshakeResult {
node_id: self.other_node_id.expect("other_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"), node_id: self.peer_node_id.expect("peer_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"),
shared_key: self.shared_key.clone().expect("shared_key is filled in Send/ReceivePublicKey; ReceivePrivateKeySignature follows Send/ReceivePublicKey; qed"), shared_key: self.shared_key.clone().expect("shared_key is filled in Send/ReceivePublicKey; ReceivePrivateKeySignature follows Send/ReceivePublicKey; qed"),
})))) }))))
}, },
@ -253,27 +318,26 @@ mod tests {
use std::collections::BTreeSet; use std::collections::BTreeSet;
use futures::Future; use futures::Future;
use ethkey::{Random, Generator, sign}; use ethkey::{Random, Generator, sign};
use ethcrypto::ecdh::agree;
use bigint::hash::H256; use bigint::hash::H256;
use key_server_cluster::PlainNodeKeyPair; use key_server_cluster::PlainNodeKeyPair;
use key_server_cluster::io::message::fix_shared_key;
use key_server_cluster::io::message::tests::TestIo; use key_server_cluster::io::message::tests::TestIo;
use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature};
use super::{handshake_with_plain_confirmation, accept_handshake, HandshakeResult}; use super::{handshake_with_init_data, accept_handshake, HandshakeResult};
fn prepare_test_io() -> (H256, TestIo) { fn prepare_test_io() -> (H256, TestIo) {
let self_key_pair = Random.generate().unwrap(); let mut io = TestIo::new();
let peer_key_pair = Random.generate().unwrap();
let mut io = TestIo::new(self_key_pair.clone(), peer_key_pair.public().clone());
let self_confirmation_plain = *Random.generate().unwrap().secret().clone(); let self_confirmation_plain = *Random.generate().unwrap().secret().clone();
let peer_confirmation_plain = *Random.generate().unwrap().secret().clone(); let peer_confirmation_plain = *Random.generate().unwrap().secret().clone();
let self_confirmation_signed = sign(peer_key_pair.secret(), &self_confirmation_plain).unwrap(); let self_confirmation_signed = sign(io.peer_key_pair().secret(), &self_confirmation_plain).unwrap();
let peer_confirmation_signed = sign(io.peer_session_key_pair().secret(), &peer_confirmation_plain).unwrap();
let peer_public = io.peer_key_pair().public().clone();
io.add_input_message(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey { io.add_input_message(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey {
node_id: peer_key_pair.public().clone().into(), node_id: peer_public.into(),
confirmation_plain: peer_confirmation_plain.into(), confirmation_plain: peer_confirmation_plain.into(),
confirmation_signed_session: peer_confirmation_signed.into(),
}))); })));
io.add_encrypted_input_message(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { io.add_encrypted_input_message(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature {
confirmation_signed: self_confirmation_signed.into(), confirmation_signed: self_confirmation_signed.into(),
@ -285,14 +349,15 @@ mod tests {
#[test] #[test]
fn active_handshake_works() { fn active_handshake_works() {
let (self_confirmation_plain, io) = prepare_test_io(); let (self_confirmation_plain, io) = prepare_test_io();
let self_key_pair = io.self_key_pair().clone(); let trusted_nodes: BTreeSet<_> = vec![io.peer_key_pair().public().clone()].into_iter().collect();
let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); let self_session_key_pair = io.self_session_key_pair().clone();
let shared_key = fix_shared_key(&agree(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap()).unwrap(); let self_key_pair = Arc::new(PlainNodeKeyPair::new(io.self_key_pair().clone()));
let shared_key = io.shared_key_pair().clone();
let handshake = handshake_with_plain_confirmation(io, Ok(self_confirmation_plain), Arc::new(PlainNodeKeyPair::new(self_key_pair)), trusted_nodes); let handshake = handshake_with_init_data(io, Ok((self_confirmation_plain, self_session_key_pair)), self_key_pair, trusted_nodes);
let handshake_result = handshake.wait().unwrap(); let handshake_result = handshake.wait().unwrap();
assert_eq!(handshake_result.1, Ok(HandshakeResult { assert_eq!(handshake_result.1, Ok(HandshakeResult {
node_id: handshake_result.0.peer_public().clone(), node_id: handshake_result.0.peer_key_pair().public().clone(),
shared_key: shared_key, shared_key: shared_key,
})); }));
} }
@ -300,16 +365,17 @@ mod tests {
#[test] #[test]
fn passive_handshake_works() { fn passive_handshake_works() {
let (self_confirmation_plain, io) = prepare_test_io(); let (self_confirmation_plain, io) = prepare_test_io();
let self_key_pair = io.self_key_pair().clone(); let self_key_pair = Arc::new(PlainNodeKeyPair::new(io.self_key_pair().clone()));
let trusted_nodes: BTreeSet<_> = vec![io.peer_public().clone()].into_iter().collect(); let self_session_key_pair = io.self_session_key_pair().clone();
let shared_key = fix_shared_key(&agree(self_key_pair.secret(), trusted_nodes.iter().nth(0).unwrap()).unwrap()).unwrap(); let shared_key = io.shared_key_pair().clone();
let mut handshake = accept_handshake(io, Arc::new(PlainNodeKeyPair::new(self_key_pair))); let mut handshake = accept_handshake(io, self_key_pair);
handshake.set_self_confirmation_plain(self_confirmation_plain); handshake.set_self_confirmation_plain(self_confirmation_plain);
handshake.set_self_session_key_pair(self_session_key_pair);
let handshake_result = handshake.wait().unwrap(); let handshake_result = handshake.wait().unwrap();
assert_eq!(handshake_result.1, Ok(HandshakeResult { assert_eq!(handshake_result.1, Ok(HandshakeResult {
node_id: handshake_result.0.peer_public().clone(), node_id: handshake_result.0.peer_key_pair().public().clone(),
shared_key: shared_key, shared_key: shared_key,
})); }));
} }

View File

@ -30,6 +30,8 @@ use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, En
/// Size of serialized header. /// Size of serialized header.
pub const MESSAGE_HEADER_SIZE: usize = 4; pub const MESSAGE_HEADER_SIZE: usize = 4;
/// Current header version.
pub const CURRENT_HEADER_VERSION: u8 = 1;
/// Message header. /// Message header.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -97,7 +99,7 @@ pub fn serialize_message(message: Message) -> Result<SerializedMessage, Error> {
let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; let payload = payload.map_err(|err| Error::Serde(err.to_string()))?;
build_serialized_message(MessageHeader { build_serialized_message(MessageHeader {
kind: message_kind, kind: message_kind,
version: 1, version: CURRENT_HEADER_VERSION,
size: 0, size: 0,
}, payload) }, payload)
} }
@ -177,8 +179,13 @@ fn serialize_header(header: &MessageHeader) -> Result<Vec<u8>, Error> {
/// Deserialize message header. /// Deserialize message header.
pub fn deserialize_header(data: &[u8]) -> Result<MessageHeader, Error> { pub fn deserialize_header(data: &[u8]) -> Result<MessageHeader, Error> {
let mut reader = Cursor::new(data); let mut reader = Cursor::new(data);
let version = reader.read_u8()?;
if version != CURRENT_HEADER_VERSION {
return Err(Error::InvalidMessageVersion);
}
Ok(MessageHeader { Ok(MessageHeader {
version: reader.read_u8()?, version: version,
kind: reader.read_u8()?, kind: reader.read_u8()?,
size: reader.read_u16::<LittleEndian>()?, size: reader.read_u16::<LittleEndian>()?,
}) })
@ -202,25 +209,34 @@ pub mod tests {
use std::io; use std::io;
use futures::Poll; use futures::Poll;
use tokio_io::{AsyncRead, AsyncWrite}; use tokio_io::{AsyncRead, AsyncWrite};
use ethkey::{KeyPair, Public}; use ethkey::{Random, Generator, KeyPair};
use ethcrypto::ecdh::agree; use ethcrypto::ecdh::agree;
use key_server_cluster::Error;
use key_server_cluster::message::Message; use key_server_cluster::message::Message;
use super::{MESSAGE_HEADER_SIZE, MessageHeader, fix_shared_key, encrypt_message, serialize_message, use super::{MESSAGE_HEADER_SIZE, CURRENT_HEADER_VERSION, MessageHeader, fix_shared_key, encrypt_message,
serialize_header, deserialize_header}; serialize_message, serialize_header, deserialize_header};
pub struct TestIo { pub struct TestIo {
self_key_pair: KeyPair, self_key_pair: KeyPair,
peer_public: Public, self_session_key_pair: KeyPair,
peer_key_pair: KeyPair,
peer_session_key_pair: KeyPair,
shared_key_pair: KeyPair, shared_key_pair: KeyPair,
input_buffer: io::Cursor<Vec<u8>>, input_buffer: io::Cursor<Vec<u8>>,
} }
impl TestIo { impl TestIo {
pub fn new(self_key_pair: KeyPair, peer_public: Public) -> Self { pub fn new() -> Self {
let shared_key_pair = fix_shared_key(&agree(self_key_pair.secret(), &peer_public).unwrap()).unwrap(); let self_session_key_pair = Random.generate().unwrap();
let peer_session_key_pair = Random.generate().unwrap();
let self_key_pair = Random.generate().unwrap();
let peer_key_pair = Random.generate().unwrap();
let shared_key_pair = fix_shared_key(&agree(self_session_key_pair.secret(), peer_session_key_pair.public()).unwrap()).unwrap();
TestIo { TestIo {
self_key_pair: self_key_pair, self_key_pair: self_key_pair,
peer_public: peer_public, self_session_key_pair: self_session_key_pair,
peer_key_pair: peer_key_pair,
peer_session_key_pair: peer_session_key_pair,
shared_key_pair: shared_key_pair, shared_key_pair: shared_key_pair,
input_buffer: io::Cursor::new(Vec::new()), input_buffer: io::Cursor::new(Vec::new()),
} }
@ -230,8 +246,20 @@ pub mod tests {
&self.self_key_pair &self.self_key_pair
} }
pub fn peer_public(&self) -> &Public { pub fn self_session_key_pair(&self) -> &KeyPair {
&self.peer_public &self.self_session_key_pair
}
pub fn peer_key_pair(&self) -> &KeyPair {
&self.peer_key_pair
}
pub fn peer_session_key_pair(&self) -> &KeyPair {
&self.peer_session_key_pair
}
pub fn shared_key_pair(&self) -> &KeyPair {
&self.shared_key_pair
} }
pub fn add_input_message(&mut self, message: Message) { pub fn add_input_message(&mut self, message: Message) {
@ -281,7 +309,7 @@ pub mod tests {
fn header_serialization_works() { fn header_serialization_works() {
let header = MessageHeader { let header = MessageHeader {
kind: 1, kind: 1,
version: 2, version: CURRENT_HEADER_VERSION,
size: 3, size: 3,
}; };
@ -291,4 +319,15 @@ pub mod tests {
let deserialized_header = deserialize_header(&serialized_header).unwrap(); let deserialized_header = deserialize_header(&serialized_header).unwrap();
assert_eq!(deserialized_header, header); assert_eq!(deserialized_header, header);
} }
#[test]
fn deserializing_header_of_wrong_version_fails() {
let header = MessageHeader {
kind: 1,
version: CURRENT_HEADER_VERSION + 1,
size: 3,
};
assert_eq!(deserialize_header(&serialize_header(&header).unwrap()).unwrap_err(), Error::InvalidMessageVersion);
}
} }

View File

@ -127,8 +127,10 @@ pub enum SigningMessage {
pub struct NodePublicKey { pub struct NodePublicKey {
/// Node identifier (aka node public key). /// Node identifier (aka node public key).
pub node_id: MessageNodeId, pub node_id: MessageNodeId,
/// Data, which must be signed by peer to prove that he owns the corresponding private key. /// Random data, which must be signed by peer to prove that he owns the corresponding private key.
pub confirmation_plain: SerializableH256, pub confirmation_plain: SerializableH256,
/// The same random `confirmation_plain`, signed with one-time session key.
pub confirmation_signed_session: SerializableSignature,
} }
/// Confirm that node owns the private key of previously passed public key (aka node id). /// Confirm that node owns the private key of previously passed public key (aka node id).
@ -138,7 +140,6 @@ pub struct NodePrivateKeySignature {
pub confirmation_signed: SerializableSignature, pub confirmation_signed: SerializableSignature,
} }
/// Ask if the node is still alive. /// Ask if the node is still alive.
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct KeepAlive { pub struct KeepAlive {
@ -154,6 +155,8 @@ pub struct KeepAliveResponse {
pub struct InitializeSession { pub struct InitializeSession {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Session author. /// Session author.
pub author: SerializablePublic, pub author: SerializablePublic,
/// All session participants along with their identification numbers. /// All session participants along with their identification numbers.
@ -173,6 +176,8 @@ pub struct InitializeSession {
pub struct ConfirmInitialization { pub struct ConfirmInitialization {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Derived generation point. /// Derived generation point.
pub derived_point: SerializablePublic, pub derived_point: SerializablePublic,
} }
@ -182,6 +187,8 @@ pub struct ConfirmInitialization {
pub struct CompleteInitialization { pub struct CompleteInitialization {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Derived generation point. /// Derived generation point.
pub derived_point: SerializablePublic, pub derived_point: SerializablePublic,
} }
@ -191,6 +198,8 @@ pub struct CompleteInitialization {
pub struct KeysDissemination { pub struct KeysDissemination {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Secret 1. /// Secret 1.
pub secret1: SerializableSecret, pub secret1: SerializableSecret,
/// Secret 2. /// Secret 2.
@ -204,6 +213,8 @@ pub struct KeysDissemination {
pub struct PublicKeyShare { pub struct PublicKeyShare {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Public key share. /// Public key share.
pub public_share: SerializablePublic, pub public_share: SerializablePublic,
} }
@ -213,6 +224,8 @@ pub struct PublicKeyShare {
pub struct SessionError { pub struct SessionError {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Public key share. /// Public key share.
pub error: String, pub error: String,
} }
@ -222,6 +235,8 @@ pub struct SessionError {
pub struct SessionCompleted { pub struct SessionCompleted {
/// Session Id. /// Session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
} }
/// Node is requested to prepare for saving encrypted data. /// Node is requested to prepare for saving encrypted data.
@ -229,6 +244,8 @@ pub struct SessionCompleted {
pub struct InitializeEncryptionSession { pub struct InitializeEncryptionSession {
/// Encryption session Id. /// Encryption session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Requestor signature. /// Requestor signature.
pub requestor_signature: SerializableSignature, pub requestor_signature: SerializableSignature,
/// Common point. /// Common point.
@ -242,6 +259,8 @@ pub struct InitializeEncryptionSession {
pub struct ConfirmEncryptionInitialization { pub struct ConfirmEncryptionInitialization {
/// Encryption session Id. /// Encryption session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
} }
/// When encryption session error has occured. /// When encryption session error has occured.
@ -249,6 +268,8 @@ pub struct ConfirmEncryptionInitialization {
pub struct EncryptionSessionError { pub struct EncryptionSessionError {
/// Encryption session Id. /// Encryption session Id.
pub session: MessageSessionId, pub session: MessageSessionId,
/// Session-level nonce.
pub session_nonce: u64,
/// Error message. /// Error message.
pub error: String, pub error: String,
} }
@ -274,6 +295,8 @@ pub struct SigningConsensusMessage {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Consensus message. /// Consensus message.
pub message: ConsensusMessage, pub message: ConsensusMessage,
} }
@ -285,6 +308,8 @@ pub struct SigningGenerationMessage {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Generation message. /// Generation message.
pub message: GenerationMessage, pub message: GenerationMessage,
} }
@ -296,6 +321,8 @@ pub struct RequestPartialSignature {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Request id. /// Request id.
pub request_id: SerializableSecret, pub request_id: SerializableSecret,
/// Message hash. /// Message hash.
@ -311,6 +338,8 @@ pub struct PartialSignature {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Request id. /// Request id.
pub request_id: SerializableSecret, pub request_id: SerializableSecret,
/// S part of signature. /// S part of signature.
@ -324,6 +353,8 @@ pub struct SigningSessionError {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Error description. /// Error description.
pub error: String, pub error: String,
} }
@ -335,6 +366,8 @@ pub struct SigningSessionCompleted {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
} }
/// Consensus-related decryption message. /// Consensus-related decryption message.
@ -344,6 +377,8 @@ pub struct DecryptionConsensusMessage {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Signing session Id. /// Signing session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Consensus message. /// Consensus message.
pub message: ConsensusMessage, pub message: ConsensusMessage,
} }
@ -355,6 +390,8 @@ pub struct RequestPartialDecryption {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Decryption session Id. /// Decryption session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Request id. /// Request id.
pub request_id: SerializableSecret, pub request_id: SerializableSecret,
/// Is shadow decryption requested? When true, decryption result /// Is shadow decryption requested? When true, decryption result
@ -371,6 +408,8 @@ pub struct PartialDecryption {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Decryption session Id. /// Decryption session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Request id. /// Request id.
pub request_id: SerializableSecret, pub request_id: SerializableSecret,
/// Partially decrypted secret. /// Partially decrypted secret.
@ -386,6 +425,8 @@ pub struct DecryptionSessionError {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Decryption session Id. /// Decryption session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
/// Public key share. /// Public key share.
pub error: String, pub error: String,
} }
@ -397,6 +438,8 @@ pub struct DecryptionSessionCompleted {
pub session: MessageSessionId, pub session: MessageSessionId,
/// Decryption session Id. /// Decryption session Id.
pub sub_session: SerializableSecret, pub sub_session: SerializableSecret,
/// Session-level nonce.
pub session_nonce: u64,
} }
impl GenerationMessage { impl GenerationMessage {
@ -411,6 +454,18 @@ impl GenerationMessage {
GenerationMessage::SessionCompleted(ref msg) => &msg.session, GenerationMessage::SessionCompleted(ref msg) => &msg.session,
} }
} }
pub fn session_nonce(&self) -> u64 {
match *self {
GenerationMessage::InitializeSession(ref msg) => msg.session_nonce,
GenerationMessage::ConfirmInitialization(ref msg) => msg.session_nonce,
GenerationMessage::CompleteInitialization(ref msg) => msg.session_nonce,
GenerationMessage::KeysDissemination(ref msg) => msg.session_nonce,
GenerationMessage::PublicKeyShare(ref msg) => msg.session_nonce,
GenerationMessage::SessionError(ref msg) => msg.session_nonce,
GenerationMessage::SessionCompleted(ref msg) => msg.session_nonce,
}
}
} }
impl EncryptionMessage { impl EncryptionMessage {
@ -421,6 +476,14 @@ impl EncryptionMessage {
EncryptionMessage::EncryptionSessionError(ref msg) => &msg.session, EncryptionMessage::EncryptionSessionError(ref msg) => &msg.session,
} }
} }
pub fn session_nonce(&self) -> u64 {
match *self {
EncryptionMessage::InitializeEncryptionSession(ref msg) => msg.session_nonce,
EncryptionMessage::ConfirmEncryptionInitialization(ref msg) => msg.session_nonce,
EncryptionMessage::EncryptionSessionError(ref msg) => msg.session_nonce,
}
}
} }
impl DecryptionMessage { impl DecryptionMessage {
@ -443,6 +506,16 @@ impl DecryptionMessage {
DecryptionMessage::DecryptionSessionCompleted(ref msg) => &msg.sub_session, DecryptionMessage::DecryptionSessionCompleted(ref msg) => &msg.sub_session,
} }
} }
pub fn session_nonce(&self) -> u64 {
match *self {
DecryptionMessage::DecryptionConsensusMessage(ref msg) => msg.session_nonce,
DecryptionMessage::RequestPartialDecryption(ref msg) => msg.session_nonce,
DecryptionMessage::PartialDecryption(ref msg) => msg.session_nonce,
DecryptionMessage::DecryptionSessionError(ref msg) => msg.session_nonce,
DecryptionMessage::DecryptionSessionCompleted(ref msg) => msg.session_nonce,
}
}
} }
impl SigningMessage { impl SigningMessage {
@ -467,6 +540,17 @@ impl SigningMessage {
SigningMessage::SigningSessionCompleted(ref msg) => &msg.sub_session, SigningMessage::SigningSessionCompleted(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,
}
}
} }
impl fmt::Display for Message { impl fmt::Display for Message {

View File

@ -90,6 +90,10 @@ pub enum Error {
/// Message or some data in the message was recognized as invalid. /// Message or some data in the message was recognized as invalid.
/// This means that node is misbehaving/cheating. /// This means that node is misbehaving/cheating.
InvalidMessage, InvalidMessage,
/// Message version is not supported.
InvalidMessageVersion,
/// Message is invalid because of replay-attack protection.
ReplayProtection,
/// Connection to node, required for this session is not established. /// Connection to node, required for this session is not established.
NodeDisconnected, NodeDisconnected,
/// Cryptographic error. /// Cryptographic error.
@ -140,6 +144,8 @@ impl fmt::Display for Error {
Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"),
Error::InvalidNodeForRequest => write!(f, "invalid node for this request"), Error::InvalidNodeForRequest => write!(f, "invalid node for this request"),
Error::InvalidMessage => write!(f, "invalid message is received"), Error::InvalidMessage => write!(f, "invalid message is received"),
Error::InvalidMessageVersion => write!(f, "unsupported message is received"),
Error::ReplayProtection => write!(f, "replay message is received"),
Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"),
Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), Error::EthKey(ref e) => write!(f, "cryptographic error {}", e),
Error::Io(ref e) => write!(f, "i/o error {}", e), Error::Io(ref e) => write!(f, "i/o error {}", e),

View File

@ -63,6 +63,8 @@ struct SessionCore {
pub key_share: DocumentKeyShare, pub key_share: DocumentKeyShare,
/// Cluster which allows this node to send messages to other nodes in the cluster. /// Cluster which allows this node to send messages to other nodes in the cluster.
pub cluster: Arc<Cluster>, pub cluster: Arc<Cluster>,
/// Session-level nonce.
pub nonce: u64,
/// SessionImpl completion condvar. /// SessionImpl completion condvar.
pub completed: Condvar, pub completed: Condvar,
} }
@ -108,6 +110,8 @@ pub struct SessionParams {
pub acl_storage: Arc<AclStorage>, pub acl_storage: Arc<AclStorage>,
/// Cluster /// Cluster
pub cluster: Arc<Cluster>, pub cluster: Arc<Cluster>,
/// Session nonce.
pub nonce: u64,
} }
/// Signing consensus transport. /// Signing consensus transport.
@ -116,6 +120,8 @@ struct SigningConsensusTransport {
id: SessionId, id: SessionId,
/// Session access key. /// Session access key.
access_key: Secret, access_key: Secret,
/// Session-level nonce.
nonce: u64,
/// Cluster. /// Cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
} }
@ -126,6 +132,8 @@ struct SessionKeyGenerationTransport {
access_key: Secret, access_key: Secret,
/// Cluster. /// Cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
/// Session-level nonce.
nonce: u64,
/// Other nodes ids. /// Other nodes ids.
other_nodes_ids: BTreeSet<NodeId>, other_nodes_ids: BTreeSet<NodeId>,
} }
@ -134,8 +142,10 @@ struct SessionKeyGenerationTransport {
struct SigningJobTransport { struct SigningJobTransport {
/// Session id. /// Session id.
id: SessionId, id: SessionId,
//// Session access key. /// Session access key.
access_key: Secret, access_key: Secret,
/// Session-level nonce.
nonce: u64,
/// Cluster. /// Cluster.
cluster: Arc<Cluster>, cluster: Arc<Cluster>,
} }
@ -156,6 +166,7 @@ impl SessionImpl {
let consensus_transport = SigningConsensusTransport { let consensus_transport = SigningConsensusTransport {
id: params.meta.id.clone(), id: params.meta.id.clone(),
access_key: params.access_key.clone(), access_key: params.access_key.clone(),
nonce: params.nonce,
cluster: params.cluster.clone(), cluster: params.cluster.clone(),
}; };
@ -165,6 +176,7 @@ impl SessionImpl {
access_key: params.access_key, access_key: params.access_key,
key_share: params.key_share, key_share: params.key_share,
cluster: params.cluster, cluster: params.cluster,
nonce: params.nonce,
completed: Condvar::new(), completed: Condvar::new(),
}, },
data: Mutex::new(SessionData { data: Mutex::new(SessionData {
@ -208,8 +220,10 @@ impl SessionImpl {
cluster: Arc::new(SessionKeyGenerationTransport { cluster: Arc::new(SessionKeyGenerationTransport {
access_key: self.core.access_key.clone(), access_key: self.core.access_key.clone(),
cluster: self.core.cluster.clone(), cluster: self.core.cluster.clone(),
nonce: self.core.nonce,
other_nodes_ids: BTreeSet::new() other_nodes_ids: BTreeSet::new()
}), }),
nonce: None,
}); });
generation_session.initialize(Public::default(), 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect())?; generation_session.initialize(Public::default(), 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect())?;
@ -232,6 +246,10 @@ impl SessionImpl {
/// Process signing message. /// Process signing message.
pub fn process_message(&self, sender: &NodeId, message: &SigningMessage) -> Result<(), Error> { pub fn process_message(&self, sender: &NodeId, message: &SigningMessage) -> Result<(), Error> {
if self.core.nonce != message.session_nonce() {
return Err(Error::ReplayProtection);
}
match message { match message {
&SigningMessage::SigningConsensusMessage(ref message) => &SigningMessage::SigningConsensusMessage(ref message) =>
self.on_consensus_message(sender, message), self.on_consensus_message(sender, message),
@ -274,8 +292,10 @@ impl SessionImpl {
cluster: Arc::new(SessionKeyGenerationTransport { cluster: Arc::new(SessionKeyGenerationTransport {
access_key: self.core.access_key.clone(), access_key: self.core.access_key.clone(),
cluster: self.core.cluster.clone(), cluster: self.core.cluster.clone(),
nonce: self.core.nonce,
other_nodes_ids: other_consensus_group_nodes, other_nodes_ids: other_consensus_group_nodes,
}), }),
nonce: None,
}); });
generation_session.initialize(Public::default(), self.core.key_share.threshold, consensus_group)?; generation_session.initialize(Public::default(), self.core.key_share.threshold, consensus_group)?;
data.generation_session = Some(generation_session); data.generation_session = Some(generation_session);
@ -308,8 +328,10 @@ impl SessionImpl {
cluster: Arc::new(SessionKeyGenerationTransport { cluster: Arc::new(SessionKeyGenerationTransport {
access_key: self.core.access_key.clone(), access_key: self.core.access_key.clone(),
cluster: self.core.cluster.clone(), cluster: self.core.cluster.clone(),
nonce: self.core.nonce,
other_nodes_ids: other_consensus_group_nodes other_nodes_ids: other_consensus_group_nodes
}), }),
nonce: None,
}); });
data.generation_session = Some(generation_session); data.generation_session = Some(generation_session);
data.state = SessionState::SessionKeyGeneration; data.state = SessionState::SessionKeyGeneration;
@ -390,6 +412,7 @@ impl SessionImpl {
self.core.cluster.send(&node, Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted { self.core.cluster.send(&node, Message::Signing(SigningMessage::SigningSessionCompleted(SigningSessionCompleted {
session: self.core.meta.id.clone().into(), session: self.core.meta.id.clone().into(),
sub_session: self.core.access_key.clone().into(), sub_session: self.core.access_key.clone().into(),
session_nonce: self.core.nonce,
})))?; })))?;
} }
@ -490,6 +513,7 @@ impl SessionKeyGenerationTransport {
Message::Generation(message) => Ok(Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage { Message::Generation(message) => Ok(Message::Signing(SigningMessage::SigningGenerationMessage(SigningGenerationMessage {
session: message.session_id().clone().into(), session: message.session_id().clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
message: message, message: message,
}))), }))),
_ => Err(Error::InvalidMessage), _ => Err(Error::InvalidMessage),
@ -517,6 +541,7 @@ impl SessionCore {
SigningJobTransport { SigningJobTransport {
id: self.meta.id.clone(), id: self.meta.id.clone(),
access_key: self.access_key.clone(), access_key: self.access_key.clone(),
nonce: self.nonce,
cluster: self.cluster.clone() cluster: self.cluster.clone()
} }
} }
@ -535,6 +560,7 @@ impl JobTransport for SigningConsensusTransport {
self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession {
requestor_signature: request.into(), requestor_signature: request.into(),
}) })
@ -545,6 +571,7 @@ impl JobTransport for SigningConsensusTransport {
self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage { self.cluster.send(node, Message::Signing(SigningMessage::SigningConsensusMessage(SigningConsensusMessage {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization {
is_confirmed: response, is_confirmed: response,
}) })
@ -560,6 +587,7 @@ impl JobTransport for SigningJobTransport {
self.cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature { self.cluster.send(node, Message::Signing(SigningMessage::RequestPartialSignature(RequestPartialSignature {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
request_id: request.id.into(), request_id: request.id.into(),
message_hash: request.message_hash.into(), message_hash: request.message_hash.into(),
nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(),
@ -570,6 +598,7 @@ impl JobTransport for SigningJobTransport {
self.cluster.send(node, Message::Signing(SigningMessage::PartialSignature(PartialSignature { self.cluster.send(node, Message::Signing(SigningMessage::PartialSignature(PartialSignature {
session: self.id.clone().into(), session: self.id.clone().into(),
sub_session: self.access_key.clone().into(), sub_session: self.access_key.clone().into(),
session_nonce: self.nonce,
request_id: response.request_id.into(), request_id: response.request_id.into(),
partial_signature: response.partial_signature.into(), partial_signature: response.partial_signature.into(),
}))) })))
@ -630,6 +659,7 @@ mod tests {
key_share: gl_node.key_storage.get(&session_id).unwrap(), key_share: gl_node.key_storage.get(&session_id).unwrap(),
acl_storage: acl_storage, acl_storage: acl_storage,
cluster: cluster.clone(), cluster: cluster.clone(),
nonce: 0,
}, if i == 0 { signature.clone() } else { None }).unwrap(); }, if i == 0 { signature.clone() } else { None }).unwrap();
nodes.insert(gl_node_id.clone(), Node { node_id: gl_node_id.clone(), cluster: cluster, session: session }); nodes.insert(gl_node_id.clone(), Node { node_id: gl_node_id.clone(), cluster: cluster, session: session });
} }
@ -764,6 +794,7 @@ mod tests {
}, },
acl_storage: Arc::new(DummyAclStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()),
cluster: Arc::new(DummyCluster::new(self_node_id.clone())), cluster: Arc::new(DummyCluster::new(self_node_id.clone())),
nonce: 0,
}, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) {
Ok(_) => (), Ok(_) => (),
_ => panic!("unexpected"), _ => panic!("unexpected"),
@ -794,6 +825,7 @@ mod tests {
}, },
acl_storage: Arc::new(DummyAclStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()),
cluster: Arc::new(DummyCluster::new(self_node_id.clone())), cluster: Arc::new(DummyCluster::new(self_node_id.clone())),
nonce: 0,
}, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) {
Err(Error::InvalidNodesConfiguration) => (), Err(Error::InvalidNodesConfiguration) => (),
_ => panic!("unexpected"), _ => panic!("unexpected"),
@ -824,6 +856,7 @@ mod tests {
}, },
acl_storage: Arc::new(DummyAclStorage::default()), acl_storage: Arc::new(DummyAclStorage::default()),
cluster: Arc::new(DummyCluster::new(self_node_id.clone())), cluster: Arc::new(DummyCluster::new(self_node_id.clone())),
nonce: 0,
}, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) { }, Some(ethkey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap())) {
Err(Error::InvalidThreshold) => (), Err(Error::InvalidThreshold) => (),
_ => panic!("unexpected"), _ => panic!("unexpected"),
@ -862,6 +895,7 @@ mod tests {
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(), &SigningConsensusMessage {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sl.master().core.access_key.clone().into(), sub_session: sl.master().core.access_key.clone().into(),
session_nonce: 0,
message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization {
is_confirmed: true, is_confirmed: true,
}), }),
@ -874,8 +908,10 @@ mod tests {
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(), &SigningGenerationMessage {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sl.master().core.access_key.clone().into(), sub_session: sl.master().core.access_key.clone().into(),
session_nonce: 0,
message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { message: GenerationMessage::ConfirmInitialization(ConfirmInitialization {
session: SessionId::default().into(), session: SessionId::default().into(),
session_nonce: 0,
derived_point: Public::default().into(), derived_point: Public::default().into(),
}), }),
}), Err(Error::InvalidStateForRequest)); }), Err(Error::InvalidStateForRequest));
@ -893,8 +929,10 @@ mod tests {
assert_eq!(slave1.on_generation_message(&slave2_id, &SigningGenerationMessage { assert_eq!(slave1.on_generation_message(&slave2_id, &SigningGenerationMessage {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sl.master().core.access_key.clone().into(), sub_session: sl.master().core.access_key.clone().into(),
session_nonce: 0,
message: GenerationMessage::InitializeSession(InitializeSession { message: GenerationMessage::InitializeSession(InitializeSession {
session: SessionId::default().into(), session: SessionId::default().into(),
session_nonce: 0,
author: Public::default().into(), author: Public::default().into(),
nodes: BTreeMap::new(), nodes: BTreeMap::new(),
threshold: 1, threshold: 1,
@ -910,6 +948,7 @@ mod tests {
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(), &RequestPartialSignature {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sl.master().core.access_key.clone().into(), sub_session: sl.master().core.access_key.clone().into(),
session_nonce: 0,
request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(),
message_hash: H256::default().into(), message_hash: H256::default().into(),
nodes: Default::default(), nodes: Default::default(),
@ -922,6 +961,7 @@ mod tests {
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(), &RequestPartialSignature {
session: SessionId::default().into(), session: SessionId::default().into(),
sub_session: sl.master().core.access_key.clone().into(), sub_session: sl.master().core.access_key.clone().into(),
session_nonce: 0,
request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(),
message_hash: H256::default().into(), message_hash: H256::default().into(),
nodes: Default::default(), nodes: Default::default(),
@ -983,4 +1023,19 @@ mod tests {
_ => unreachable!(), _ => unreachable!(),
} }
} }
#[test]
fn 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 {
session: SessionId::default().into(),
sub_session: sl.master().core.access_key.clone().into(),
session_nonce: 10,
message: GenerationMessage::ConfirmInitialization(ConfirmInitialization {
session: SessionId::default().into(),
session_nonce: 0,
derived_point: Public::default().into(),
}),
})), Err(Error::ReplayProtection));
}
} }