Browse Source

sharedptr wrapper class for socket

cl-refactor
subtly 10 years ago
parent
commit
a0aaf614f3
  1. 30
      libp2p/Host.cpp
  2. 6
      libp2p/Host.h
  3. 206
      libp2p/RLPxFrameIO.cpp
  4. 97
      libp2p/RLPxFrameIO.h
  5. 210
      libp2p/RLPxHandshake.cpp
  6. 54
      libp2p/RLPxHandshake.h
  7. 4
      libp2p/Session.cpp
  8. 3
      libp2p/Session.h

30
libp2p/Host.cpp

@ -157,7 +157,7 @@ unsigned Host::protocolVersion() const
return 4;
}
bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket, RLPXFrameIO* _io)
bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint)
{
/// Get or create Peer
shared_ptr<Peer> p;
@ -172,7 +172,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket
p->m_lastConnected = std::chrono::system_clock::now();
p->m_failedAttempts = 0;
// TODO: update pendingconns w/session-weak-ptr for graceful shutdown (otherwise this line isn't safe)
p->endpoint.tcp.address(_socket->remote_endpoint().address());
p->endpoint.tcp.address(_endpoint.address());
auto protocolVersion = _rlp[1].toInt<unsigned>();
auto clientVersion = _rlp[2].toString();
@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket
clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort;
// create session so disconnects are managed
auto ps = make_shared<Session>(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet<CapDesc>(), 0, map<string, string>()}));
auto ps = make_shared<Session>(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet<CapDesc>(), 0, map<string, string>()}));
if (protocolVersion != this->protocolVersion())
{
ps->disconnect(IncompatibleProtocol);
@ -368,8 +368,8 @@ void Host::runAcceptor()
// case the socket may be open and must be closed to prevent asio from
// processing socket events after socket is deallocated.
bi::tcp::socket *s = new bi::tcp::socket(m_ioService);
m_tcp4Acceptor.async_accept(*s, [=](boost::system::error_code ec)
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec)
{
// if no error code
bool success = false;
@ -378,7 +378,7 @@ void Host::runAcceptor()
try
{
// incoming connection; we don't yet know nodeid
auto handshake = make_shared<RLPXHandshake>(this, s);
auto handshake = make_shared<RLPXHandshake>(this, socket);
handshake->start();
success = true;
}
@ -392,17 +392,8 @@ void Host::runAcceptor()
}
}
// asio doesn't close socket on error
if (!success)
{
if (s->is_open())
{
boost::system::error_code ec;
s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
s->close();
}
delete s;
}
socket->ref().close();
m_accepting = false;
if (ec.value() < 1)
@ -485,20 +476,19 @@ void Host::connect(std::shared_ptr<Peer> const& _p)
}
clog(NetConnect) << "Attempting connection to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "from" << id().abridged();
bi::tcp::socket* s = new bi::tcp::socket(m_ioService);
s->async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec)
auto socket = make_shared<RLPXSocket>(new bi::tcp::socket(m_ioService));
socket->ref().async_connect(_p->peerEndpoint(), [=](boost::system::error_code const& ec)
{
if (ec)
{
clog(NetConnect) << "Connection refused to node" << _p->id.abridged() << "@" << _p->peerEndpoint() << "(" << ec.message() << ")";
_p->m_lastDisconnect = TCPError;
_p->m_lastAttempted = std::chrono::system_clock::now();
delete s;
}
else
{
clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint();
auto handshake = make_shared<RLPXHandshake>(this, s, _p->id);
auto handshake = make_shared<RLPXHandshake>(this, socket, _p->id);
handshake->start();
}
Guard l(x_pendingNodeConns);

6
libp2p/Host.h

@ -40,7 +40,7 @@
#include "HostCapability.h"
#include "Network.h"
#include "Peer.h"
#include "RLPxHandshake.h"
#include "RLPxFrameIO.h"
#include "Common.h"
namespace ba = boost::asio;
namespace bi = ba::ip;
@ -150,8 +150,8 @@ public:
NodeId id() const { return m_alias.pub(); }
/// Validates and starts peer session, taking ownership of _socket. Disconnects and returns false upon error.
bool startPeerSession(Public const& _id, RLP const& _hello, bi::tcp::socket* _socket, RLPXFrameIO* _io);
/// Validates and starts peer session, taking ownership of _io. Disconnects and returns false upon error.
bool startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint);
protected:
void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e);

