Skip to content
This repository has been archived by the owner on Feb 8, 2018. It is now read-only.

Commit

Permalink
libp2p: implement EIP-8 RLPx handshake encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
fjl committed Jan 18, 2016
1 parent 3c7748f commit 8d34e97
Show file tree
Hide file tree
Showing 3 changed files with 315 additions and 28 deletions.
100 changes: 79 additions & 21 deletions libp2p/RLPxHandshake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,19 @@ void RLPXHandshake::writeAck()
});
}

void RLPXHandshake::setAuthValues(Signature _sig, Public _remotePubk, h256 _remoteNonce, uint64_t _remoteVersion)
{
_remotePubk.ref().copyTo(m_remote.ref());
_remoteNonce.ref().copyTo(m_remoteNonce.ref());
m_remoteVersion = _remoteVersion;
Secret sharedSecret;
crypto::ecdh::agree(m_host->m_alias.sec(), _remotePubk, sharedSecret);
m_remoteEphemeral = recover(_sig, sharedSecret.makeInsecure() ^ _remoteNonce);
}

void RLPXHandshake::readAuth()
{
clog(NetP2PConnect) << "p2p.connect.ingress recving auth from " << m_socket->remoteEndpoint();
clog(NetP2PConnect) << "p2p.connect.ingress receiving auth from " << m_socket->remoteEndpoint();
m_authCipher.resize(307);
auto self(shared_from_this());
ba::async_read(m_socket->ref(), ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t)
Expand All @@ -83,25 +93,43 @@ void RLPXHandshake::readAuth()
transition(ec);
else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_authCipher), m_auth))
{
bytesConstRef sig(&m_auth[0], Signature::size);
bytesConstRef hepubk(&m_auth[Signature::size], h256::size);
bytesConstRef pubk(&m_auth[Signature::size + h256::size], Public::size);
bytesConstRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size);
pubk.copyTo(m_remote.ref());
nonce.copyTo(m_remoteNonce.ref());

Secret sharedSecret;
crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, sharedSecret);
m_remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret.makeInsecure() ^ m_remoteNonce);
bytesConstRef data(&m_auth);
auto sig = Signature(data.cropped(0, Signature::size));
auto pubk = Public(data.cropped(Signature::size + h256::size, Public::size));
auto nonce = h256(data.cropped(Signature::size + h256::size + Public::size, h256::size));
setAuthValues(sig, pubk, nonce, 4);
transition();
}
else
readAuthEIP8();
});
}

if (sha3(m_remoteEphemeral) != *(h256*)hepubk.data())
clog(NetP2PConnect) << "p2p.connect.ingress auth failed (invalid: hash mismatch) for" << m_socket->remoteEndpoint();

void RLPXHandshake::readAuthEIP8()
{
clog(NetP2PConnect) << "p2p.connect.ingress receiving EIP-8 auth from " << m_socket->remoteEndpoint();
auto length = fromBigEndian<uint16_t>(bytesConstRef(&m_authCipher).cropped(0, 2));
m_authCipher.resize(length + 2);
auto rembuf = ba::buffer(ba::buffer(m_authCipher) + 307);
auto self = shared_from_this();
ba::async_read(m_socket->ref(), rembuf, [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_authCipher).cropped(2), m_auth))
{
RLP rlp(m_auth, RLP::ThrowOnFail | RLP::FailIfTooSmall);
setAuthValues(
rlp[0].toHash<Signature>(),
rlp[1].toHash<Public>(),
rlp[2].toHash<h256>(),
rlp[3].toInt<uint64_t>()
);
transition();
}
else
{
clog(NetP2PConnect) << "p2p.connect.ingress recving auth decrypt failed for" << m_socket->remoteEndpoint();
clog(NetP2PConnect) << "p2p.connect.ingress auth decrypt failed for" << m_socket->remoteEndpoint();
m_nextState = Error;
transition();
}
Expand All @@ -110,7 +138,7 @@ void RLPXHandshake::readAuth()

