Fabcoin Core  0.16.2
P2P Digital Currency
RLPxHandshake.cpp
Go to the documentation of this file.
1 /*
2  This file is part of cpp-ethereum.
3 
4  cpp-ethereum is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  cpp-ethereum is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
16  */
22 #include "Host.h"
23 #include "Session.h"
24 #include "Peer.h"
25 #include "RLPxHandshake.h"
26 using namespace std;
27 using namespace dev;
28 using namespace dev::p2p;
29 using namespace dev::crypto;
30 
31 void RLPXHandshake::writeAuth()
32 {
33  clog(NetP2PConnect) << "p2p.connect.egress sending auth to " << m_socket->remoteEndpoint();
34  m_auth.resize(Signature::size + h256::size + Public::size + h256::size + 1);
35  bytesRef sig(&m_auth[0], Signature::size);
36  bytesRef hepubk(&m_auth[Signature::size], h256::size);
37  bytesRef pubk(&m_auth[Signature::size + h256::size], Public::size);
38  bytesRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size);
39 
40  // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0)
41  Secret staticShared;
42  crypto::ecdh::agree(m_host->m_alias.secret(), m_remote, staticShared);
43  sign(m_ecdhe.seckey(), staticShared.makeInsecure() ^ m_nonce).ref().copyTo(sig);
44  sha3(m_ecdhe.pubkey().ref(), hepubk);
45  m_host->m_alias.pub().ref().copyTo(pubk);
46  m_nonce.ref().copyTo(nonce);
47  m_auth[m_auth.size() - 1] = 0x0;
48  encryptECIES(m_remote, &m_auth, m_authCipher);
49 
50  auto self(shared_from_this());
51  ba::async_write(m_socket->ref(), ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t)
52  {
53  transition(ec);
54  });
55 }
56 
57 void RLPXHandshake::writeAck()
58 {
59  clog(NetP2PConnect) << "p2p.connect.ingress sending ack to " << m_socket->remoteEndpoint();
60  m_ack.resize(Public::size + h256::size + 1);
61  bytesRef epubk(&m_ack[0], Public::size);
62  bytesRef nonce(&m_ack[Public::size], h256::size);
63  m_ecdhe.pubkey().ref().copyTo(epubk);
64  m_nonce.ref().copyTo(nonce);
65  m_ack[m_ack.size() - 1] = 0x0;
66  encryptECIES(m_remote, &m_ack, m_ackCipher);
67 
68  auto self(shared_from_this());
69  ba::async_write(m_socket->ref(), ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t)
70  {
71  transition(ec);
72  });
73 }
74 
75 void RLPXHandshake::writeAckEIP8()
76 {
77  clog(NetP2PConnect) << "p2p.connect.ingress sending EIP-8 ack to " << m_socket->remoteEndpoint();
78 
79  RLPStream rlp;
80  rlp.appendList(3)
81  << m_ecdhe.pubkey()
82  << m_nonce
83  << c_rlpxVersion;
84  m_ack = rlp.out();
85  int padAmount(rand()%100 + 100);
86  m_ack.resize(m_ack.size() + padAmount, 0);
87 
88  bytes prefix(2);
89  toBigEndian<uint16_t>(m_ack.size() + c_eciesOverhead, prefix);
90  encryptECIES(m_remote, bytesConstRef(&prefix), &m_ack, m_ackCipher);
91  m_ackCipher.insert(m_ackCipher.begin(), prefix.begin(), prefix.end());
92 
93  auto self(shared_from_this());
94  ba::async_write(m_socket->ref(), ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t)
95  {
96  transition(ec);
97  });
98 }
99 
100 void RLPXHandshake::setAuthValues(Signature const& _sig, Public const& _remotePubk, h256 const& _remoteNonce, uint64_t _remoteVersion)
101 {
102  _remotePubk.ref().copyTo(m_remote.ref());
103  _remoteNonce.ref().copyTo(m_remoteNonce.ref());
104  m_remoteVersion = _remoteVersion;
105  Secret sharedSecret;
106  crypto::ecdh::agree(m_host->m_alias.secret(), _remotePubk, sharedSecret);
107  m_remoteEphemeral = recover(_sig, sharedSecret.makeInsecure() ^ _remoteNonce);
108 }
109 
110 void RLPXHandshake::readAuth()
111 {
112  clog(NetP2PConnect) << "p2p.connect.ingress receiving auth from " << m_socket->remoteEndpoint();
113  m_authCipher.resize(307);
114  auto self(shared_from_this());
115  ba::async_read(m_socket->ref(), ba::buffer(m_authCipher, 307), [this, self](boost::system::error_code ec, std::size_t)
116  {
117  if (ec)
118  transition(ec);
119  else if (decryptECIES(m_host->m_alias.secret(), bytesConstRef(&m_authCipher), m_auth))
120  {
121  bytesConstRef data(&m_auth);
122  Signature sig(data.cropped(0, Signature::size));
125  setAuthValues(sig, pubk, nonce, 4);
126  transition();
127  }
128  else
129  readAuthEIP8();
130  });
131 }
132 
133 void RLPXHandshake::readAuthEIP8()
134 {
135  assert(m_authCipher.size() == 307);
136  uint16_t size(m_authCipher[0]<<8 | m_authCipher[1]);
137  clog(NetP2PConnect) << "p2p.connect.ingress receiving " << size << "bytes EIP-8 auth from " << m_socket->remoteEndpoint();
138  m_authCipher.resize((size_t)size + 2);
139  auto rest = ba::buffer(ba::buffer(m_authCipher) + 307);
140  auto self(shared_from_this());
141  ba::async_read(m_socket->ref(), rest, [this, self](boost::system::error_code ec, std::size_t)
142  {
143  bytesConstRef ct(&m_authCipher);
144  if (ec)
145  transition(ec);
146  else if (decryptECIES(m_host->m_alias.secret(), ct.cropped(0, 2), ct.cropped(2), m_auth))
147  {
148  RLP rlp(m_auth, RLP::ThrowOnFail | RLP::FailIfTooSmall);
149  setAuthValues(
150  rlp[0].toHash<Signature>(),
151  rlp[1].toHash<Public>(),
152  rlp[2].toHash<h256>(),
153  rlp[3].toInt<uint64_t>()
154  );
155  m_nextState = AckAuthEIP8;
156  transition();
157  }
158  else
159  {
160  clog(NetP2PConnect) << "p2p.connect.ingress auth decrypt failed for" << m_socket->remoteEndpoint();
161  m_nextState = Error;
162  transition();
163  }
164  });
165 }
166 
167 void RLPXHandshake::readAck()
168 {
169  clog(NetP2PConnect) << "p2p.connect.egress receiving ack from " << m_socket->remoteEndpoint();
170  m_ackCipher.resize(210);
171  auto self(shared_from_this());
172  ba::async_read(m_socket->ref(), ba::buffer(m_ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t)
173  {
174  if (ec)
175  transition(ec);
176  else if (decryptECIES(m_host->m_alias.secret(), bytesConstRef(&m_ackCipher), m_ack))
177  {
178  bytesConstRef(&m_ack).cropped(0, Public::size).copyTo(m_remoteEphemeral.ref());
179  bytesConstRef(&m_ack).cropped(Public::size, h256::size).copyTo(m_remoteNonce.ref());
180  m_remoteVersion = 4;
181  transition();
182  }
183  else
184  readAckEIP8();
185  });
186 }
187 
188 void RLPXHandshake::readAckEIP8()
189 {
190  assert(m_ackCipher.size() == 210);
191  uint16_t size(m_ackCipher[0]<<8 | m_ackCipher[1]);
192  clog(NetP2PConnect) << "p2p.connect.egress receiving " << size << "bytes EIP-8 ack from " << m_socket->remoteEndpoint();
193  m_ackCipher.resize((size_t)size + 2);
194  auto rest = ba::buffer(ba::buffer(m_ackCipher) + 210);
195  auto self(shared_from_this());
196  ba::async_read(m_socket->ref(), rest, [this, self](boost::system::error_code ec, std::size_t)
197  {
198  bytesConstRef ct(&m_ackCipher);
199  if (ec)
200  transition(ec);
201  else if (decryptECIES(m_host->m_alias.secret(), ct.cropped(0, 2), ct.cropped(2), m_ack))
202  {
203  RLP rlp(m_ack, RLP::ThrowOnFail | RLP::FailIfTooSmall);
204  m_remoteEphemeral = rlp[0].toHash<Public>();
205  m_remoteNonce = rlp[1].toHash<h256>();
206  m_remoteVersion = rlp[2].toInt<uint64_t>();
207  transition();
208  }
209  else
210  {
211  clog(NetP2PConnect) << "p2p.connect.egress ack decrypt failed for " << m_socket->remoteEndpoint();
212  m_nextState = Error;
213  transition();
214  }
215  });
216 }
217 
218 void RLPXHandshake::cancel()
219 {
220  m_cancel = true;
221  m_idleTimer.cancel();
222  m_socket->close();
223  m_io.reset();
224 }
225 
227 {
228  auto connected = m_socket->isConnected();
229  if (connected && !m_socket->remoteEndpoint().address().is_unspecified())
230  clog(NetP2PConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)";
231  else
232  clog(NetP2PConnect) << "Handshake Failed (Connection reset by peer)";
233 
234  cancel();
235 }
236 
237 void RLPXHandshake::transition(boost::system::error_code _ech)
238 {
239  // reset timeout
240  m_idleTimer.cancel();
241 
242  if (_ech || m_nextState == Error || m_cancel)
243  {
244  clog(NetP2PConnect) << "Handshake Failed (I/O Error:" << _ech.message() << ")";
245  return error();
246  }
247 
248  auto self(shared_from_this());
249  assert(m_nextState != StartSession);
250  m_idleTimer.expires_from_now(c_timeout);
251  m_idleTimer.async_wait([this, self](boost::system::error_code const& _ec)
252  {
253  if (!_ec)
254  {
255  if (!m_socket->remoteEndpoint().address().is_unspecified())
256  clog(NetP2PConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Timeout)";
257  cancel();
258  }
259  });
260 
261  if (m_nextState == New)
262  {
263  m_nextState = AckAuth;
264  if (m_originated)
265  writeAuth();
266  else
267  readAuth();
268  }
269  else if (m_nextState == AckAuth)
270  {
271  m_nextState = WriteHello;
272  if (m_originated)
273  readAck();
274  else
275  writeAck();
276  }
277  else if (m_nextState == AckAuthEIP8)
278  {
279  m_nextState = WriteHello;
280  if (m_originated)
281  readAck();
282  else
283  writeAckEIP8();
284  }
285  else if (m_nextState == WriteHello)
286  {
287  m_nextState = ReadHello;
288  clog(NetP2PConnect) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "sending capabilities handshake";
289 
292  m_io.reset(new RLPXFrameCoder(*this));
293 
294  RLPStream s;
295  s.append((unsigned)HelloPacket).appendList(5)
297  << m_host->m_clientVersion
298  << m_host->caps()
299  << m_host->listenPort()
300  << m_host->id();
301 
302  bytes packet;
303  s.swapOut(packet);
304  m_io->writeSingleFramePacket(&packet, m_handshakeOutBuffer);
305  ba::async_write(m_socket->ref(), ba::buffer(m_handshakeOutBuffer), [this, self](boost::system::error_code ec, std::size_t)
306  {
307  transition(ec);
308  });
309  }
310  else if (m_nextState == ReadHello)
311  {
312  // Authenticate and decrypt initial hello frame with initial RLPXFrameCoder
313  // and request m_host to start session.
314  m_nextState = StartSession;
315 
316  // read frame header
317  unsigned const handshakeSize = 32;
318  m_handshakeInBuffer.resize(handshakeSize);
319  ba::async_read(m_socket->ref(), boost::asio::buffer(m_handshakeInBuffer, handshakeSize), [this, self](boost::system::error_code ec, std::size_t)
320  {
321  if (ec)
322  transition(ec);
323  else
324  {
325  if (!m_io)
326  {
327  clog(NetP2PWarn) << "Internal error in handshake: RLPXFrameCoder disappeared.";
328  m_nextState = Error;
329  transition();
330  return;
331 
332  }
334  if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), m_handshakeInBuffer.size())))
335  {
336  m_nextState = Error;
337  transition();
338  return;
339  }
340 
341  clog(NetP2PNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header";
342 
344  bytes& header = m_handshakeInBuffer;
345  uint32_t frameSize = (uint32_t)(header[2]) | (uint32_t)(header[1])<<8 | (uint32_t)(header[0])<<16;
346  if (frameSize > 1024)
347  {
348  // all future frames: 16777216
349  clog(NetP2PWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large" << frameSize;
350  m_nextState = Error;
351  transition();
352  return;
353  }
354 
356  bytes headerRLP(header.size() - 3 - h128::size); // this is always 32 - 3 - 16 = 13. wtf?
357  bytesConstRef(&header).cropped(3).copyTo(&headerRLP);
358 
360  m_handshakeInBuffer.resize(frameSize + ((16 - (frameSize % 16)) % 16) + h128::size);
361  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)
362  {
363  m_idleTimer.cancel();
364 
365  if (ec)
366  transition(ec);
367  else
368  {
369  if (!m_io)
370  {
371  clog(NetP2PWarn) << "Internal error in handshake: RLPXFrameCoder disappeared.";
372  m_nextState = Error;
373  transition();
374  return;
375 
376  }
377  bytesRef frame(&m_handshakeInBuffer);
378  if (!m_io->authAndDecryptFrame(frame))
379  {
380  clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed";
381  m_nextState = Error;
382  transition();
383  return;
384  }
385 
386  PacketType packetType = frame[0] == 0x80 ? HelloPacket : (PacketType)frame[0];
387  if (packetType != HelloPacket)
388  {
389  clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type";
390  m_nextState = Error;
391  transition();
392  return;
393  }
394 
395  clog(NetTriviaSummary) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session.";
396  try
397  {
398  RLP rlp(frame.cropped(1), RLP::ThrowOnFail | RLP::FailIfTooSmall);
399  m_host->startPeerSession(m_remote, rlp, move(m_io), m_socket);
400  }
401  catch (std::exception const& _e)
402  {
403  clog(NetWarn) << "Handshake causing an exception:" << _e.what();
404  m_nextState = Error;
405  transition();
406  }
407  }
408  });
409  }
410  });
411  }
412 }
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
Definition: Arith256.cpp:15
bool error(const char *fmt, const Args &...args)
Definition: util.h:178
A modifiable reference to an existing object or vector in memory.
Definition: vector_ref.h:20
vector_ref< _T const > ref(_T const &_t)
Definition: vector_ref.h:115
bytes rlp(_T _t)
Export a single item in RLP format, returning a byte array.
Definition: RLP.h:467
RLPStream & append(unsigned _s)
Append given datum to the byte stream.
Definition: RLP.h:395
bytes const & out() const
Read the byte stream.
Definition: RLP.h:433
PacketType
Definition: Common.h:95
bool decryptECIES(Secret const &_k, bytesConstRef _cipher, bytes &o_plaintext)
Decrypt payload using ECIES standard with AES128-CTR.
Definition: Common.cpp:131
const char * prefix
Definition: rest.cpp:623
std::hash for asio::adress
Definition: Common.h:323
assert(len-trim+(2 *lenIndices)<=WIDTH)
void encryptECIES(Public const &_k, bytesConstRef _plain, bytes &o_cipher)
Encrypt payload using ECIES standard with AES128-CTR.
Definition: Common.cpp:119
const unsigned c_protocolVersion
Peer network protocol version.
Definition: Common.cpp:28
vector_ref< _T > cropped(size_t _begin, size_t _count) const
Definition: vector_ref.h:62
_N toHash(int _flags=Strict) const
Definition: RLP.h:298
bytesRef ref()
Definition: FixedHash.h:133
FixedHash< T > const & makeInsecure() const
Definition: FixedHash.h:253
Public recover(Signature const &_sig, h256 const &_hash)
Recovers Public key from signed message hash.
Definition: Common.cpp:203
std::vector< byte > bytes
Definition: Common.h:75
vector_ref< byte const > bytesConstRef
Definition: Common.h:77
Fixed-size raw-byte array container type, with an API optimised for storing hashes.
Definition: FixedHash.h:47
RLPStream & appendList(size_t _items)
Appends a list.
Definition: RLP.cpp:276
Signature sign(Secret const &_k, h256 const &_hash)
Returns siganture of message hash.
Definition: Common.cpp:233
uint8_t const size_t const size
Definition: sha3.h:20
void copyTo(vector_ref< typename std::remove_const< _T >::type > _t) const
Copies the contents of this vector_ref to the contents of _t, up to the max size of _t...
Definition: vector_ref.h:69
void agree(Secret const &_s, Public const &_r, Secret &o_s)
Definition: Common.cpp:348
bool sha3(bytesConstRef _input, bytesRef o_output)
Calculate SHA3-256 hash of the given input and load it into the given output.
Definition: SHA3.cpp:214
#define clog(X)
Definition: Log.h:295
void swapOut(bytes &_dest)
Swap the contents of the output stream out for some other byte array.
Definition: RLP.h:439
_T toInt(int _flags=Strict) const
Converts to int of type given; if isString(), decodes as big-endian bytestream.
Definition: RLP.h:275
Class for writing to an RLP bytestream.
Definition: RLP.h:383
Encoder/decoder transport for RLPx connection established by RLPXHandshake.
uint8_t const * data
Definition: sha3.h:19
Class for interpreting Recursive Linear-Prefix Data.
Definition: RLP.h:64