206
libp2p/RLPxFrameIO.cpp

@ -0,0 +1,206 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameIO.cpp
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#include "Host.h"
#include "Session.h"
#include "Peer.h"
#include "RLPxHandshake.h"
#include "RLPxFrameIO.h"
using namespace std;
using namespace dev;
using namespace dev::p2p;
using namespace CryptoPP;
RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket)
{
// we need:
// originated?
// Secret == output of ecdhe agreement
// authCipher
// ackCipher
bytes keyMaterialBytes(64);
bytesRef keyMaterial(&keyMaterialBytes);
// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
Secret ephemeralShared;
_init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared);
ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size));
h512 nonceMaterial;
h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce;
h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce;
leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size));
rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size));
auto outRef(keyMaterial.cropped(h256::size, h256::size));
sha3(nonceMaterial.ref(), outRef); // output h(nonces)
sha3(keyMaterial, outRef); // output shared-secret
// token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk)
// aes-secret = sha3(ecdhe-shared-secret || shared-secret)
sha3(keyMaterial, outRef); // output aes-secret
m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data());
m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data());
// mac-secret = sha3(ecdhe-shared-secret || aes-secret)
sha3(keyMaterial, outRef); // output mac-secret
m_macEnc.SetKey(outRef.data(), h128::size);
// Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init)
// ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack)
// Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack)
// ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init)
(*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial);
bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher;
keyMaterialBytes.resize(h256::size + egressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size()));
m_egressMac.Update(keyMaterial.data(), keyMaterial.size());
// recover mac-secret by re-xoring remoteNonce
(*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial);
bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher;
keyMaterialBytes.resize(h256::size + ingressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
m_ingressMac.Update(keyMaterial.data(), keyMaterial.size());
}
void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
{
// _packet = type || rlpList()
// current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id);
RLPStream header;
header.appendRaw(bytes({byte(_packet.size() >> 16), byte(_packet.size() >> 8), byte(_packet.size())}));
// zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize).
header.appendRaw(bytes({0xc2,0x80,0x80}));
// TODO: SECURITY check that header is <= 16 bytes
bytes headerWithMac;
header.swapOut(headerWithMac);
headerWithMac.resize(32);
m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16);
updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16));
egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size));
auto padding = (16 - (_packet.size() % 16)) % 16;
o_bytes.swap(headerWithMac);
o_bytes.resize(32 + _packet.size() + padding + h128::size);
bytesRef packetRef(o_bytes.data() + 32, _packet.size());
m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size());
bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding);
if (padding)
m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding);
bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding);
updateEgressMACWithEndOfFrame(packetWithPaddingRef);
bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size);
egressDigest().ref().copyTo(macRef);
clog(NetConnect) << "SENT FRAME " << _packet.size() << *(h128*)macRef.data();
clog(NetConnect) << "FRAME TAIL " << *(h128*)(o_bytes.data() + 32 + _packet.size() + padding);
}
bool RLPXFrameIO::authAndDecryptHeader(h256& io)
{
updateIngressMACWithHeader(io.ref());
bytesConstRef macRef = io.ref().cropped(h128::size, h128::size);
if (*(h128*)macRef.data() != ingressDigest())
return false;
m_frameDec.ProcessData(io.data(), io.data(), 16);
return true;
}
bool RLPXFrameIO::authAndDecryptFrame(bytesRef io)
{
bytesRef cipherText(io.cropped(0, io.size() - h128::size));
updateIngressMACWithEndOfFrame(cipherText);
bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size);
if (*(h128*)frameMac.data() != ingressDigest())
return false;
m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size);
return true;
}
h128 RLPXFrameIO::egressDigest()
{
SHA3_256 h(m_egressMac);
h128 digest;
h.TruncatedFinal(digest.data(), h128::size);
return move(digest);
}
h128 RLPXFrameIO::ingressDigest()
{
SHA3_256 h(m_ingressMac);
h128 digest;
h.TruncatedFinal(digest.data(), h128::size);
return move(digest);
}
void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher)
{
updateMAC(m_egressMac, *(h128*)_headerCipher.data());
}
void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher)
{
m_egressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_egressMac);
{
SHA3_256 prev(m_egressMac);
h128 digest;
prev.TruncatedFinal(digest.data(), h128::size);
clog(NetConnect) << "EGRESS FRAMEMAC " << _cipher.size() << digest;
}
}
void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher)
{
updateMAC(m_ingressMac, *(h128*)_headerCipher.data());
}
void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher)
{
m_ingressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_ingressMac);
{
SHA3_256 prev(m_ingressMac);
h128 digest;
prev.TruncatedFinal(digest.data(), h128::size);
clog(NetConnect) << "INGRESS FRAMEMAC " << _cipher.size() << digest;
}
}
void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed)
{
SHA3_256 prevDigest(_mac);
h128 prevDigestOut;
prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size);
h128 encDigest;
m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size);
encDigest ^= (!!_seed ? _seed : prevDigestOut);
// update mac for final digest
_mac.Update(encDigest.data(), h128::size);
}

