Fabcoin Core  0.16.2
P2P Digital Currency
RLPXFrameCoder.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 "RLPXFrameCoder.h"
23 #include <cryptopp/aes.h>
24 #include <cryptopp/keccak.h>
25 #include <cryptopp/modes.h>
26 #include <libdevcore/Assertions.h>
27 #include <libdevcore/SHA3.h>
28 #include "RLPxHandshake.h"
29 #include "RLPXPacket.h"
30 
31 static_assert(CRYPTOPP_VERSION == 570, "Wrong Crypto++ version");
32 
33 using namespace std;
34 using namespace dev;
35 using namespace dev::p2p;
36 using namespace CryptoPP;
37 
38 RLPXFrameInfo::RLPXFrameInfo(bytesConstRef _header):
39  length((_header[0] * 256 + _header[1]) * 256 + _header[2]),
40  padding((16 - (length % 16)) % 16),
41  data(_header.cropped(3).toBytes()),
42  header(RLP(data, RLP::ThrowOnFail | RLP::FailIfTooSmall)),
43  protocolId(header[0].toInt<uint16_t>()),
44  multiFrame(header.itemCount() > 1),
45  sequenceId(multiFrame ? header[1].toInt<uint16_t>() : 0),
46  totalLength(header.itemCount() == 3 ? header[2].toInt<uint32_t>() : 0)
47 {}
48 
49 namespace dev
50 {
51 namespace p2p
52 {
54 {
55 public:
57  void updateMAC(CryptoPP::Keccak_256& _mac, bytesConstRef _seed = {});
58 
60  CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption frameEnc;
61 
63  CryptoPP::CTR_Mode<CryptoPP::AES>::Encryption frameDec;
64 
66  CryptoPP::ECB_Mode<CryptoPP::AES>::Encryption macEnc;
67 
70 
71 private:
73 };
74 }
75 }
76 
78 {}
79 
81  m_impl(new RLPXFrameCoderImpl)
82 {
83  setup(_init.m_originated, _init.m_remoteEphemeral, _init.m_remoteNonce, _init.m_ecdhe, _init.m_nonce, &_init.m_ackCipher, &_init.m_authCipher);
84 }
85 
86 RLPXFrameCoder::RLPXFrameCoder(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ecdhe, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher):
88 {
89  setup(_originated, _remoteEphemeral, _remoteNonce, _ecdhe, _nonce, _ackCipher, _authCipher);
90 }
91 
92 void RLPXFrameCoder::setup(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ecdhe, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher)
93 {
94  bytes keyMaterialBytes(64);
95  bytesRef keyMaterial(&keyMaterialBytes);
96 
97  // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
98  Secret ephemeralShared;
99  _ecdhe.agree(_remoteEphemeral, ephemeralShared);
100  ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size));
101  h512 nonceMaterial;
102  h256 const& leftNonce = _originated ? _remoteNonce : _nonce;
103  h256 const& rightNonce = _originated ? _nonce : _remoteNonce;
104  leftNonce.ref().copyTo(nonceMaterial.ref().cropped(0, h256::size));
105  rightNonce.ref().copyTo(nonceMaterial.ref().cropped(h256::size, h256::size));
106  auto outRef(keyMaterial.cropped(h256::size, h256::size));
107  sha3(nonceMaterial.ref(), outRef); // output h(nonces)
108 
109  sha3(keyMaterial, outRef); // output shared-secret
110  // token: sha3(outRef, bytesRef(&token)); -> m_host (to be saved to disk)
111 
112  // aes-secret = sha3(ecdhe-shared-secret || shared-secret)
113  sha3(keyMaterial, outRef); // output aes-secret
114  m_impl->frameEncKey.resize(h256::size);
115  memcpy(m_impl->frameEncKey.data(), outRef.data(), h256::size);
116  m_impl->frameDecKey.resize(h256::size);
117  memcpy(m_impl->frameDecKey.data(), outRef.data(), h256::size);
118  h128 iv;
119  m_impl->frameEnc.SetKeyWithIV(m_impl->frameEncKey, h256::size, iv.data());
120  m_impl->frameDec.SetKeyWithIV(m_impl->frameDecKey, h256::size, iv.data());
121 
122  // mac-secret = sha3(ecdhe-shared-secret || aes-secret)
123  sha3(keyMaterial, outRef); // output mac-secret
124  m_impl->macEncKey.resize(h256::size);
125  memcpy(m_impl->macEncKey.data(), outRef.data(), h256::size);
126  m_impl->macEnc.SetKey(m_impl->macEncKey, h256::size);
127 
128  // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init)
129  // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack)
130  // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack)
131  // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init)
132 
133  (*(h256*)outRef.data() ^ _remoteNonce).ref().copyTo(keyMaterial);
134  bytesConstRef egressCipher = _originated ? _authCipher : _ackCipher;
135  keyMaterialBytes.resize(h256::size + egressCipher.size());
136  keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
137  egressCipher.copyTo(keyMaterial.cropped(h256::size, egressCipher.size()));
138  m_impl->egressMac.Update(keyMaterial.data(), keyMaterial.size());
139 
140  // recover mac-secret by re-xoring remoteNonce
141  (*(h256*)keyMaterial.data() ^ _remoteNonce ^ _nonce).ref().copyTo(keyMaterial);
142  bytesConstRef ingressCipher = _originated ? _ackCipher : _authCipher;
143  keyMaterialBytes.resize(h256::size + ingressCipher.size());
144  keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
145  ingressCipher.copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
146  m_impl->ingressMac.Update(keyMaterial.data(), keyMaterial.size());
147 }
148 
149 void RLPXFrameCoder::writeFrame(uint16_t _protocolType, bytesConstRef _payload, bytes& o_bytes)
150 {
151  RLPStream header;
152  uint32_t len = (uint32_t)_payload.size();
153  header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
154  header.appendList(1) << _protocolType;
155  writeFrame(header, _payload, o_bytes);
156 }
157 
158 void RLPXFrameCoder::writeFrame(uint16_t _protocolType, uint16_t _seqId, bytesConstRef _payload, bytes& o_bytes)
159 {
160  RLPStream header;
161  uint32_t len = (uint32_t)_payload.size();
162  header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
163  header.appendList(2) << _protocolType << _seqId;
164  writeFrame(header, _payload, o_bytes);
165 }
166 
167 void RLPXFrameCoder::writeFrame(uint16_t _protocolType, uint16_t _seqId, uint32_t _totalSize, bytesConstRef _payload, bytes& o_bytes)
168 {
169  RLPStream header;
170  uint32_t len = (uint32_t)_payload.size();
171  header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
172  header.appendList(3) << _protocolType << _seqId << _totalSize;
173  writeFrame(header, _payload, o_bytes);
174 }
175 
176 void RLPXFrameCoder::writeFrame(RLPStream const& _header, bytesConstRef _payload, bytes& o_bytes)
177 {
178  // TODO: SECURITY check header values && header <= 16 bytes
179  bytes headerWithMac(h256::size);
180  bytesConstRef(&_header.out()).copyTo(bytesRef(&headerWithMac));
181  m_impl->frameEnc.ProcessData(headerWithMac.data(), headerWithMac.data(), 16);
182  updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16));
183  egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size));
184 
185  auto padding = (16 - (_payload.size() % 16)) % 16;
186  o_bytes.swap(headerWithMac);
187  o_bytes.resize(32 + _payload.size() + padding + h128::size);
188  bytesRef packetRef(o_bytes.data() + 32, _payload.size());
189  m_impl->frameEnc.ProcessData(packetRef.data(), _payload.data(), _payload.size());
190  bytesRef paddingRef(o_bytes.data() + 32 + _payload.size(), padding);
191  if (padding)
192  m_impl->frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding);
193  bytesRef packetWithPaddingRef(o_bytes.data() + 32, _payload.size() + padding);
194  updateEgressMACWithFrame(packetWithPaddingRef);
195  bytesRef macRef(o_bytes.data() + 32 + _payload.size() + padding, h128::size);
196  egressDigest().ref().copyTo(macRef);
197 }
198 
200 {
201  RLPStream header;
202  uint32_t len = (uint32_t)_packet.size();
203  header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
204  header.appendRaw(bytes({0xc2,0x80,0x80}));
205  writeFrame(header, _packet, o_bytes);
206 }
207 
209 {
210  asserts(io.size() == h256::size);
213  h128 expected = ingressDigest();
214  if (*(h128*)macRef.data() != expected)
215  return false;
216  m_impl->frameDec.ProcessData(io.data(), io.data(), h128::size);
217  return true;
218 }
219 
221 {
222  bytesRef cipherText(io.cropped(0, io.size() - h128::size));
223  updateIngressMACWithFrame(cipherText);
224  bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size);
225  if (*(h128*)frameMac.data() != ingressDigest())
226  return false;
227  m_impl->frameDec.ProcessData(io.data(), io.data(), io.size() - h128::size);
228  return true;
229 }
230 
232 {
233  Keccak_256 h(m_impl->egressMac);
234  h128 digest;
235  h.TruncatedFinal(digest.data(), h128::size);
236  return digest;
237 }
238 
240 {
241  Keccak_256 h(m_impl->ingressMac);
242  h128 digest;
243  h.TruncatedFinal(digest.data(), h128::size);
244  return digest;
245 }
246 
248 {
249  m_impl->updateMAC(m_impl->egressMac, _headerCipher.cropped(0, 16));
250 }
251 
253 {
254  m_impl->egressMac.Update(_cipher.data(), _cipher.size());
255  m_impl->updateMAC(m_impl->egressMac);
256 }
257 
259 {
260  m_impl->updateMAC(m_impl->ingressMac, _headerCipher.cropped(0, 16));
261 }
262 
264 {
265  m_impl->ingressMac.Update(_cipher.data(), _cipher.size());
266  m_impl->updateMAC(m_impl->ingressMac);
267 }
268 
270 {
271  if (_seed.size() && _seed.size() != h128::size)
272  asserts(false);
273 
274  Keccak_256 prevDigest(_mac);
275  h128 encDigest(h128::size);
276  prevDigest.TruncatedFinal(encDigest.data(), h128::size);
277  h128 prevDigestOut = encDigest;
278 
279  {
280  Guard l(x_macEnc);
281  macEnc.ProcessData(encDigest.data(), encDigest.data(), 16);
282  }
283  if (_seed.size())
284  encDigest ^= *(h128*)_seed.data();
285  else
286  encDigest ^= *(h128*)prevDigestOut.data();
287 
288  // update mac for final digest
289  _mac.Update(encDigest.data(), h128::size);
290 }
bytes m_ackCipher
Ciphertext of egress or ingress Ack message.
Adapted from code found on http://stackoverflow.com/questions/180947/base64-decode-snippet-in-c Origi...
Definition: Arith256.cpp:15
vector_ref< _T const > ref(_T const &_t)
Definition: vector_ref.h:115
void updateEgressMACWithHeader(bytesConstRef _headerCipher)
Update state of egress MAC with frame header.
byte * data()
Definition: FixedHash.h:139
CryptoPP::SecByteBlock frameDecKey
Key for m_frameDec.
Class file for modes of operation.
void updateIngressMACWithFrame(bytesConstRef _cipher)
Update state of ingress MAC with frame.
h256 m_nonce
Nonce generated by this host for handshake.
#define asserts(A)
Definition: Assertions.h:41
#define h(i)
Definition: sha.cpp:736
bytes const & out() const
Read the byte stream.
Definition: RLP.h:433
Classes for Keccak message digests.
bool authAndDecryptFrame(bytesRef io_cipherWithMac)
Authenticate and decrypt frame in-place.
std::hash for asio::adress
Definition: Common.h:323
void writeFrame(uint16_t _protocolType, bytesConstRef _payload, bytes &o_bytes)
Write single-frame payload of packet(s).
vector_ref< _T > cropped(size_t _begin, size_t _count) const
Definition: vector_ref.h:62
void agree(Public const &_remoteEphemeral, Secret &o_sharedSecret) const
Input public key for dh agreement, output generated shared secret.
Definition: ECDHE.cpp:27
std::unique_ptr< class RLPXFrameCoderImpl > m_impl
CryptoPP::ECB_Mode< CryptoPP::AES >::Encryption macEnc
Key for m_macEnd.
CryptoPP::Keccak_256 ingressMac
State of MAC for ingress ciphertext.
bytes m_authCipher
Ciphertext of egress or ingress Auth message.
void retarget(_T *_d, size_t _s)
Definition: vector_ref.h:65
crypto::ECDHE m_ecdhe
Ephemeral ECDH secret and agreement.
bytesConstRef ref() const
Definition: FixedHash.h:292
vector_ref< byte > bytesRef
Definition: Common.h:76
bytesRef ref()
Definition: FixedHash.h:133
std::lock_guard< std::mutex > Guard
Definition: Guards.h:41
bool authAndDecryptHeader(bytesRef io_cipherWithMac)
Authenticate and decrypt header in-place.
Class file for the AES cipher (Rijndael)
SecBlock< byte > SecByteBlock
Definition: secblock.h:739
CryptoPP::CTR_Mode< CryptoPP::AES >::Encryption frameEnc
Encoder for egress plaintext.
std::vector< byte > bytes
Definition: Common.h:75
vector_ref< byte const > bytesConstRef
Definition: Common.h:77
Public m_remoteEphemeral
Remote ephemeral public key.
RLPStream & appendList(size_t _items)
Appends a list.
Definition: RLP.cpp:276
CryptoPP::Keccak_256 egressMac
One-way coder used by updateMAC for ingress and egress MAC updates.
Keccak_Final< 32 > Keccak_256
Definition: keccak.h:96
size_t size() const
Definition: vector_ref.h:55
void updateEgressMACWithFrame(bytesConstRef _cipher)
Update state of egress MAC with frame.
void updateMAC(CryptoPP::Keccak_256 &_mac, bytesConstRef _seed={})
Update state of _mac.
CryptoPP::CTR_Mode< CryptoPP::AES >::Encryption frameDec
Decoder for egress plaintext.
bool m_originated
True if connection is outbound.
void updateIngressMACWithHeader(bytesConstRef _headerCipher)
Update state of ingress MAC with frame header.
_T * data() const
Definition: vector_ref.h:51
void * memcpy(void *a, const void *b, size_t c)
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
uint8_t byte
Definition: Common.h:10
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 CRYPTOPP_VERSION
Definition: config.h:66
void setup(bool _originated, h512 const &_remoteEphemeral, h256 const &_remoteNonce, crypto::ECDHE const &_ephemeral, h256 const &_nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher)
Establish shared secrets and setup AES and MAC states.
void writeSingleFramePacket(bytesConstRef _packet, bytes &o_bytes)
Legacy. Encrypt _packet as ill-defined legacy RLPx frame.
Keccak-256 message digest.
h128 ingressDigest()
Return first 16 bytes of current digest from ingress mac.
h256 m_remoteNonce
Nonce generated by remote host for handshake.
std::mutex Mutex
Definition: Guards.h:37
h128 egressDigest()
Return first 16 bytes of current digest from egress mac.
CryptoPP::SecByteBlock frameEncKey
Key for m_frameEnc.
CryptoPP::SecByteBlock macEncKey
RLPXFrameCoder(RLPXHandshake const &_init)
Construct; requires instance of RLPXHandshake which has encrypted ECDH key exchange (first two phases...
Class for writing to an RLP bytestream.
Definition: RLP.h:383
Derive DH shared secret from EC keypairs.
Definition: ECDHE.h:37
RLPStream & appendRaw(bytesConstRef _rlp, size_t _itemCount=1)
Appends raw (pre-serialised) RLP data. Use with caution.
Definition: RLP.cpp:230
uint8_t const * data
Definition: sha3.h:19
Class for interpreting Recursive Linear-Prefix Data.
Definition: RLP.h:64
u256 toInt(json_spirit::mValue const &_v)
Definition: TestHelper.cpp:195
Setup inbound or outbound connection for communication over RLPXFrameCoder.
Definition: RLPxHandshake.h:51