void RLPXHandshake::readAck()
{
clog(NetP2PConnect) << "p2p.connect.egress recving ack from " << m_socket->remoteEndpoint();
clog(NetP2PConnect) << "p2p.connect.egress receiving ack from " << m_socket->remoteEndpoint();
m_ackCipher.resize(210);
auto self(shared_from_this());
ba::async_read(m_socket->ref(), ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t)
Expand All @@ -121,29 +149,59 @@ void RLPXHandshake::readAck()
{
bytesConstRef(&m_ack).cropped(0, Public::size).copyTo(m_remoteEphemeral.ref());
bytesConstRef(&m_ack).cropped(Public::size, h256::size).copyTo(m_remoteNonce.ref());
m_remoteVersion = 4;
transition();
}
else
readAckEIP8();
});
}

void RLPXHandshake::readAckEIP8()
{
clog(NetP2PConnect) << "p2p.connect.egress receiving EIP-8 ack from " << m_socket->remoteEndpoint();
auto length = fromBigEndian<uint16_t>(bytesConstRef(&m_ackCipher).cropped(0, 2));
m_ackCipher.resize(length + 2);
auto rembuf = ba::buffer(ba::buffer(m_ackCipher) + 210);
auto self = shared_from_this();
ba::async_read(m_socket->ref(), rembuf, [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_ackCipher).cropped(2), m_ack))
{
RLP rlp(m_ack, RLP::ThrowOnFail | RLP::FailIfTooSmall);
m_remoteEphemeral = rlp[0].toHash<Public>();
m_remoteNonce = rlp[1].toHash<h256>();
m_remoteVersion = rlp[2].toInt<uint64_t>();
transition();
}
else
{
clog(NetP2PConnect) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remoteEndpoint();
clog(NetP2PConnect) << "p2p.connect.egress ack decrypt failed for " << m_socket->remoteEndpoint();
m_nextState = Error;
transition();
}
});
}

void RLPXHandshake::error()
void RLPXHandshake::cancel()
{
m_cancel = true;
m_idleTimer.cancel();

m_socket->close();
m_io.reset();
}

void RLPXHandshake::error()
{
auto connected = m_socket->isConnected();
if (connected && !m_socket->remoteEndpoint().address().is_unspecified())
clog(NetP2PConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)";
else
clog(NetP2PConnect) << "Handshake Failed (Connection reset by peer)";

m_socket->close();
m_io.reset();
cancel();
}

void RLPXHandshake::transition(boost::system::error_code _ech)
Expand Down
20 changes: 15 additions & 5 deletions libp2p/RLPxHandshake.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>
/// Start handshake.
void start() { transition(); }

/// Cancels handshake preventing
void cancel() { m_cancel = true; m_socket->close(); }
/// Aborts the handshake.
void cancel();

protected:
/// Sequential states of handshake
enum State
Expand All @@ -79,21 +79,30 @@ class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>

/// Write Auth message to socket and transitions to AckAuth.
void writeAuth();

/// Reads Auth message from socket and transitions to AckAuth.
void readAuth();

/// Continues reading Auth message in EIP-8 format and transitions to AckAuthEIP8.
void readAuthEIP8();

/// Derives ephemeral secret from signature and sets members after Auth has been decrypted.
void setAuthValues(Signature sig, Public remotePubk, h256 remoteNonce, uint64_t remoteVersion);

/// Write Ack message to socket and transitions to WriteHello.
void writeAck();

/// Reads Auth message from socket and transitions to WriteHello.
void readAck();

/// Continues reading Ack message in EIP-8 format and transitions to WriteHello.
void readAckEIP8();

/// Closes connection and ends transitions.
void error();

/// Performs transition for m_nextState.
void transition(boost::system::error_code _ech = boost::system::error_code());
virtual void transition(boost::system::error_code _ech = boost::system::error_code());

/// Timeout for remote to respond to transition events. Enforced by m_idleTimer and refreshed by transition().
boost::posix_time::milliseconds const c_timeout = boost::posix_time::milliseconds(1800);
Expand All @@ -120,6 +129,7 @@ class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>

Public m_remoteEphemeral; ///< Remote ephemeral public key.
h256 m_remoteNonce; ///< Nonce generated by remote host for handshake.
uint64_t m_remoteVersion;

/// Used to read and write RLPx encrypted frames for last step of handshake authentication.
/// Passed onto Host which will take ownership.
Expand Down
Loading

0 comments on commit 8d34e97

Please sign in to comment.