97
libp2p/RLPxFrameIO.h

@ -0,0 +1,97 @@
/*
This file is part of cpp-ethereum.
cpp-ethereum is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
cpp-ethereum is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file RLPXFrameIO.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2015
*/
#pragma once
#include <memory>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h>
#include "Common.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
namespace dev
{
namespace p2p
{
class RLPXHandshake;
class RLPXSocket: public std::enable_shared_from_this<RLPXSocket>
{
public:
RLPXSocket(bi::tcp::socket* _socket): m_socket(std::move(*_socket)) {}
~RLPXSocket() { close(); }
bool isConnected() const { return m_socket.is_open(); }
void close() { try { boost::system::error_code ec; m_socket.shutdown(bi::tcp::socket::shutdown_both, ec); if (m_socket.is_open()) m_socket.close(); } catch (...){} }
bi::tcp::endpoint remoteEndpoint() { try { return m_socket.remote_endpoint(); } catch (...){ return bi::tcp::endpoint(); } }
bi::tcp::socket& ref() { return m_socket; }
protected:
bi::tcp::socket m_socket;
};
class RLPXFrameIO
{
friend class Session;
public:
RLPXFrameIO(RLPXHandshake const& _init);
void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes);
/// Authenticates and decrypts header in-place.
bool authAndDecryptHeader(h256& io_cipherWithMac);
/// Authenticates and decrypts frame in-place.
bool authAndDecryptFrame(bytesRef io_cipherWithMac);
h128 egressDigest();
h128 ingressDigest();
void updateEgressMACWithHeader(bytesConstRef _headerCipher);
void updateEgressMACWithEndOfFrame(bytesConstRef _cipher);
void updateIngressMACWithHeader(bytesConstRef _headerCipher);
void updateIngressMACWithEndOfFrame(bytesConstRef _cipher);
protected:
bi::tcp::socket& socket() { return m_socket->ref(); }
private:
void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128());
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption m_frameEnc;
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption m_frameDec;
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_macEnc;
CryptoPP::SHA3_256 m_egressMac;
CryptoPP::SHA3_256 m_ingressMac;
std::shared_ptr<RLPXSocket> m_socket;
};
}
}

210
libp2p/RLPxHandshake.cpp

