From a0aaf614f37ecc3acc4252c1e4c06e48e5ddaf88 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 03:14:38 +0100 Subject: [PATCH] sharedptr wrapper class for socket --- libp2p/Host.cpp | 32 ++---- libp2p/Host.h | 6 +- libp2p/RLPxFrameIO.cpp | 206 +++++++++++++++++++++++++++++++++++++ libp2p/RLPxFrameIO.h | 97 ++++++++++++++++++ libp2p/RLPxHandshake.cpp | 216 ++++----------------------------------- libp2p/RLPxHandshake.h | 60 ++--------- libp2p/Session.cpp | 4 +- libp2p/Session.h | 3 +- 8 files changed, 348 insertions(+), 276 deletions(-) create mode 100644 libp2p/RLPxFrameIO.cpp create mode 100644 libp2p/RLPxFrameIO.h diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index ef2f95180..057453d8f 100644 --- a/libp2p/Host.cpp +++ b/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 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(); 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(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, move(*_io), p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); 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(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(this, s); + auto handshake = make_shared(this, socket); handshake->start(); success = true; } @@ -392,18 +392,9 @@ 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) runAcceptor(); @@ -485,20 +476,19 @@ void Host::connect(std::shared_ptr 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(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(this, s, _p->id); + auto handshake = make_shared(this, socket, _p->id); handshake->start(); } Guard l(x_pendingNodeConns); diff --git a/libp2p/Host.h b/libp2p/Host.h index a5091ae7e..55f4d61b0 100644 --- a/libp2p/Host.h +++ b/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); diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp new file mode 100644 index 000000000..3ddaf9143 --- /dev/null +++ b/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 . + */ +/** @file RLPXFrameIO.cpp + * @author Alex Leverington + * @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); +} diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h new file mode 100644 index 000000000..07211d4de --- /dev/null +++ b/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 . + */ +/** @file RLPXFrameIO.h + * @author Alex Leverington + * @date 2015 + */ + + +#pragma once + +#include +#include +#include +#include +#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 +{ +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::Encryption m_frameEnc; + CryptoPP::CTR_Mode::Encryption m_frameDec; + CryptoPP::ECB_Mode::Encryption m_macEnc; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + std::shared_ptr m_socket; +}; + +} +} \ No newline at end of file diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 8bec9b6dd..bca1afd61 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/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,11 +131,8 @@ 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()) - m_socket->close(); + clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; + m_socket->close(); } void RLPXHandshake::transition(boost::system::error_code _ech) @@ -344,7 +165,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - + m_io.reset(new RLPXFrameIO(*this)); // old packet format @@ -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); @@ -429,9 +250,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) transition(); 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()); } }); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index a50ec2718..12abe4379 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -26,6 +26,7 @@ #include #include #include +#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::Encryption m_frameEnc; - CryptoPP::CTR_Mode::Encryption m_frameDec; - CryptoPP::ECB_Mode::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 { -public: friend class RLPXFrameIO; - enum State { Error = -1, @@ -90,17 +48,18 @@ public: ReadHello, 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 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() { delete m_socket; } + RLPXHandshake(Host* _host, std::shared_ptr 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() {} void start() { transition(); } - + protected: void writeAuth(); void readAuth(); @@ -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 m_io; + std::shared_ptr m_socket; }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index e11e70e35..f44fa2d6e 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -16,6 +16,7 @@ */ /** @file Session.cpp * @author Gav Wood + * @author Alex Leverington * @date 2014 */ @@ -39,9 +40,8 @@ using namespace dev::p2p; Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr 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()) diff --git a/libp2p/Session.h b/libp2p/Session.h index 5767b7756..89ef5fd74 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -16,6 +16,7 @@ */ /** @file Session.h * @author Gav Wood + * @author Alex Leverington * @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 m_writeQueue; ///< The write queue. std::array m_data; ///< Buffer for ingress packet data.