Move ethcore files back into root.
This commit is contained in:
217
util/src/network/handshake.rs
Normal file
217
util/src/network/handshake.rs
Normal file
@@ -0,0 +1,217 @@
|
||||
use mio::*;
|
||||
use mio::tcp::*;
|
||||
use hash::*;
|
||||
use sha3::Hashable;
|
||||
use bytes::Bytes;
|
||||
use crypto::*;
|
||||
use crypto;
|
||||
use network::connection::{Connection};
|
||||
use network::host::{HostInfo};
|
||||
use network::node::NodeId;
|
||||
use error::*;
|
||||
use network::error::NetworkError;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
enum HandshakeState {
|
||||
/// Just created
|
||||
New,
|
||||
/// Waiting for auth packet
|
||||
ReadingAuth,
|
||||
/// Waiting for ack packet
|
||||
ReadingAck,
|
||||
/// Ready to start a session
|
||||
StartSession,
|
||||
}
|
||||
|
||||
/// RLPx protocol handhake. See https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake
|
||||
pub struct Handshake {
|
||||
/// Remote node public key
|
||||
pub id: NodeId,
|
||||
/// Underlying connection
|
||||
pub connection: Connection,
|
||||
/// Handshake state
|
||||
state: HandshakeState,
|
||||
/// Outgoing or incoming connection
|
||||
pub originated: bool,
|
||||
/// Disconnect timeout
|
||||
idle_timeout: Option<Timeout>,
|
||||
/// ECDH ephemeral
|
||||
pub ecdhe: KeyPair,
|
||||
/// Connection nonce
|
||||
pub nonce: H256,
|
||||
/// Handshake public key
|
||||
pub remote_public: Public,
|
||||
/// Remote connection nonce.
|
||||
pub remote_nonce: H256,
|
||||
/// A copy of received encryped auth packet
|
||||
pub auth_cipher: Bytes,
|
||||
/// A copy of received encryped ack packet
|
||||
pub ack_cipher: Bytes
|
||||
}
|
||||
|
||||
const AUTH_PACKET_SIZE: usize = 307;
|
||||
const ACK_PACKET_SIZE: usize = 210;
|
||||
|
||||
impl Handshake {
|
||||
/// Create a new handshake object
|
||||
pub fn new(token: Token, id: &NodeId, socket: TcpStream, nonce: &H256) -> Result<Handshake, UtilError> {
|
||||
Ok(Handshake {
|
||||
id: id.clone(),
|
||||
connection: Connection::new(token, socket),
|
||||
originated: false,
|
||||
state: HandshakeState::New,
|
||||
idle_timeout: None,
|
||||
ecdhe: try!(KeyPair::create()),
|
||||
nonce: nonce.clone(),
|
||||
remote_public: Public::new(),
|
||||
remote_nonce: H256::new(),
|
||||
auth_cipher: Bytes::new(),
|
||||
ack_cipher: Bytes::new(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Start a handhsake
|
||||
pub fn start(&mut self, host: &HostInfo, originated: bool) -> Result<(), UtilError> {
|
||||
self.originated = originated;
|
||||
if originated {
|
||||
try!(self.write_auth(host));
|
||||
}
|
||||
else {
|
||||
self.state = HandshakeState::ReadingAuth;
|
||||
self.connection.expect(AUTH_PACKET_SIZE);
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if handshake is complete
|
||||
pub fn done(&self) -> bool {
|
||||
self.state == HandshakeState::StartSession
|
||||
}
|
||||
|
||||
/// Readable IO handler. Drives the state change.
|
||||
pub fn readable<Host:Handler>(&mut self, event_loop: &mut EventLoop<Host>, host: &HostInfo) -> Result<(), UtilError> {
|
||||
self.idle_timeout.map(|t| event_loop.clear_timeout(t));
|
||||
match self.state {
|
||||
HandshakeState::ReadingAuth => {
|
||||
match try!(self.connection.readable()) {
|
||||
Some(data) => {
|
||||
try!(self.read_auth(host, &data));
|
||||
try!(self.write_ack());
|
||||
},
|
||||
None => {}
|
||||
};
|
||||
},
|
||||
HandshakeState::ReadingAck => {
|
||||
match try!(self.connection.readable()) {
|
||||
Some(data) => {
|
||||
try!(self.read_ack(host, &data));
|
||||
self.state = HandshakeState::StartSession;
|
||||
},
|
||||
None => {}
|
||||
};
|
||||
},
|
||||
_ => { panic!("Unexpected state"); }
|
||||
}
|
||||
if self.state != HandshakeState::StartSession {
|
||||
try!(self.connection.reregister(event_loop));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writabe IO handler.
|
||||
pub fn writable<Host:Handler>(&mut self, event_loop: &mut EventLoop<Host>, _host: &HostInfo) -> Result<(), UtilError> {
|
||||
self.idle_timeout.map(|t| event_loop.clear_timeout(t));
|
||||
try!(self.connection.writable());
|
||||
if self.state != HandshakeState::StartSession {
|
||||
try!(self.connection.reregister(event_loop));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Register the IO handler with the event loop
|
||||
pub fn register<Host:Handler<Timeout=Token>>(&mut self, event_loop: &mut EventLoop<Host>) -> Result<(), UtilError> {
|
||||
self.idle_timeout.map(|t| event_loop.clear_timeout(t));
|
||||
self.idle_timeout = event_loop.timeout_ms(self.connection.token, 1800).ok();
|
||||
try!(self.connection.register(event_loop));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse, validate and confirm auth message
|
||||
fn read_auth(&mut self, host: &HostInfo, data: &[u8]) -> Result<(), UtilError> {
|
||||
trace!(target:"net", "Received handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||
assert!(data.len() == AUTH_PACKET_SIZE);
|
||||
self.auth_cipher = data.to_vec();
|
||||
let auth = try!(ecies::decrypt(host.secret(), data));
|
||||
let (sig, rest) = auth.split_at(65);
|
||||
let (hepubk, rest) = rest.split_at(32);
|
||||
let (pubk, rest) = rest.split_at(64);
|
||||
let (nonce, _) = rest.split_at(32);
|
||||
self.remote_public.clone_from_slice(pubk);
|
||||
self.remote_nonce.clone_from_slice(nonce);
|
||||
let shared = try!(ecdh::agree(host.secret(), &self.remote_public));
|
||||
let signature = Signature::from_slice(sig);
|
||||
let spub = try!(ec::recover(&signature, &(&shared ^ &self.remote_nonce)));
|
||||
if &spub.sha3()[..] != hepubk {
|
||||
trace!(target:"net", "Handshake hash mismath with {:?}", self.connection.socket.peer_addr());
|
||||
return Err(From::from(NetworkError::Auth));
|
||||
};
|
||||
self.write_ack()
|
||||
}
|
||||
|
||||
/// Parse and validate ack message
|
||||
fn read_ack(&mut self, host: &HostInfo, data: &[u8]) -> Result<(), UtilError> {
|
||||
trace!(target:"net", "Received handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||
assert!(data.len() == ACK_PACKET_SIZE);
|
||||
self.ack_cipher = data.to_vec();
|
||||
let ack = try!(ecies::decrypt(host.secret(), data));
|
||||
self.remote_public.clone_from_slice(&ack[0..64]);
|
||||
self.remote_nonce.clone_from_slice(&ack[64..(64+32)]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends auth message
|
||||
fn write_auth(&mut self, host: &HostInfo) -> Result<(), UtilError> {
|
||||
trace!(target:"net", "Sending handshake auth to {:?}", self.connection.socket.peer_addr());
|
||||
let mut data = [0u8; /*Signature::SIZE*/ 65 + /*H256::SIZE*/ 32 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32 + 1]; //TODO: use associated constants
|
||||
let len = data.len();
|
||||
{
|
||||
data[len - 1] = 0x0;
|
||||
let (sig, rest) = data.split_at_mut(65);
|
||||
let (hepubk, rest) = rest.split_at_mut(32);
|
||||
let (pubk, rest) = rest.split_at_mut(64);
|
||||
let (nonce, _) = rest.split_at_mut(32);
|
||||
|
||||
// E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
|
||||
let shared = try!(crypto::ecdh::agree(host.secret(), &self.id));
|
||||
try!(crypto::ec::sign(self.ecdhe.secret(), &(&shared ^ &self.nonce))).copy_to(sig);
|
||||
self.ecdhe.public().sha3_into(hepubk);
|
||||
host.id().copy_to(pubk);
|
||||
self.nonce.copy_to(nonce);
|
||||
}
|
||||
let message = try!(crypto::ecies::encrypt(&self.id, &data));
|
||||
self.auth_cipher = message.clone();
|
||||
self.connection.send(message);
|
||||
self.connection.expect(ACK_PACKET_SIZE);
|
||||
self.state = HandshakeState::ReadingAck;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Sends ack message
|
||||
fn write_ack(&mut self) -> Result<(), UtilError> {
|
||||
trace!(target:"net", "Sending handshake ack to {:?}", self.connection.socket.peer_addr());
|
||||
let mut data = [0u8; 1 + /*Public::SIZE*/ 64 + /*H256::SIZE*/ 32]; //TODO: use associated constants
|
||||
let len = data.len();
|
||||
{
|
||||
data[len - 1] = 0x0;
|
||||
let (epubk, rest) = data.split_at_mut(64);
|
||||
let (nonce, _) = rest.split_at_mut(32);
|
||||
self.ecdhe.public().copy_to(epubk);
|
||||
self.nonce.copy_to(nonce);
|
||||
}
|
||||
let message = try!(crypto::ecies::encrypt(&self.id, &data));
|
||||
self.ack_cipher = message.clone();
|
||||
self.connection.send(message);
|
||||
self.state = HandshakeState::StartSession;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user