@ -28,185 +28,9 @@ using namespace dev;
using namespace dev::p2p;
using namespace CryptoPP;
RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket)
{
// we need:
// originated?
// Secret == output of ecdhe agreement
// authCipher
// ackCipher
bytes keyMaterialBytes(64);
bytesRef keyMaterial(&keyMaterialBytes);
// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
Secret ephemeralShared;
_init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared);
ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size));
h512 nonceMaterial;
h256 const& leftNonce = _init.m_originated ? _init.m_remoteNonce : _init.m_nonce;
h256 const& rightNonce = _init.m_originated ? _init.m_nonce : _init.m_remoteNonce;
leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size));
rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size));
auto outRef(keyMaterial.cropped(h256::size, h256::size));
sha3(nonceMaterial.ref(), outRef); // output h(nonces)
sha3(keyMaterial, outRef); // output shared-secret
// token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk)
// aes-secret = sha3(ecdhe-shared-secret || shared-secret)
sha3(keyMaterial, outRef); // output aes-secret
m_frameEnc.SetKeyWithIV(outRef.data(), h128::size, h128().data());
m_frameDec.SetKeyWithIV(outRef.data(), h128::size, h128().data());
// mac-secret = sha3(ecdhe-shared-secret || aes-secret)
sha3(keyMaterial, outRef); // output mac-secret
m_macEnc.SetKey(outRef.data(), h128::size);
// Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init)
// ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack)
// Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack)
// ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init)
(*(h256*)outRef.data() ^ _init.m_remoteNonce).ref().copyTo(keyMaterial);
bytes const& egressCipher = _init.m_originated ? _init.m_authCipher : _init.m_ackCipher;
keyMaterialBytes.resize(h256::size + egressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size()));
m_egressMac.Update(keyMaterial.data(), keyMaterial.size());
// recover mac-secret by re-xoring remoteNonce
(*(h256*)keyMaterial.data() ^ _init.m_remoteNonce ^ _init.m_nonce).ref().copyTo(keyMaterial);
bytes const& ingressCipher = _init.m_originated ? _init.m_ackCipher : _init.m_authCipher;
keyMaterialBytes.resize(h256::size + ingressCipher.size());
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
m_ingressMac.Update(keyMaterial.data(), keyMaterial.size());
}
void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
{
// _packet = type || rlpList()
// current/old packet format: prep(_s).appendList(_args + 1).append((unsigned)_id);
RLPStream header;
header.appendRaw(bytes({byte(_packet.size() >> 16), byte(_packet.size() >> 8), byte(_packet.size())}));
// zeroHeader: []byte{0xC2, 0x80, 0x80}. Should be rlpList(protocolType,seqId,totalPacketSize).
header.appendRaw(bytes({0xc2,0x80,0x80}));
// TODO: SECURITY check that header is <= 16 bytes
bytes headerWithMac;
header.swapOut(headerWithMac);
headerWithMac.resize(32);
m_frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16);
updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16));
egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size));
auto padding = (16 - (_packet.size() % 16)) % 16;
o_bytes.swap(headerWithMac);
o_bytes.resize(32 + _packet.size() + padding + h128::size);
bytesRef packetRef(o_bytes.data() + 32, _packet.size());
m_frameEnc.ProcessData(packetRef.data(), _packet.data(), _packet.size());
bytesRef paddingRef(o_bytes.data() + 32 + _packet.size(), padding);
if (padding)
m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding);
bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding);
updateEgressMACWithEndOfFrame(packetWithPaddingRef);
bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size);
egressDigest().ref().copyTo(macRef);
clog(NetConnect) << "SENT FRAME " << _packet.size() << *(h128*)macRef.data();
clog(NetConnect) << "FRAME TAIL " << *(h128*)(o_bytes.data() + 32 + _packet.size() + padding);
}
bool RLPXFrameIO::authAndDecryptHeader(h256& io)
{
updateIngressMACWithHeader(io.ref());
bytesConstRef macRef = io.ref().cropped(h128::size, h128::size);
if (*(h128*)macRef.data() != ingressDigest())
return false;
m_frameDec.ProcessData(io.data(), io.data(), 16);
return true;
}
bool RLPXFrameIO::authAndDecryptFrame(bytesRef io)
{
bytesRef cipherText(io.cropped(0, io.size() - h128::size));
updateIngressMACWithEndOfFrame(cipherText);
bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size);
if (*(h128*)frameMac.data() != ingressDigest())
return false;
m_frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size);
return true;
}
h128 RLPXFrameIO::egressDigest()
{
SHA3_256 h(m_egressMac);
h128 digest;
h.TruncatedFinal(digest.data(), h128::size);
return move(digest);
}
h128 RLPXFrameIO::ingressDigest()
{
SHA3_256 h(m_ingressMac);
h128 digest;
h.TruncatedFinal(digest.data(), h128::size);
return move(digest);
}
void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher)
{
updateMAC(m_egressMac, *(h128*)_headerCipher.data());
}
void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher)
{
m_egressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_egressMac);
{
SHA3_256 prev(m_egressMac);
h128 digest;
prev.TruncatedFinal(digest.data(), h128::size);
clog(NetConnect) << "EGRESS FRAMEMAC " << _cipher.size() << digest;
}
}
void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher)
{
updateMAC(m_ingressMac, *(h128*)_headerCipher.data());
}
void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher)
{
m_ingressMac.Update(_cipher.data(), _cipher.size());
updateMAC(m_ingressMac);
{
SHA3_256 prev(m_ingressMac);
h128 digest;
prev.TruncatedFinal(digest.data(), h128::size);
clog(NetConnect) << "INGRESS FRAMEMAC " << _cipher.size() << digest;
}
}
void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed)
{
SHA3_256 prevDigest(_mac);
h128 prevDigestOut;
prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size);
h128 encDigest;
m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size);
encDigest ^= (!!_seed ? _seed : prevDigestOut);
// update mac for final digest
_mac.Update(encDigest.data(), h128::size);
}
void RLPXHandshake::writeAuth()
{
clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remote_endpoint();
clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remoteEndpoint();
m_auth.resize(Signature::size + h256::size + Public::size + h256::size + 1);
bytesRef sig(&m_auth[0], Signature::size);
bytesRef hepubk(&m_auth[Signature::size], h256::size);
@ -224,7 +48,7 @@ void RLPXHandshake::writeAuth()
encryptECIES(m_remote, &m_auth, m_authCipher);
auto self(shared_from_this());
ba::async_write(*m_socket, ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t)
ba::async_write(m_socket->ref(), ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t)
{
transition(ec);
});
@ -232,7 +56,7 @@ void RLPXHandshake::writeAuth()
void RLPXHandshake::writeAck()
{
clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remote_endpoint();
clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remoteEndpoint();
m_ack.resize(Public::size + h256::size + 1);
bytesRef epubk(&m_ack[0], Public::size);
bytesRef nonce(&m_ack[Public::size], h256::size);
@ -242,7 +66,7 @@ void RLPXHandshake::writeAck()
encryptECIES(m_remote, &m_ack, m_ackCipher);
auto self(shared_from_this());
ba::async_write(*m_socket, ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t)
ba::async_write(m_socket->ref(), ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t)
{
transition(ec);
});
@ -250,10 +74,10 @@ void RLPXHandshake::writeAck()
void RLPXHandshake::readAuth()
{
clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remote_endpoint();
clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remoteEndpoint();
m_authCipher.resize(307);
auto self(shared_from_this());
ba::async_read(*m_socket, ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t)
ba::async_read(m_socket->ref(), ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
@ -274,7 +98,7 @@ void RLPXHandshake::readAuth()
}
else
{
clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remote_endpoint();
clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remoteEndpoint();
m_nextState = Error;
transition();
}
@ -283,10 +107,10 @@ void RLPXHandshake::readAuth()
void RLPXHandshake::readAck()
{
clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remote_endpoint();
clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remoteEndpoint();
m_ackCipher.resize(210);
auto self(shared_from_this());
ba::async_read(*m_socket, ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t)
ba::async_read(m_socket->ref(), ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t)
{
if (ec)
transition(ec);
@ -298,7 +122,7 @@ void RLPXHandshake::readAck()
}
else
{
clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remote_endpoint();
clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remoteEndpoint();
m_nextState = Error;
transition();
}
@ -307,10 +131,7 @@ void RLPXHandshake::readAck()
void RLPXHandshake::error()
{
clog(NetConnect) << "Disconnecting " << m_socket->remote_endpoint() << " (Handshake Failed)";
boost::system::error_code ec;
m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec);
if (m_socket->is_open())
clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)";
m_socket->close();
}
@ -359,7 +180,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
bytes packet;
s.swapOut(packet);
m_io->writeSingleFramePacket(&packet, m_handshakeOutBuffer);
ba::async_write(*m_socket, ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t)
ba::async_write(m_socket->ref(), ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t)
{
transition(ec);
});
@ -372,7 +193,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
// read frame header
m_handshakeInBuffer.resize(h256::size);
ba::async_read(*m_socket, boost::asio::buffer(m_handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length)
ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length)
{
if (ec)
transition(ec);
@ -406,7 +227,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
/// read padded frame and mac
m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size);
ba::async_read(*m_socket, boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length)
ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, m_handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length)
{
if (ec)
transition(ec);
@ -430,8 +251,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech)
return;
}
// todo: memory management of RLPFrameIO
m_host->startPeerSession(m_remote, rlp, m_socket, m_io.get());
m_host->startPeerSession(m_remote, rlp, m_io.get(), m_socket->remoteEndpoint());
}
});
}

