|
|
@ -28,7 +28,7 @@ using namespace dev; |
|
|
|
using namespace dev::p2p; |
|
|
|
using namespace CryptoPP; |
|
|
|
|
|
|
|
RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() |
|
|
|
RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) |
|
|
|
{ |
|
|
|
// we need:
|
|
|
|
// originated?
|
|
|
@ -36,7 +36,7 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() |
|
|
|
// authCipher
|
|
|
|
// ackCipher
|
|
|
|
|
|
|
|
bytes keyMaterialBytes(512); |
|
|
|
bytes keyMaterialBytes(64); |
|
|
|
bytesRef keyMaterial(&keyMaterialBytes); |
|
|
|
|
|
|
|
// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
|
|
|
@ -44,23 +44,24 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() |
|
|
|
_init.ecdhe.agree(_init.remoteEphemeral, ephemeralShared); |
|
|
|
ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); |
|
|
|
h512 nonceMaterial; |
|
|
|
h256& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; |
|
|
|
h256& rightNonce = _init.originated ? _init.nonce : _init.remoteNonce; |
|
|
|
h256 const& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; |
|
|
|
h256 const& rightNonce = _init.originated ? _init.nonce : _init.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)
|
|
|
|
// token: sha3(outRef, bytesRef(&token)); -> Host (to be saved to disk)
|
|
|
|
|
|
|
|
// aes-secret = sha3(ecdhe-shared-secret || shared-secret)
|
|
|
|
sha3(keyMaterial, outRef); // output aes-secret
|
|
|
|
m_frameEnc.SetKey(outRef.data(), h256::size); |
|
|
|
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(), h256::size); |
|
|
|
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)
|
|
|
@ -75,7 +76,7 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() |
|
|
|
m_egressMac.Update(keyMaterial.data(), keyMaterial.size()); |
|
|
|
|
|
|
|
// recover mac-secret by re-xoring remoteNonce
|
|
|
|
(*(h256*)outRef.data() ^ _init.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); |
|
|
|
(*(h256*)keyMaterial.data() ^ _init.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); |
|
|
|
bytes const& ingressCipher = _init.originated ? _init.ackCipher : _init.authCipher; |
|
|
|
keyMaterialBytes.resize(h256::size + ingressCipher.size()); |
|
|
|
keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); |
|
|
@ -83,25 +84,60 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() |
|
|
|
m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); |
|
|
|
} |
|
|
|
|
|
|
|
void RLPXFrameIO::writeFullPacketFrame(bytesConstRef _packet) |
|
|
|
{ |
|
|
|
RLPStream header; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
void RLPXFrameIO::writeHeader(bi::tcp::socket* _socket, h128 const& _header) |
|
|
|
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); |
|
|
|
} |
|
|
|
|
|
|
|
void RLPXFrameIO::write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof) |
|
|
|
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::read(bytesConstRef _in, bytes& o_out) |
|
|
|
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() |
|
|
@ -120,9 +156,8 @@ h128 RLPXFrameIO::ingressDigest() |
|
|
|
return move(digest); |
|
|
|
} |
|
|
|
|
|
|
|
void RLPXFrameIO::updateEgressMACWithHeader(h128 const& _headerCipher) |
|
|
|
void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) |
|
|
|
{ |
|
|
|
m_egressMac.Update(_headerCipher.data(), h128::size); |
|
|
|
updateMAC(m_egressMac, *(h128*)_headerCipher.data()); |
|
|
|
} |
|
|
|
|
|
|
@ -130,11 +165,16 @@ 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) |
|
|
|
{ |
|
|
|
m_ingressMac.Update(_headerCipher.data(), h128::size); |
|
|
|
updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); |
|
|
|
} |
|
|
|
|
|
|
@ -142,6 +182,12 @@ 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) |
|
|
@ -155,12 +201,13 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) |
|
|
|
encDigest ^= (!!_seed ? _seed : prevDigestOut); |
|
|
|
|
|
|
|
// update mac for final digest
|
|
|
|
_mac.Update(encDigest.data(), h256::size); |
|
|
|
_mac.Update(encDigest.data(), h128::size); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void RLPXHandshake::generateAuth() |
|
|
|
void RLPXHandshake::writeAuth() |
|
|
|
{ |
|
|
|
clog(NetConnect) << "p2p.connect.egress sending auth to " << socket->remote_endpoint(); |
|
|
|
auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); |
|
|
|
bytesRef sig(&auth[0], Signature::size); |
|
|
|
bytesRef hepubk(&auth[Signature::size], h256::size); |
|
|
@ -168,211 +215,227 @@ void RLPXHandshake::generateAuth() |
|
|
|
bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); |
|
|
|
|
|
|
|
// E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
|
|
|
|
//crypto::ecdh::agree(host->m_alias.sec(), remote, ss);
|
|
|
|
//sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig);
|
|
|
|
Secret staticShared; |
|
|
|
crypto::ecdh::agree(host->m_alias.sec(), remote, staticShared); |
|
|
|
sign(ecdhe.seckey(), staticShared ^ this->nonce).ref().copyTo(sig); |
|
|
|
sha3(ecdhe.pubkey().ref(), hepubk); |
|
|
|
host->m_alias.pub().ref().copyTo(pubk); |
|
|
|
this->nonce.ref().copyTo(nonce); |
|
|
|
auth[auth.size() - 1] = 0x0; |
|
|
|
encryptECIES(remote, &auth, authCipher); |
|
|
|
|
|
|
|
auto self(shared_from_this()); |
|
|
|
ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
transition(ec); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
void RLPXHandshake::generateAck() |
|
|
|
void RLPXHandshake::writeAck() |
|
|
|
{ |
|
|
|
clog(NetConnect) << "p2p.connect.ingress sending ack to " << socket->remote_endpoint(); |
|
|
|
ack.resize(Public::size + h256::size + 1); |
|
|
|
bytesRef epubk(&ack[0], Public::size); |
|
|
|
bytesRef nonce(&ack[Public::size], h256::size); |
|
|
|
ecdhe.pubkey().ref().copyTo(epubk); |
|
|
|
this->nonce.ref().copyTo(nonce); |
|
|
|
ack[ack.size() - 1] = 0x0; |
|
|
|
encryptECIES(remote, &ack, ackCipher); |
|
|
|
|
|
|
|
auto self(shared_from_this()); |
|
|
|
ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
transition(ec); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
bool RLPXHandshake::decodeAuth() |
|
|
|
void RLPXHandshake::readAuth() |
|
|
|
{ |
|
|
|
if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) |
|
|
|
return false; |
|
|
|
|
|
|
|
bytesConstRef sig(&auth[0], Signature::size); |
|
|
|
bytesConstRef hepubk(&auth[Signature::size], h256::size); |
|
|
|
bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); |
|
|
|
bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); |
|
|
|
pubk.copyTo(remote.ref()); |
|
|
|
nonce.copyTo(remoteNonce.ref()); |
|
|
|
|
|
|
|
//crypto::ecdh::agree(host->m_alias.sec(), remote, ss);
|
|
|
|
//remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce);
|
|
|
|
assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); |
|
|
|
return true; |
|
|
|
clog(NetConnect) << "p2p.connect.ingress recving auth from " << socket->remote_endpoint(); |
|
|
|
authCipher.resize(307); |
|
|
|
auto self(shared_from_this()); |
|
|
|
ba::async_read(*socket, ba::buffer(authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
if (ec) |
|
|
|
transition(ec); |
|
|
|
else if (decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) |
|
|
|
{ |
|
|
|
bytesConstRef sig(&auth[0], Signature::size); |
|
|
|
bytesConstRef hepubk(&auth[Signature::size], h256::size); |
|
|
|
bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); |
|
|
|
bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); |
|
|
|
pubk.copyTo(remote.ref()); |
|
|
|
nonce.copyTo(remoteNonce.ref()); |
|
|
|
|
|
|
|
Secret sharedSecret; |
|
|
|
crypto::ecdh::agree(host->m_alias.sec(), remote, sharedSecret); |
|
|
|
remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ remoteNonce); |
|
|
|
assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); |
|
|
|
transition(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << socket->remote_endpoint(); |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
bool RLPXHandshake::decodeAck() |
|
|
|
void RLPXHandshake::readAck() |
|
|
|
{ |
|
|
|
|
|
|
|
clog(NetConnect) << "p2p.connect.egress recving ack from " << socket->remote_endpoint(); |
|
|
|
ackCipher.resize(210); |
|
|
|
auto self(shared_from_this()); |
|
|
|
ba::async_read(*socket, ba::buffer(ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
if (ec) |
|
|
|
transition(ec); |
|
|
|
else if (decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) |
|
|
|
{ |
|
|
|
bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); |
|
|
|
bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); |
|
|
|
transition(); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << socket->remote_endpoint(); |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/// used for protocol handshake
|
|
|
|
bytes RLPXHandshake::frame(bytesConstRef _packet) |
|
|
|
void RLPXHandshake::error() |
|
|
|
{ |
|
|
|
|
|
|
|
clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; |
|
|
|
boost::system::error_code ec; |
|
|
|
socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); |
|
|
|
if (socket->is_open()) |
|
|
|
socket->close(); |
|
|
|
} |
|
|
|
|
|
|
|
void RLPXHandshake::transition(boost::system::error_code _ech) |
|
|
|
{ |
|
|
|
if (_ech || nextState == Error) |
|
|
|
{ |
|
|
|
clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; |
|
|
|
boost::system::error_code ec; |
|
|
|
socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); |
|
|
|
if (socket->is_open()) |
|
|
|
socket->close(); |
|
|
|
return; |
|
|
|
} |
|
|
|
return error(); |
|
|
|
|
|
|
|
auto self(shared_from_this()); |
|
|
|
if (nextState == New) |
|
|
|
{ |
|
|
|
nextState = AckAuth; |
|
|
|
|
|
|
|
clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); |
|
|
|
if (originated) |
|
|
|
writeAuth(); |
|
|
|
else |
|
|
|
readAuth(); |
|
|
|
} |
|
|
|
else if (nextState == AckAuth) |
|
|
|
{ |
|
|
|
nextState = WriteHello; |
|
|
|
if (originated) |
|
|
|
readAck(); |
|
|
|
else |
|
|
|
writeAck(); |
|
|
|
} |
|
|
|
else if (nextState == WriteHello) |
|
|
|
{ |
|
|
|
nextState = ReadHello; |
|
|
|
|
|
|
|
if (originated) |
|
|
|
{ |
|
|
|
clog(NetConnect) << "p2p.connect.egress sending auth"; |
|
|
|
generateAuth(); |
|
|
|
ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
transition(ec); |
|
|
|
}); |
|
|
|
} |
|
|
|
clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; |
|
|
|
else |
|
|
|
clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; |
|
|
|
|
|
|
|
io = new RLPXFrameIO(*this); |
|
|
|
|
|
|
|
// old packet format
|
|
|
|
// 5 arguments, HelloPacket
|
|
|
|
RLPStream s; |
|
|
|
s.appendList(5 + 1).append((unsigned)0) |
|
|
|
<< host->protocolVersion() |
|
|
|
<< host->m_clientVersion |
|
|
|
<< host->caps() |
|
|
|
<< host->m_tcpPublic.port() |
|
|
|
<< host->id(); |
|
|
|
bytes packet; |
|
|
|
s.swapOut(packet); |
|
|
|
io->writeSingleFramePacket(&packet, handshakeOutBuffer); |
|
|
|
ba::async_write(*socket, ba::buffer(handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
transition(ec); |
|
|
|
}); |
|
|
|
} |
|
|
|
else if (nextState == ReadHello) |
|
|
|
{ |
|
|
|
// Authenticate and decrypt initial hello frame with initial RLPXFrameIO
|
|
|
|
// and request host to start session.
|
|
|
|
nextState = StartSession; |
|
|
|
|
|
|
|
// read frame header
|
|
|
|
handshakeInBuffer.resize(h256::size); |
|
|
|
ba::async_read(*socket, boost::asio::buffer(handshakeInBuffer, h256::size), [this,self](boost::system::error_code ec, std::size_t length) |
|
|
|
{ |
|
|
|
clog(NetConnect) << "p2p.connect.ingress recving auth"; |
|
|
|
authCipher.resize(321); |
|
|
|
ba::async_read(*socket, ba::buffer(authCipher, 321), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
if (ec) |
|
|
|
transition(ec); |
|
|
|
else |
|
|
|
{ |
|
|
|
if (ec) |
|
|
|
transition(ec); |
|
|
|
else if (decodeAuth()) |
|
|
|
transition(); |
|
|
|
else |
|
|
|
/// authenticate and decrypt header
|
|
|
|
if (!io->authAndDecryptHeader(*(h256*)handshakeInBuffer.data())) |
|
|
|
{ |
|
|
|
clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed"; |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
return; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (nextState == AckAuth) |
|
|
|
{ |
|
|
|
nextState = Authenticating; |
|
|
|
|
|
|
|
if (originated) |
|
|
|
{ |
|
|
|
clog(NetConnect) << "p2p.connect.egress recving ack"; |
|
|
|
// egress: rx ack
|
|
|
|
ackCipher.resize(225); |
|
|
|
ba::async_read(*socket, ba::buffer(ackCipher, 225), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
if (ec) |
|
|
|
transition(ec); |
|
|
|
else |
|
|
|
{ |
|
|
|
if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) |
|
|
|
{ |
|
|
|
clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed"; |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
return; |
|
|
|
} |
|
|
|
clog(NetNote) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; |
|
|
|
|
|
|
|
bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); |
|
|
|
bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); |
|
|
|
/// check frame size
|
|
|
|
bytes& header = handshakeInBuffer; |
|
|
|
uint32_t frameSize = (uint32_t)(header[2]) | (uint32_t)(header[1])<<8 | (uint32_t)(header[0])<<16; |
|
|
|
if (frameSize > 1024) |
|
|
|
{ |
|
|
|
// all future frames: 16777216
|
|
|
|
clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
return; |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
else |
|
|
|
{ |
|
|
|
clog(NetConnect) << "p2p.connect.ingress sending ack"; |
|
|
|
// ingress: tx ack
|
|
|
|
ack.resize(Public::size + h256::size + 1); |
|
|
|
bytesRef epubk(&ack[0], Public::size); |
|
|
|
bytesRef nonce(&ack[Public::size], h256::size); |
|
|
|
ecdhe.pubkey().ref().copyTo(epubk); |
|
|
|
this->nonce.ref().copyTo(nonce); |
|
|
|
ack[ack.size() - 1] = 0x0; |
|
|
|
encryptECIES(remote, &ack, ackCipher); |
|
|
|
ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) |
|
|
|
{ |
|
|
|
transition(ec); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
else if (nextState == Authenticating) |
|
|
|
{ |
|
|
|
if (originated) |
|
|
|
clog(NetConnect) << "p2p.connect.egress sending magic sequence"; |
|
|
|
else |
|
|
|
clog(NetConnect) << "p2p.connect.ingress sending magic sequence"; |
|
|
|
|
|
|
|
RLPXFrameIO* io = new RLPXFrameIO(*this); |
|
|
|
|
|
|
|
// // This test will be replaced with protocol-capabilities information (was Hello packet)
|
|
|
|
// // TESTING: send encrypt magic sequence
|
|
|
|
// bytes magic {0x22,0x40,0x08,0x91};
|
|
|
|
// // rlpx encrypt
|
|
|
|
// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h128());
|
|
|
|
// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32);
|
|
|
|
// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref());
|
|
|
|
// k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32));
|
|
|
|
//
|
|
|
|
// clog(NetConnect) << "p2p.connect.egress txrx magic sequence";
|
|
|
|
// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size());
|
|
|
|
//
|
|
|
|
// ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t)
|
|
|
|
// {
|
|
|
|
// if (ec)
|
|
|
|
// {
|
|
|
|
// delete k;
|
|
|
|
// transition(ec);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t)
|
|
|
|
// {
|
|
|
|
// if (originated)
|
|
|
|
// clog(NetNote) << "p2p.connect.egress recving magic sequence";
|
|
|
|
// else
|
|
|
|
// clog(NetNote) << "p2p.connect.ingress recving magic sequence";
|
|
|
|
//
|
|
|
|
// if (ec)
|
|
|
|
// {
|
|
|
|
// delete k;
|
|
|
|
// transition(ec);
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// /// capabilities handshake (encrypted magic sequence is placeholder)
|
|
|
|
// bytes decryptedMagic;
|
|
|
|
// decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic);
|
|
|
|
// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91)
|
|
|
|
// {
|
|
|
|
shared_ptr<Peer> p; |
|
|
|
p = host->m_peers[remote]; |
|
|
|
if (!p) |
|
|
|
|
|
|
|
/// rlp of header has protocol-type, sequence-id[, total-packet-size]
|
|
|
|
bytes headerRLP(header.size() - 3 - h128::size); |
|
|
|
bytesConstRef(&header).cropped(3).copyTo(&headerRLP); |
|
|
|
|
|
|
|
/// read padded frame and mac
|
|
|
|
handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size); |
|
|
|
ba::async_read(*socket, boost::asio::buffer(handshakeInBuffer, handshakeInBuffer.size()), [this, self, headerRLP](boost::system::error_code ec, std::size_t length) |
|
|
|
{ |
|
|
|
if (ec) |
|
|
|
transition(ec); |
|
|
|
else |
|
|
|
{ |
|
|
|
p.reset(new Peer()); |
|
|
|
p->id = remote; |
|
|
|
if (!io->authAndDecryptFrame(bytesRef(&handshakeInBuffer))) |
|
|
|
{ |
|
|
|
clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
RLP rlp(handshakeInBuffer); |
|
|
|
auto packetType = (PacketType)rlp[0].toInt<unsigned>(); |
|
|
|
if (packetType != 0) |
|
|
|
{ |
|
|
|
clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; |
|
|
|
nextState = Error; |
|
|
|
transition(); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// todo: handover RLPFrameIO
|
|
|
|
host->startPeerSession(remote, rlp, socket); |
|
|
|
} |
|
|
|
p->endpoint.tcp.address(socket->remote_endpoint().address()); |
|
|
|
p->m_lastDisconnect = NoDisconnect; |
|
|
|
p->m_lastConnected = std::chrono::system_clock::now(); |
|
|
|
p->m_failedAttempts = 0; |
|
|
|
|
|
|
|
auto ps = std::make_shared<Session>(host, move(*socket), p); |
|
|
|
ps->start(); |
|
|
|
// }
|
|
|
|
|
|
|
|
// todo: PeerSession will take ownership of k and use it to encrypt wireline.
|
|
|
|
delete io; |
|
|
|
// });
|
|
|
|
// });
|
|
|
|
}); |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|