123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- /*
- 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 RLPXFrameCoder.cpp
- * @author Alex Leverington <nessence@gmail.com>
- * @date 2015
- */
- #include "RLPXFrameCoder.h"
- #include <libdevcore/Assertions.h>
- #include "RLPxHandshake.h"
- #include "RLPXPacket.h"
- using namespace std;
- using namespace dev;
- using namespace dev::p2p;
- using namespace CryptoPP;
- RLPXFrameInfo::RLPXFrameInfo(bytesConstRef _header):
- length((_header[0] * 256 + _header[1]) * 256 + _header[2]),
- padding((16 - (length % 16)) % 16),
- data(_header.cropped(3).toBytes()),
- header(RLP(data, RLP::ThrowOnFail | RLP::FailIfTooSmall)),
- protocolId(header[0].toInt<uint16_t>()),
- multiFrame(header.itemCount() > 1),
- sequenceId(multiFrame ? header[1].toInt<uint16_t>() : 0),
- totalLength(header.itemCount() == 3 ? header[2].toInt<uint32_t>() : 0)
- {}
- RLPXFrameCoder::RLPXFrameCoder(RLPXHandshake const& _init)
- {
- setup(_init.m_originated, _init.m_remoteEphemeral, _init.m_remoteNonce, _init.m_ecdhe, _init.m_nonce, &_init.m_ackCipher, &_init.m_authCipher);
- }
- RLPXFrameCoder::RLPXFrameCoder(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ecdhe, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher)
- {
- setup(_originated, _remoteEphemeral, _remoteNonce, _ecdhe, _nonce, _ackCipher, _authCipher);
- }
- void RLPXFrameCoder::setup(bool _originated, h512 const& _remoteEphemeral, h256 const& _remoteNonce, crypto::ECDHE const& _ecdhe, h256 const& _nonce, bytesConstRef _ackCipher, bytesConstRef _authCipher)
- {
- bytes keyMaterialBytes(64);
- bytesRef keyMaterial(&keyMaterialBytes);
- // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce))
- Secret ephemeralShared;
- _ecdhe.agree(_remoteEphemeral, ephemeralShared);
- ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size));
- h512 nonceMaterial;
- h256 const& leftNonce = _originated ? _remoteNonce : _nonce;
- h256 const& rightNonce = _originated ? _nonce : _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_frameEncKey.resize(h256::size);
- memcpy(m_frameEncKey.data(), outRef.data(), h256::size);
- m_frameDecKey.resize(h256::size);
- memcpy(m_frameDecKey.data(), outRef.data(), h256::size);
- h128 iv;
- m_frameEnc.SetKeyWithIV(m_frameEncKey, h256::size, iv.data());
- m_frameDec.SetKeyWithIV(m_frameDecKey, h256::size, iv.data());
- // mac-secret = sha3(ecdhe-shared-secret || aes-secret)
- sha3(keyMaterial, outRef); // output mac-secret
- m_macEncKey.resize(h256::size);
- memcpy(m_macEncKey.data(), outRef.data(), h256::size);
- m_macEnc.SetKey(m_macEncKey, h256::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() ^ _remoteNonce).ref().copyTo(keyMaterial);
- bytesConstRef egressCipher = _originated ? _authCipher : _ackCipher;
- keyMaterialBytes.resize(h256::size + egressCipher.size());
- keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
- 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() ^ _remoteNonce ^ _nonce).ref().copyTo(keyMaterial);
- bytesConstRef ingressCipher = _originated ? _ackCipher : _authCipher;
- keyMaterialBytes.resize(h256::size + ingressCipher.size());
- keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size());
- ingressCipher.copyTo(keyMaterial.cropped(h256::size, ingressCipher.size()));
- m_ingressMac.Update(keyMaterial.data(), keyMaterial.size());
- }
- void RLPXFrameCoder::writeFrame(uint16_t _protocolType, bytesConstRef _payload, bytes& o_bytes)
- {
- RLPStream header;
- uint32_t len = (uint32_t)_payload.size();
- header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
- header.appendList(1) << _protocolType;
- writeFrame(header, _payload, o_bytes);
- }
- void RLPXFrameCoder::writeFrame(uint16_t _protocolType, uint16_t _seqId, bytesConstRef _payload, bytes& o_bytes)
- {
- RLPStream header;
- uint32_t len = (uint32_t)_payload.size();
- header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
- header.appendList(2) << _protocolType << _seqId;
- writeFrame(header, _payload, o_bytes);
- }
- void RLPXFrameCoder::writeFrame(uint16_t _protocolType, uint16_t _seqId, uint32_t _totalSize, bytesConstRef _payload, bytes& o_bytes)
- {
- RLPStream header;
- uint32_t len = (uint32_t)_payload.size();
- header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
- header.appendList(3) << _protocolType << _seqId << _totalSize;
- writeFrame(header, _payload, o_bytes);
- }
- void RLPXFrameCoder::writeFrame(RLPStream const& _header, bytesConstRef _payload, bytes& o_bytes)
- {
- // TODO: SECURITY check header values && header <= 16 bytes
- bytes headerWithMac(h256::size);
- bytesConstRef(&_header.out()).copyTo(bytesRef(&headerWithMac));
- 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 - (_payload.size() % 16)) % 16;
- o_bytes.swap(headerWithMac);
- o_bytes.resize(32 + _payload.size() + padding + h128::size);
- bytesRef packetRef(o_bytes.data() + 32, _payload.size());
- m_frameEnc.ProcessData(packetRef.data(), _payload.data(), _payload.size());
- bytesRef paddingRef(o_bytes.data() + 32 + _payload.size(), padding);
- if (padding)
- m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding);
- bytesRef packetWithPaddingRef(o_bytes.data() + 32, _payload.size() + padding);
- updateEgressMACWithFrame(packetWithPaddingRef);
- bytesRef macRef(o_bytes.data() + 32 + _payload.size() + padding, h128::size);
- egressDigest().ref().copyTo(macRef);
- }
- void RLPXFrameCoder::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes)
- {
- RLPStream header;
- uint32_t len = (uint32_t)_packet.size();
- header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)}));
- header.appendRaw(bytes({0xc2,0x80,0x80}));
- writeFrame(header, _packet, o_bytes);
- }
- bool RLPXFrameCoder::authAndDecryptHeader(bytesRef io)
- {
- asserts(io.size() == h256::size);
- updateIngressMACWithHeader(io);
- bytesConstRef macRef = io.cropped(h128::size, h128::size);
- h128 expected = ingressDigest();
- if (*(h128*)macRef.data() != expected)
- return false;
- m_frameDec.ProcessData(io.data(), io.data(), h128::size);
- return true;
- }
- bool RLPXFrameCoder::authAndDecryptFrame(bytesRef io)
- {
- bytesRef cipherText(io.cropped(0, io.size() - h128::size));
- updateIngressMACWithFrame(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;
- }
- #if defined(__GNUC__)
- // Do not warn about uses of functions (see Function Attributes), variables
- // (see Variable Attributes), and types (see Type Attributes) marked as
- // deprecated by using the deprecated attribute.
- //
- // Specifically we are suppressing the warnings from the deprecation
- // attributes added to the SHA3_256 and SHA3_512 classes in CryptoPP
- // after the 5.6.3 release.
- //
- // From that header file ...
- //
- // "The Crypto++ SHA-3 implementation dates back to January 2013 when NIST
- // selected Keccak as SHA-3. In August 2015 NIST finalized SHA-3, and it
- // was a modified version of the Keccak selection. Crypto++ 5.6.2 through
- // 5.6.4 provides the pre-FIPS 202 version of SHA-3; while Crypto++ 5.7
- // and above provides the FIPS 202 version of SHA-3.
- //
- // See also http://en.wikipedia.org/wiki/SHA-3
- //
- // This means that we will never be able to move to the CryptoPP-5.7.x
- // series of releases, because Ethereum requires Keccak, not the final
- // SHA-3 standard algorithm. We are planning to migrate cpp-ethereum
- // off CryptoPP anyway, so this is unlikely to be a long-standing issue.
- //
- // https://github.com/ethereum/cpp-ethereum/issues/3088
- //
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
- #endif // defined(__GNUC__)
- h128 RLPXFrameCoder::egressDigest()
- {
- SHA3_256 h(m_egressMac);
- h128 digest;
- h.TruncatedFinal(digest.data(), h128::size);
- return digest;
- }
- h128 RLPXFrameCoder::ingressDigest()
- {
- SHA3_256 h(m_ingressMac);
- h128 digest;
- h.TruncatedFinal(digest.data(), h128::size);
- return digest;
- }
- void RLPXFrameCoder::updateEgressMACWithHeader(bytesConstRef _headerCipher)
- {
- updateMAC(m_egressMac, _headerCipher.cropped(0, 16));
- }
- void RLPXFrameCoder::updateEgressMACWithFrame(bytesConstRef _cipher)
- {
- m_egressMac.Update(_cipher.data(), _cipher.size());
- updateMAC(m_egressMac);
- }
- void RLPXFrameCoder::updateIngressMACWithHeader(bytesConstRef _headerCipher)
- {
- updateMAC(m_ingressMac, _headerCipher.cropped(0, 16));
- }
- void RLPXFrameCoder::updateIngressMACWithFrame(bytesConstRef _cipher)
- {
- m_ingressMac.Update(_cipher.data(), _cipher.size());
- updateMAC(m_ingressMac);
- }
- void RLPXFrameCoder::updateMAC(SHA3_256& _mac, bytesConstRef _seed)
- {
- if (_seed.size() && _seed.size() != h128::size)
- asserts(false);
- SHA3_256 prevDigest(_mac);
- h128 encDigest(h128::size);
- prevDigest.TruncatedFinal(encDigest.data(), h128::size);
- h128 prevDigestOut = encDigest;
- {
- Guard l(x_macEnc);
- m_macEnc.ProcessData(encDigest.data(), encDigest.data(), 16);
- }
- if (_seed.size())
- encDigest ^= *(h128*)_seed.data();
- else
- encDigest ^= *(h128*)prevDigestOut.data();
- // update mac for final digest
- _mac.Update(encDigest.data(), h128::size);
- }
- #if defined(__GNUC__)
- #pragma GCC diagnostic pop
- #endif // defined(__GNUC__)
|