54
libp2p/RLPxHandshake.h

@ -26,6 +26,7 @@
#include <libdevcrypto/Common.h>
#include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h>
#include "RLPxFrameIO.h"
#include "Common.h"
namespace ba = boost::asio;
namespace bi = boost::asio::ip;
@ -35,52 +36,9 @@ namespace dev
namespace p2p
{
class Session;
class RLPXHandshake;
class RLPXFrameIO
{
friend class Session;
public:
RLPXFrameIO(RLPXHandshake const& _init);
void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes);
/// Authenticates and decrypts header in-place.
bool authAndDecryptHeader(h256& io_cipherWithMac);
/// Authenticates and decrypts frame in-place.
bool authAndDecryptFrame(bytesRef io_cipherWithMac);
h128 egressDigest();
h128 ingressDigest();
void updateEgressMACWithHeader(bytesConstRef _headerCipher);
void updateEgressMACWithEndOfFrame(bytesConstRef _cipher);
void updateIngressMACWithHeader(bytesConstRef _headerCipher);
void updateIngressMACWithEndOfFrame(bytesConstRef _cipher);
private:
void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128());
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption m_frameEnc;
CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption m_frameDec;
CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption m_macEnc;
CryptoPP::SHA3_256 m_egressMac;
CryptoPP::SHA3_256 m_ingressMac;
bi::tcp::socket* m_socket;
};
class RLPXHandshake: public std::enable_shared_from_this<RLPXHandshake>
{
public:
friend class RLPXFrameIO;
enum State
{
Error = -1,
@ -91,13 +49,14 @@ public:
StartSession
};
public:
/// Handshake for ingress connection. Takes ownership of socket.
RLPXHandshake(Host* _host, bi::tcp::socket* _socket): m_host(_host), m_socket(std::move(_socket)), m_originated(false) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket): m_host(_host), m_originated(false), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
/// Handshake for egress connection to _remote. Takes ownership of socket.
RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_socket(std::move(_socket)), m_originated(true) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
RLPXHandshake(Host* _host, std::shared_ptr<RLPXSocket> const& _socket, NodeId _remote): m_host(_host), m_remote(_remote), m_originated(true), m_socket(_socket) { crypto::Nonce::get().ref().copyTo(m_nonce.ref()); }
~RLPXHandshake() { delete m_socket; }
~RLPXHandshake() {}
void start() { transition(); }
@ -118,8 +77,6 @@ protected:
/// Node id of remote host for socket.
NodeId m_remote;
bi::tcp::socket* m_socket;
bool m_originated = false;
/// Buffers for encoded and decoded handshake phases
@ -138,6 +95,7 @@ protected:
/// Frame IO is used to read frame for last step of handshake authentication.
std::unique_ptr<RLPXFrameIO> m_io;
std::shared_ptr<RLPXSocket> m_socket;
};
}

4
libp2p/Session.cpp

@ -16,6 +16,7 @@
*/
/** @file Session.cpp
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
@ -39,9 +40,8 @@ using namespace dev::p2p;
Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr<Peer> const& _n, PeerSessionInfo _info):
m_server(_s),
#warning fixme
m_socket(move(*_io.m_socket)),
m_io(move(_io)),
m_socket(m_io.socket()),
m_peer(_n),
m_info(_info),
m_ping(chrono::steady_clock::time_point::max())

3
libp2p/Session.h

@ -16,6 +16,7 @@
*/
/** @file Session.h
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
@ -104,8 +105,8 @@ private:
Host* m_server; ///< The host that owns us. Never null.
mutable bi::tcp::socket m_socket; ///< Socket for the peer's connection. Mutable to ask for native_handle().
RLPXFrameIO m_io; ///< Transport over which packets are sent.
bi::tcp::socket& m_socket; ///< Socket for the peer's connection.
Mutex x_writeQueue; ///< Mutex for the write queue.
std::deque<bytes> m_writeQueue; ///< The write queue.
std::array<byte, 65536> m_data; ///< Buffer for ingress packet data.

Loading…
Cancel
Save