From 0d6e08cbeccf33c429644df8ee86e8326a97d2a2 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 02:48:33 -0500 Subject: [PATCH 01/50] initiator handshake crypto-test --- libdevcrypto/ECDHE.h | 2 ++ test/crypto.cpp | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 4450aec4b..2a09e8e78 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -62,6 +62,8 @@ public: /// Public key sent to remote. Public pubkey() { return m_ephemeral.pub(); } + Secret seckey() { return m_ephemeral.sec(); } + /// Input public key for dh agreement, output generated shared secret. void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret); diff --git a/test/crypto.cpp b/test/crypto.cpp index 291893f59..b63bdf596 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -316,6 +316,43 @@ BOOST_AUTO_TEST_CASE(ecdhe) BOOST_REQUIRE_EQUAL(sremote, slocal); } +BOOST_AUTO_TEST_CASE(handshakeNew) +{ + // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) + + Secret nodeAsecret(sha3("privacy")); + KeyPair nodeA(nodeAsecret); + + Secret nodeBsecret(sha3("privacy++")); + KeyPair nodeB(nodeBsecret); + + // Initiator is Alice (nodeA) + ECDHE eA; + bytes nAbytes(fromHex("0xAAAA")); + h256 nonceA(sha3(nAbytes)); + + bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); + { + 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); + + Secret ss; + s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ss); + sign(eA.seckey(), ss ^ nonceA).ref().copyTo(sig); + sha3(eA.pubkey().ref(), hepubk); + nodeA.pub().ref().copyTo(pubk); + nonceA.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + + cnote << "initAuth:" << toHex(auth); + +// bytes ack(h256::size * 2 + 1); +} + BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) { // New connections require new ECDH keypairs From c25f287d2e54c78b3a547c8f2591748dc97cd3fb Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 02:57:26 -0500 Subject: [PATCH 02/50] ack handshake for new node and encrypt both sides --- test/crypto.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index b63bdf596..ed35c3408 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -347,10 +347,25 @@ BOOST_AUTO_TEST_CASE(handshakeNew) nonceA.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; } - - cnote << "initAuth:" << toHex(auth); - -// bytes ack(h256::size * 2 + 1); + bytes authcipher; + encrypt(nodeB.pub(), &auth, authcipher); + cnote << "initAuth:" << toHex(authcipher); + + ECDHE eB; + bytes nBbytes(fromHex("0xBBBB")); + h256 nonceB(sha3(nAbytes)); + bytes ack(h256::size * 2 + 1); + { + bytesConstRef epubk(&auth[0], Secret::size); + bytesConstRef nonce(&auth[Secret::size], h256::size); + + eB.pubkey().ref().copyTo(epubk); + nonceB.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + } + bytes ackcipher; + encrypt(nodeA.pub(), &ack, ackcipher); + cnote << "ackAuth:" << toHex(ackcipher); } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) From 2aad7d856e663798603fb9455d4748bd62866698 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 16:05:20 -0500 Subject: [PATCH 03/50] key agreement --- test/crypto.cpp | 45 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index ed35c3408..588fb4d6b 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -331,17 +331,16 @@ BOOST_AUTO_TEST_CASE(handshakeNew) ECDHE eA; bytes nAbytes(fromHex("0xAAAA")); h256 nonceA(sha3(nAbytes)); - bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); + Secret ssA; { 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); - Secret ss; - s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ss); - sign(eA.seckey(), ss ^ nonceA).ref().copyTo(sig); + s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ssA); + sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); sha3(eA.pubkey().ref(), hepubk); nodeA.pub().ref().copyTo(pubk); nonceA.ref().copyTo(nonce); @@ -366,6 +365,44 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); cnote << "ackAuth:" << toHex(ackcipher); + + /// Alice + // TODO: decrypt ackcipher and check decrypted pubk is eb + Secret aEncryptK; + Secret aMacK; + Secret aEgressMac; + Secret aIngressMac; + { + bytes keyMaterial(512); + + h256 ess; + // todo: ecdh-agree should be able to output bytes + s_secp256k1.agree(eA.seckey(), eB.pubkey(), ess); + ess.ref().copyTo(bytesConstRef(&keyMaterial).cropped(0, h256::size)); + ssA.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); +// auto token = sha3(ssA); + aEncryptK = sha3(keyMaterial); + aEncryptK.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); + aMacK = sha3(keyMaterial); + + // todo: resize key-material and copy ciphertext + aEgressMac = sha3(keyMaterial); + aIngressMac = sha3(keyMaterial); + } + + +// ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk) +// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) +// token = sha3(shared-secret) +// aes-secret = sha3(ecdhe-shared-secret || shared-secret) +//# destroy shared-secret +// mac-secret = sha3(ecdhe-shared-secret || aes-secret) +//# destroy ecdhe-shared-secret +// egress-mac = sha3(mac-secret^nonce || auth) +//# destroy nonce +// ingress-mac = sha3(mac-secret^initiator-nonce || auth) +//# destroy remote-nonce + } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) From 685a7d7a10904a4cb743360b144fb83f29c673d5 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 20:45:03 -0500 Subject: [PATCH 04/50] end-to-end handshake for uknown node --- test/crypto.cpp | 85 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 66 insertions(+), 19 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index 588fb4d6b..646202168 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -323,9 +323,13 @@ BOOST_AUTO_TEST_CASE(handshakeNew) Secret nodeAsecret(sha3("privacy")); KeyPair nodeA(nodeAsecret); + BOOST_REQUIRE(nodeA.pub()); Secret nodeBsecret(sha3("privacy++")); KeyPair nodeB(nodeBsecret); + BOOST_REQUIRE(nodeA.pub()); + + BOOST_REQUIRE_NE(nodeA.sec(), nodeB.sec()); // Initiator is Alice (nodeA) ECDHE eA; @@ -348,7 +352,6 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes authcipher; encrypt(nodeB.pub(), &auth, authcipher); - cnote << "initAuth:" << toHex(authcipher); ECDHE eB; bytes nBbytes(fromHex("0xBBBB")); @@ -364,45 +367,89 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); - cnote << "ackAuth:" << toHex(ackcipher); + + BOOST_REQUIRE(eA.pubkey()); + BOOST_REQUIRE(eB.pubkey()); + BOOST_REQUIRE_NE(eA.seckey(), eB.seckey()); /// Alice - // TODO: decrypt ackcipher and check decrypted pubk is eb Secret aEncryptK; Secret aMacK; Secret aEgressMac; Secret aIngressMac; { - bytes keyMaterial(512); + // TODO: decrypt ackcipher and check decrypted material + // TODO: export ess and require equal to b + + bytes keyMaterialBytes(512); + bytesConstRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes s_secp256k1.agree(eA.seckey(), eB.pubkey(), ess); - ess.ref().copyTo(bytesConstRef(&keyMaterial).cropped(0, h256::size)); - ssA.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); aEncryptK = sha3(keyMaterial); - aEncryptK.ref().copyTo(bytesConstRef(&keyMaterial).cropped(h256::size, h256::size)); + aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); aMacK = sha3(keyMaterial); - // todo: resize key-material and copy ciphertext + // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceB).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); aEgressMac = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (aMacK ^ nonceA).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); aIngressMac = sha3(keyMaterial); } -// ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk) -// shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) -// token = sha3(shared-secret) -// aes-secret = sha3(ecdhe-shared-secret || shared-secret) -//# destroy shared-secret -// mac-secret = sha3(ecdhe-shared-secret || aes-secret) -//# destroy ecdhe-shared-secret -// egress-mac = sha3(mac-secret^nonce || auth) -//# destroy nonce -// ingress-mac = sha3(mac-secret^initiator-nonce || auth) -//# destroy remote-nonce + /// Bob + Secret ssB; + s_secp256k1.agree(nodeB.sec(), nodeA.pub(), ssB); + BOOST_REQUIRE_EQUAL(ssA, ssB); + + Secret bEncryptK; + Secret bMacK; + Secret bEgressMac; + Secret bIngressMac; + { + bytes keyMaterialBytes(512); + bytesConstRef keyMaterial(&keyMaterialBytes); + + h256 ess; + // todo: ecdh-agree should be able to output bytes + s_secp256k1.agree(eB.seckey(), eA.pubkey(), ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// auto token = sha3(ssA); + bEncryptK = sha3(keyMaterial); + bEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + bMacK = sha3(keyMaterial); + + // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + ackcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceA).ref().copyTo(keyMaterial); + bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); + bEgressMac = sha3(keyMaterial); + + keyMaterialBytes.resize(h256::size + authcipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (bMacK ^ nonceB).ref().copyTo(keyMaterial); + bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); + bIngressMac = sha3(keyMaterial); + } + BOOST_REQUIRE_EQUAL(aEncryptK, bEncryptK); + BOOST_REQUIRE_EQUAL(aMacK, bMacK); + BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); + BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) From 068dea54230ee1d7a1d8c15d041b4a02ca43aa97 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 13 Feb 2015 22:11:23 -0500 Subject: [PATCH 05/50] finished test of new-node handshake --- test/crypto.cpp | 66 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 12 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index 646202168..d572dc697 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -352,14 +352,16 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes authcipher; encrypt(nodeB.pub(), &auth, authcipher); + BOOST_REQUIRE(authcipher.size()); + // Receipient is Bob (nodeB) ECDHE eB; bytes nBbytes(fromHex("0xBBBB")); h256 nonceB(sha3(nAbytes)); - bytes ack(h256::size * 2 + 1); + bytes ack(Public::size + h256::size + 1); { - bytesConstRef epubk(&auth[0], Secret::size); - bytesConstRef nonce(&auth[Secret::size], h256::size); + bytesConstRef epubk(&ack[0], Public::size); + bytesConstRef nonce(&ack[Public::size], h256::size); eB.pubkey().ref().copyTo(epubk); nonceB.ref().copyTo(nonce); @@ -367,18 +369,29 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); + BOOST_REQUIRE(ackcipher.size()); BOOST_REQUIRE(eA.pubkey()); BOOST_REQUIRE(eB.pubkey()); BOOST_REQUIRE_NE(eA.seckey(), eB.seckey()); - /// Alice + /// Alice (after receiving ack) Secret aEncryptK; Secret aMacK; Secret aEgressMac; Secret aIngressMac; { - // TODO: decrypt ackcipher and check decrypted material + bytes ackdecrypted; + decrypt(nodeA.sec(), &ackcipher, ackdecrypted); + BOOST_REQUIRE(ackdecrypted.size()); + bytesConstRef ackRef(&ackdecrypted); + Public eBAck; + h256 nonceBAck; + ackRef.cropped(0, Public::size).copyTo(bytesConstRef(eBAck.data(), Public::size)); + ackRef.cropped(Public::size, h256::size).copyTo(nonceBAck.ref()); + BOOST_REQUIRE_EQUAL(eBAck, eB.pubkey()); + BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); + // TODO: export ess and require equal to b bytes keyMaterialBytes(512); @@ -386,18 +399,17 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eA.seckey(), eB.pubkey(), ess); + s_secp256k1.agree(eA.seckey(), eBAck, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); aEncryptK = sha3(keyMaterial); aEncryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); aMacK = sha3(keyMaterial); - - // todo: replace nonceB with decrypted nonceB + keyMaterialBytes.resize(h256::size + authcipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (aMacK ^ nonceB).ref().copyTo(keyMaterial); + (aMacK ^ nonceBAck).ref().copyTo(keyMaterial); bytesConstRef(&authcipher).copyTo(keyMaterial.cropped(h256::size, authcipher.size())); aEgressMac = sha3(keyMaterial); @@ -409,7 +421,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } - /// Bob + /// Bob (after sending ack) Secret ssB; s_secp256k1.agree(nodeB.sec(), nodeA.pub(), ssB); BOOST_REQUIRE_EQUAL(ssA, ssB); @@ -419,12 +431,42 @@ BOOST_AUTO_TEST_CASE(handshakeNew) Secret bEgressMac; Secret bIngressMac; { + bytes authdecrypted; + decrypt(nodeB.sec(), &authcipher, authdecrypted); + BOOST_REQUIRE(authdecrypted.size()); + bytesConstRef ackRef(&authdecrypted); + Signature sigAuth; + h256 heA; + Public eAAuth; + Public nodeAAuth; + h256 nonceAAuth; + bytesConstRef sig(&authdecrypted[0], Signature::size); + bytesConstRef hepubk(&authdecrypted[Signature::size], h256::size); + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&authdecrypted[Signature::size + h256::size + Public::size], h256::size); + + nonce.copyTo(nonceAAuth.ref()); + pubk.copyTo(nodeAAuth.ref()); + BOOST_REQUIRE(nonceAAuth); + BOOST_REQUIRE_EQUAL(nonceA, nonceAAuth); + BOOST_REQUIRE(nodeAAuth); + BOOST_REQUIRE_EQUAL(nodeA.pub(), nodeAAuth); // bad test, bad!!! + hepubk.copyTo(heA.ref()); + sig.copyTo(sigAuth.ref()); + + Secret ss; + s_secp256k1.agree(nodeB.sec(), nodeAAuth, ss); + eAAuth = recover(sigAuth, ss ^ nonceAAuth); + // todo: test when this fails; means remote is bad or packet bits were flipped + BOOST_REQUIRE_EQUAL(heA, sha3(eAAuth)); + BOOST_REQUIRE_EQUAL(eAAuth, eA.pubkey()); + bytes keyMaterialBytes(512); bytesConstRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eB.seckey(), eA.pubkey(), ess); + s_secp256k1.agree(eB.seckey(), eAAuth, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); @@ -435,7 +477,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) // todo: replace nonceB with decrypted nonceB keyMaterialBytes.resize(h256::size + ackcipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (bMacK ^ nonceA).ref().copyTo(keyMaterial); + (bMacK ^ nonceAAuth).ref().copyTo(keyMaterial); bytesConstRef(&ackcipher).copyTo(keyMaterial.cropped(h256::size, ackcipher.size())); bEgressMac = sha3(keyMaterial); From 261c04314f6e4d35024d55a067a8976ad8c91855 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 16 Feb 2015 03:24:52 -0500 Subject: [PATCH 06/50] fix snapshot when nodetable #1055 --- libp2p/Host.cpp | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index f117df37a..f4882b47d 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -665,17 +665,20 @@ bytes Host::saveNetwork() const } } - auto state = m_nodeTable->snapshot(); - state.sort(); - for (auto const& s: state) + if (!!m_nodeTable) { - network.appendList(3); - if (s.endpoint.tcp.address().is_v4()) - network << s.endpoint.tcp.address().to_v4().to_bytes(); - else - network << s.endpoint.tcp.address().to_v6().to_bytes(); - network << s.endpoint.tcp.port() << s.id; - count++; + auto state = m_nodeTable->snapshot(); + state.sort(); + for (auto const& s: state) + { + network.appendList(3); + if (s.endpoint.tcp.address().is_v4()) + network << s.endpoint.tcp.address().to_v4().to_bytes(); + else + network << s.endpoint.tcp.address().to_v6().to_bytes(); + network << s.endpoint.tcp.port() << s.id; + count++; + } } RLPStream ret(3); From 4814303c0965173ed720d70792712c7065ab3571 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 07:16:56 -0500 Subject: [PATCH 07/50] initial handshake on the wire --- libdevcrypto/Common.cpp | 62 ++++++++++ libdevcrypto/Common.h | 4 + libdevcrypto/CryptoPP.cpp | 2 +- libdevcrypto/ECDHE.cpp | 5 + libdevcrypto/ECDHE.h | 9 +- libp2p/Host.cpp | 239 +++++++++++++++++++++++++++++++++----- libp2p/Host.h | 47 +++++++- test/crypto.cpp | 66 +++++++++-- 8 files changed, 392 insertions(+), 42 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 0a94662c8..44b7d9ad8 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -92,6 +92,68 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) return decrypt(_k, _cipher, o_plain); } +h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + auto iv = Nonce::get(); + return encryptSymNoAuth(_k, _plain, o_cipher, iv); +} + +h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv) +{ + const int c_aesBlockLen = 16; + o_cipher.resize(_plain.size() + (c_aesBlockLen - (_plain.size() % c_aesBlockLen)) % c_aesBlockLen); + + bytes underflowBytes(0); + auto size = _plain.size() - (_plain.size() % c_aesBlockLen); + if (o_cipher.size() > _plain.size()) + { + underflowBytes.resize(c_aesBlockLen); + _plain.cropped(size, _plain.size() - size).copyTo(&underflowBytes); + } + + const int c_aesKeyLen = 32; + SecByteBlock key(_k.data(), c_aesKeyLen); + try + { + CTR_Mode::Encryption e; + e.SetKeyWithIV(key, key.size(), _iv.data()); + if (size) + e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); + if (underflowBytes.size()) + e.ProcessData(o_cipher.data(), underflowBytes.data(), underflowBytes.size()); + return _iv; + } + catch(CryptoPP::Exception& e) + { + cerr << e.what() << endl; + o_cipher.resize(0); + return h256(); + } +} + +bool dev::decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) +{ + const int c_aesKeyLen = 32; + const int c_aesBlockLen = 16; + asserts(_cipher.size() % c_aesBlockLen == 0); + o_plaintext.resize(_cipher.size()); + SecByteBlock key(_k.data(), c_aesKeyLen); + + try + { + CTR_Mode::Decryption d; + d.SetKeyWithIV(key, key.size(), _iv.data()); + d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); + return true; + } + catch(CryptoPP::Exception& e) + { + cerr << e.what() << endl; + o_plaintext.resize(0); + return false; + } +} + Public dev::recover(Signature const& _sig, h256 const& _message) { return s_secp256k1.recover(_sig, _message.ref()); diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index e91df2526..d46161ab8 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -93,6 +93,10 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); +h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv); +bool decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); + /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 2d0170fb9..385fec981 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -201,7 +201,7 @@ void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) { (void)o_s; (void)_s; - ECDH::Domain d(m_oid); + ECDH::Domain d(ASN1::secp256k1()); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index deae3bc6d..8f667f0f9 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -29,6 +29,11 @@ using namespace dev::crypto; static Secp256k1 s_secp256k1; +void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s) +{ + s_secp256k1.agree(_s, _r, o_s); +} + void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) { if (m_remoteEphemeral) diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 2a09e8e78..6d01b0329 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -48,6 +48,11 @@ private: std::map m_sessions; Secret m_secret; }; + +namespace ecdh +{ +void agree(Secret const& _s, Public const& _r, h256& o_s); +} /** * @brief Derive DH shared secret from EC keypairs. @@ -82,10 +87,10 @@ class ECDHEKeyExchange: private ECDHE { public: /// Exchange with unknown remote (pass public key for ingress exchange) - ECDHEKeyExchange(Alias& _k): m_alias(_k) {}; + ECDHEKeyExchange(Alias& _k): m_alias(_k) {} /// Exchange with known remote - ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {}; + ECDHEKeyExchange(Alias& _k, AliasSession _known): m_alias(_k), m_known(_known) {} /// Provide public key for dh agreement to generate shared secret. void agree(Public const& _remoteEphemeral); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index f4882b47d..c91cedaf3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -337,7 +337,7 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - doHandshake(s, NodeId()); + doHandshake(new Handshake(s)); success = true; } catch (Exception const& _e) @@ -351,15 +351,18 @@ void Host::runAcceptor() } // asio doesn't close socket on error - if (!success && s->is_open()) + if (!success) { - boost::system::error_code ec; - s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - s->close(); + if (s->is_open()) + { + boost::system::error_code ec; + s->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + s->close(); + } + delete s; } m_accepting = false; - delete s; if (ec.value() < 1) runAcceptor(); @@ -367,22 +370,209 @@ void Host::runAcceptor() } } -void Host::doHandshake(bi::tcp::socket* _socket, NodeId _nodeId) +void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) { - try { - clog(NetConnect) << "Accepting connection for " << _socket->remote_endpoint(); - } catch (...){} - - shared_ptr p; - if (_nodeId) - p = m_peers[_nodeId]; + if (_ech) + { + boost::system::error_code ec; + _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (_h->socket->is_open()) + _h->socket->close(); + delete _h; + return; + } - if (!p) - p.reset(new Peer()); - p->endpoint.tcp.address(_socket->remote_endpoint().address()); - - auto ps = std::make_shared(this, std::move(*_socket), p); - ps->start(); + if (!_h->started()) + { + clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); + + if (_h->originated) + { + clog(NetConnect) << "devp2p.connect.egress sending auth"; + // egress: tx auth + asserts(_h->remote); + _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesConstRef sig(&_h->auth[0], Signature::size); + bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); + bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&_h->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(m_alias.sec(), _h->remote, _h->ss); + sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); + sha3(_h->ecdhe.pubkey().ref(), hepubk); + m_alias.pub().ref().copyTo(pubk); + _h->nonce.ref().copyTo(nonce); + _h->auth[_h->auth.size() - 1] = 0x0; + encrypt(_h->remote, &_h->auth, _h->authCipher); + ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) + { + doHandshake(_h, ec); + }); + } + else + { + clog(NetConnect) << "devp2p.connect.ingress recving auth"; + // ingress: rx auth + _h->authCipher.resize(279); + ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) + { + if (ec) + doHandshake(_h, ec); + else + { + decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); + bytesConstRef sig(&_h->auth[0], Signature::size); + bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); + bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(_h->remote.ref()); + nonce.copyTo(_h->remoteNonce.ref()); + + crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); + _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); + assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); + doHandshake(_h); + } + }); + } + } + else if (_h->started() && !_h->acked()) + if (_h->originated) + { + clog(NetConnect) << "devp2p.connect.egress recving ack"; + // egress: rx ack + _h->ackCipher.resize(182); + ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) + { + if (ec) + doHandshake(_h, ec); + else + { + decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); + bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); + bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); + doHandshake(_h); + } + }); + } + else + { + clog(NetConnect) << "devp2p.connect.ingress sending ack"; + // ingress: tx ack + _h->ack.resize(Public::size + h256::size + 1); + bytesConstRef epubk(&_h->ack[0], Public::size); + bytesConstRef nonce(&_h->ack[Public::size], h256::size); + _h->ecdhe.pubkey().ref().copyTo(epubk); + _h->nonce.ref().copyTo(nonce); + _h->ack[_h->ack.size() - 1] = 0x0; + encrypt(_h->remote, &_h->ack, _h->ackCipher); + ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) + { + doHandshake(_h, ec); + }); + } + else if (_h->started() && _h->acked()) + { + if (_h->originated) + clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; + else + clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; + PeerSecrets* k = new PeerSecrets; + bytes keyMaterialBytes(512); + bytesConstRef keyMaterial(&keyMaterialBytes); + + _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); + _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + // auto token = sha3(ssA); + k->encryptK = sha3(keyMaterial); + k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + k->macK = sha3(keyMaterial); + + // 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) + + bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + k->egressMac = sha3(keyMaterial); + + bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + k->ingressMac = sha3(keyMaterial); + + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); + k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); + sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); + k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); + + clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; + + ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + { + if (ec) + { + delete k; + doHandshake(_h, ec); + return; + } + + ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + { + if (_h->originated) + clog(NetNote) << "devp2p.connect.egress recving magic sequence"; + else + clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; + + if (ec) + { + delete k; + doHandshake(_h, ec); + return; + } + + bytes decryptedMagic; + decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); + clog(NetNote) << "devp2p.connect received magic sequence"; + + shared_ptr p; + p = m_peers[_h->remote]; + + if (!p) + { + p.reset(new Peer()); + p->id = _h->remote; + } + p->endpoint.tcp.address(_h->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(this, move(*_h->socket), p); + ps->start(); + + delete k; + }); + }); + } + else + { + clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; + boost::system::error_code ec; + _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + _h->socket->close(); + delete _h; + } } string Host::pocHost() @@ -467,18 +657,13 @@ void Host::connect(std::shared_ptr const& _p) 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(); - _p->m_lastDisconnect = NoDisconnect; - _p->m_lastConnected = std::chrono::system_clock::now(); - _p->m_failedAttempts = 0; - auto ps = make_shared(this, std::move(*s), _p); - ps->start(); - + doHandshake(new Handshake(s, _p->id)); } - delete s; Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); }); diff --git a/libp2p/Host.h b/libp2p/Host.h index baf8f0585..3ca414c88 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "NodeTable.h" #include "HostCapability.h" #include "Network.h" @@ -157,6 +158,48 @@ protected: void restoreNetwork(bytesConstRef _b); private: + struct Handshake + { + /// Handshake for ingress connection. Takes ownership of socket. + Handshake(bi::tcp::socket* _socket): socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + Handshake(bi::tcp::socket* _socket, NodeId _remote): socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~Handshake() { delete socket; } + + bool started() { return auth.size() > 0; } + bool acked() { return ack.size() > 0; } + + /// If originated this is accepting (ingress) node id, otherwise it is originating (egress) node. + NodeId remote; + bi::tcp::socket *socket; + bool originated = false; + + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; + }; + struct PeerSecrets + { + Secret encryptK; + Secret macK; + h256 egressMac; + h256 ingressMac; + + bytes magicCipherAndMac; + bytes recvdMagicCipherAndMac; + }; + /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -171,8 +214,8 @@ private: /// Called only from startedWorking(). void runAcceptor(); - /// Handler for verifying handshake siganture before creating session. _nodeId is passed for outbound connections. If successful, socket is moved to Session via std::move. - void doHandshake(bi::tcp::socket* _socket, NodeId _nodeId = NodeId()); + /// Attempt to authenticate peer and establish a new session. + void doHandshake(Handshake* _h, boost::system::error_code _ec = boost::system::error_code()); void seal(bytes& _b); diff --git a/test/crypto.cpp b/test/crypto.cpp index d572dc697..5dec3dd6d 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -321,13 +321,16 @@ BOOST_AUTO_TEST_CASE(handshakeNew) // authInitiator -> E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) // authRecipient -> E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) - Secret nodeAsecret(sha3("privacy")); + h256 base(sha3("privacy")); + sha3(base.ref(), base.ref()); + Secret nodeAsecret(base); KeyPair nodeA(nodeAsecret); BOOST_REQUIRE(nodeA.pub()); - Secret nodeBsecret(sha3("privacy++")); + sha3(base.ref(), base.ref()); + Secret nodeBsecret(base); KeyPair nodeB(nodeBsecret); - BOOST_REQUIRE(nodeA.pub()); + BOOST_REQUIRE(nodeB.pub()); BOOST_REQUIRE_NE(nodeA.sec(), nodeB.sec()); @@ -343,7 +346,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); bytesConstRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); - s_secp256k1.agree(nodeA.sec(), nodeB.pub(), ssA); + crypto::ecdh::agree(nodeA.sec(), nodeB.pub(), ssA); sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); sha3(eA.pubkey().ref(), hepubk); nodeA.pub().ref().copyTo(pubk); @@ -352,7 +355,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes authcipher; encrypt(nodeB.pub(), &auth, authcipher); - BOOST_REQUIRE(authcipher.size()); + BOOST_REQUIRE_EQUAL(authcipher.size(), 279); // Receipient is Bob (nodeB) ECDHE eB; @@ -360,6 +363,14 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 nonceB(sha3(nAbytes)); bytes ack(Public::size + h256::size + 1); { + // todo: replace nodeA.pub() in encrypt() + // decrypt public key from auth + bytes authdecrypted; + decrypt(nodeB.sec(), &authcipher, authdecrypted); + Public node; + bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); + pubk.copyTo(node.ref()); + bytesConstRef epubk(&ack[0], Public::size); bytesConstRef nonce(&ack[Public::size], h256::size); @@ -369,7 +380,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) } bytes ackcipher; encrypt(nodeA.pub(), &ack, ackcipher); - BOOST_REQUIRE(ackcipher.size()); + BOOST_REQUIRE_EQUAL(ackcipher.size(), 182); BOOST_REQUIRE(eA.pubkey()); BOOST_REQUIRE(eB.pubkey()); @@ -399,7 +410,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eA.seckey(), eBAck, ess); + eA.agree(eBAck, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssA.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); @@ -423,7 +434,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) /// Bob (after sending ack) Secret ssB; - s_secp256k1.agree(nodeB.sec(), nodeA.pub(), ssB); + crypto::ecdh::agree(nodeB.sec(), nodeA.pub(), ssB); BOOST_REQUIRE_EQUAL(ssA, ssB); Secret bEncryptK; @@ -466,7 +477,8 @@ BOOST_AUTO_TEST_CASE(handshakeNew) h256 ess; // todo: ecdh-agree should be able to output bytes - s_secp256k1.agree(eB.seckey(), eAAuth, ess); + eB.agree(eAAuth, ess); +// s_secp256k1.agree(eB.seckey(), eAAuth, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); ssB.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); @@ -492,6 +504,9 @@ BOOST_AUTO_TEST_CASE(handshakeNew) BOOST_REQUIRE_EQUAL(aMacK, bMacK); BOOST_REQUIRE_EQUAL(aEgressMac, bIngressMac); BOOST_REQUIRE_EQUAL(bEgressMac, aIngressMac); + + + } BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) @@ -504,6 +519,37 @@ BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) } +BOOST_AUTO_TEST_CASE(crypto_rlpxwire) +{ + Secret encryptK(sha3("...")); + h256 egressMac(sha3("+++")); + // TESTING: send encrypt magic sequence + bytes magic {0x22,0x40,0x08,0x91}; + bytes magicCipherAndMac; + encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h256()); + magicCipherAndMac.resize(magicCipherAndMac.size() + 32); + sha3mac(egressMac.ref(), &magic, egressMac.ref()); + egressMac.ref().copyTo(bytesConstRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); + + bytes plaintext; + bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); + decryptSymNoAuth(encryptK, h256(), cipher, plaintext); +} + +BOOST_AUTO_TEST_CASE(crypto_aes128_ctr) +{ + Secret k(sha3("0xAAAA")); + string m = "AAAAAAAAAAAAAAAA"; + bytesConstRef msg((byte*)m.data(), m.size()); + + bytes ciphertext; + auto iv = encryptSymNoAuth(k, msg, ciphertext); + + bytes plaintext; + decryptSymNoAuth(k, iv, &ciphertext, plaintext); + BOOST_REQUIRE_EQUAL(asString(plaintext), m); +} + BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) { const int aesKeyLen = 16; @@ -535,7 +581,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) // 68 % 255 should be difference of counter e.ProcessData(out, in, text.size()); - ctr = h128(u128(ctr) + text.size() % 16); + ctr = h128(u128(ctr) + text.size() / 16); BOOST_REQUIRE(text != original); cipherCopy = text; From ec4ba8e53231f57792428049114d2a7c7cf17eb5 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 07:40:59 -0500 Subject: [PATCH 08/50] fix magic. disable ping for now, as it can trigger prior to hello packet. --- libp2p/Host.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index c91cedaf3..de3a32dd7 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -517,6 +517,7 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; + k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) { @@ -543,8 +544,7 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) bytes decryptedMagic; decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); - clog(NetNote) << "devp2p.connect received magic sequence"; - + shared_ptr p; p = m_peers[_h->remote]; @@ -716,8 +716,8 @@ void Host::run(boost::system::error_code const&) if (auto pp = p.second.lock()) pp->serviceNodesRequest(); - keepAlivePeers(); - disconnectLatePeers(); +// keepAlivePeers(); +// disconnectLatePeers(); auto c = peerCount(); if (m_idealPeerCount && !c) From ecb06feb85d7926d4f2dce518e9f98a2d43a1819 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 18:57:25 -0500 Subject: [PATCH 09/50] verification of and placeholder for capabilities handshake --- libp2p/Host.cpp | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index de3a32dd7..9a48817fc 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -542,24 +542,27 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) return; } + /// capabilities handshake (encrypted magic sequence is placeholder) bytes decryptedMagic; decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); - - shared_ptr p; - p = m_peers[_h->remote]; - - if (!p) + if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { - p.reset(new Peer()); - p->id = _h->remote; + shared_ptr p; + p = m_peers[_h->remote]; + + if (!p) + { + p.reset(new Peer()); + p->id = _h->remote; + } + p->endpoint.tcp.address(_h->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(this, move(*_h->socket), p); + ps->start(); } - p->endpoint.tcp.address(_h->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(this, move(*_h->socket), p); - ps->start(); delete k; }); From f6d5aa75a0221d6396cb685e4aa02308e05694a2 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 17 Feb 2015 21:35:10 -0500 Subject: [PATCH 10/50] refactor of handshake. first pass, compiles. --- libp2p/Host.cpp | 375 +++++++++++++++++++++++++++++++++++++----------- libp2p/Host.h | 121 ++++++++++------ libp2p/Peer.h | 3 +- 3 files changed, 371 insertions(+), 128 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 9a48817fc..1b6d8d65b 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -337,7 +338,8 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - doHandshake(new Handshake(s)); + auto handshake = make_shared(m_alias, s); + handshake->start(); success = true; } catch (Exception const& _e) @@ -370,89 +372,89 @@ void Host::runAcceptor() } } -void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) -{ +void PeerHandshake::transition(boost::system::error_code _ech) { if (_ech) { boost::system::error_code ec; - _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (_h->socket->is_open()) - _h->socket->close(); - delete _h; + socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (socket->is_open()) + socket->close(); return; } - if (!_h->started()) + auto self(shared_from_this()); + if (isNew()) { - clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); + clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); - if (_h->originated) + if (originated) { clog(NetConnect) << "devp2p.connect.egress sending auth"; // egress: tx auth - asserts(_h->remote); - _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - bytesConstRef sig(&_h->auth[0], Signature::size); - bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); - bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); + asserts(remote); + auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + 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); // E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) - crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); - sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); - sha3(_h->ecdhe.pubkey().ref(), hepubk); - m_alias.pub().ref().copyTo(pubk); - _h->nonce.ref().copyTo(nonce); - _h->auth[_h->auth.size() - 1] = 0x0; - encrypt(_h->remote, &_h->auth, _h->authCipher); - ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) + crypto::ecdh::agree(alias.sec(), remote, ss); + sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); + sha3(ecdhe.pubkey().ref(), hepubk); + alias.pub().ref().copyTo(pubk); + this->nonce.ref().copyTo(nonce); + auth[auth.size() - 1] = 0x0; + encrypt(remote, &auth, authCipher); + + ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) { - doHandshake(_h, ec); + transition(ec); }); } else { clog(NetConnect) << "devp2p.connect.ingress recving auth"; // ingress: rx auth - _h->authCipher.resize(279); - ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) + authCipher.resize(279); + ba::async_read(*socket, ba::buffer(authCipher, 279), [this, self](boost::system::error_code ec, std::size_t) { if (ec) - doHandshake(_h, ec); + transition(ec); else { - decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); - bytesConstRef sig(&_h->auth[0], Signature::size); - bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); - bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); - bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); - pubk.copyTo(_h->remote.ref()); - nonce.copyTo(_h->remoteNonce.ref()); + decrypt(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()); - crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); - _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); - assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); - doHandshake(_h); + crypto::ecdh::agree(alias.sec(), remote, ss); + remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); + assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); + transition(); } }); } } - else if (_h->started() && !_h->acked()) - if (_h->originated) + else if (isAcking()) + if (originated) { clog(NetConnect) << "devp2p.connect.egress recving ack"; // egress: rx ack - _h->ackCipher.resize(182); - ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) + ackCipher.resize(182); + ba::async_read(*socket, ba::buffer(ackCipher, 182), [this, self](boost::system::error_code ec, std::size_t) { if (ec) - doHandshake(_h, ec); + transition(ec); else { - decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); - bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); - bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); - doHandshake(_h); + decrypt(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(); } }); } @@ -460,21 +462,21 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.ingress sending ack"; // ingress: tx ack - _h->ack.resize(Public::size + h256::size + 1); - bytesConstRef epubk(&_h->ack[0], Public::size); - bytesConstRef nonce(&_h->ack[Public::size], h256::size); - _h->ecdhe.pubkey().ref().copyTo(epubk); - _h->nonce.ref().copyTo(nonce); - _h->ack[_h->ack.size() - 1] = 0x0; - encrypt(_h->remote, &_h->ack, _h->ackCipher); - ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) + ack.resize(Public::size + h256::size + 1); + bytesConstRef epubk(&ack[0], Public::size); + bytesConstRef nonce(&ack[Public::size], h256::size); + ecdhe.pubkey().ref().copyTo(epubk); + this->nonce.ref().copyTo(nonce); + ack[ack.size() - 1] = 0x0; + encrypt(remote, &ack, ackCipher); + ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) { - doHandshake(_h, ec); + transition(ec); }); } - else if (_h->started() && _h->acked()) + else if (isAuthenticating()) { - if (_h->originated) + if (originated) clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; else clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; @@ -482,9 +484,9 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) bytes keyMaterialBytes(512); bytesConstRef keyMaterial(&keyMaterialBytes); - _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); - _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + ecdhe.agree(remoteEphemeral, ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); // auto token = sha3(ssA); k->encryptK = sha3(keyMaterial); k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); @@ -495,17 +497,17 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) // Recipient egress-mac: sha3(mac-secret^initiator-nonce || auth-sent-ack) // ingress-mac: sha3(mac-secret^recipient-nonce || auth-recvd-init) - bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; + bytes const& egressCipher = originated ? authCipher : ackCipher; keyMaterialBytes.resize(h256::size + egressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); + (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); k->egressMac = sha3(keyMaterial); - bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; + bytes const& ingressCipher = originated ? ackCipher : authCipher; keyMaterialBytes.resize(h256::size + ingressCipher.size()); keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); + (k->macK ^ nonce).ref().copyTo(keyMaterial); bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); k->ingressMac = sha3(keyMaterial); @@ -519,18 +521,18 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); - ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + ba::async_write(*socket, ba::buffer(k->magicCipherAndMac), [this, self, k, magic](boost::system::error_code ec, std::size_t) { if (ec) { delete k; - doHandshake(_h, ec); + transition(ec); return; } - ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) + ba::async_read(*socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, self, k, magic](boost::system::error_code ec, std::size_t) { - if (_h->originated) + if (originated) clog(NetNote) << "devp2p.connect.egress recving magic sequence"; else clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; @@ -538,7 +540,7 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) if (ec) { delete k; - doHandshake(_h, ec); + transition(ec); return; } @@ -548,36 +550,246 @@ void Host::doHandshake(Handshake* _h, boost::system::error_code _ech) if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { shared_ptr p; - p = m_peers[_h->remote]; + // todo: need host +// p = m_peers[remote]; if (!p) { p.reset(new Peer()); - p->id = _h->remote; + p->id = remote; } - p->endpoint.tcp.address(_h->socket->remote_endpoint().address()); + 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(this, move(*_h->socket), p); - ps->start(); + // todo: need host +// auto ps = std::make_shared(this, move(*socket), p); +// ps->start(); } + // todo: PeerSession needs to take ownership of k (PeerSecrets) delete k; }); }); } else { - clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; + clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Authentication Failed)"; boost::system::error_code ec; - _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - _h->socket->close(); - delete _h; + socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + socket->close(); } } +//void Host::doHandshake(PeerHandshake* _h, boost::system::error_code _ech) +//{ +// if (_ech) +// { +// boost::system::error_code ec; +// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +// if (_h->socket->is_open()) +// _h->socket->close(); +// delete _h; +// return; +// } +// +// if (_h->isNew()) +// { +// clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); +// +// if (_h->originated) +// { +// clog(NetConnect) << "devp2p.connect.egress sending auth"; +// // egress: tx auth +// asserts(_h->remote); +// _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); +// bytesConstRef sig(&_h->auth[0], Signature::size); +// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); +// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); +// bytesConstRef nonce(&_h->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(m_alias.sec(), _h->remote, _h->ss); +// sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); +// sha3(_h->ecdhe.pubkey().ref(), hepubk); +// m_alias.pub().ref().copyTo(pubk); +// _h->nonce.ref().copyTo(nonce); +// _h->auth[_h->auth.size() - 1] = 0x0; +// encrypt(_h->remote, &_h->auth, _h->authCipher); +// ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) +// { +// doHandshake(_h, ec); +// }); +// } +// else +// { +// clog(NetConnect) << "devp2p.connect.ingress recving auth"; +// // ingress: rx auth +// _h->authCipher.resize(279); +// ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// doHandshake(_h, ec); +// else +// { +// decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); +// bytesConstRef sig(&_h->auth[0], Signature::size); +// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); +// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); +// bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); +// pubk.copyTo(_h->remote.ref()); +// nonce.copyTo(_h->remoteNonce.ref()); +// +// crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); +// _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); +// assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); +// doHandshake(_h); +// } +// }); +// } +// } +// else if (_h->isAcking()) +// if (_h->originated) +// { +// clog(NetConnect) << "devp2p.connect.egress recving ack"; +// // egress: rx ack +// _h->ackCipher.resize(182); +// ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// doHandshake(_h, ec); +// else +// { +// decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); +// bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); +// bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); +// doHandshake(_h); +// } +// }); +// } +// else +// { +// clog(NetConnect) << "devp2p.connect.ingress sending ack"; +// // ingress: tx ack +// _h->ack.resize(Public::size + h256::size + 1); +// bytesConstRef epubk(&_h->ack[0], Public::size); +// bytesConstRef nonce(&_h->ack[Public::size], h256::size); +// _h->ecdhe.pubkey().ref().copyTo(epubk); +// _h->nonce.ref().copyTo(nonce); +// _h->ack[_h->ack.size() - 1] = 0x0; +// encrypt(_h->remote, &_h->ack, _h->ackCipher); +// ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) +// { +// doHandshake(_h, ec); +// }); +// } +// else if (_h->isAuthenticating()) +// { +// if (_h->originated) +// clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; +// else +// clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; +// PeerSecrets* k = new PeerSecrets; +// bytes keyMaterialBytes(512); +// bytesConstRef keyMaterial(&keyMaterialBytes); +// +// _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); +// _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); +// _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// // auto token = sha3(ssA); +// k->encryptK = sha3(keyMaterial); +// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// k->macK = sha3(keyMaterial); +// +// // 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) +// +// bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; +// keyMaterialBytes.resize(h256::size + egressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); +// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); +// k->egressMac = sha3(keyMaterial); +// +// bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; +// keyMaterialBytes.resize(h256::size + ingressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); +// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); +// k->ingressMac = sha3(keyMaterial); +// +// // TESTING: send encrypt magic sequence +// bytes magic {0x22,0x40,0x08,0x91}; +// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); +// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); +// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); +// k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); +// +// clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; +// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); +// +// ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) +// { +// if (ec) +// { +// delete k; +// doHandshake(_h, ec); +// return; +// } +// +// ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) +// { +// if (_h->originated) +// clog(NetNote) << "devp2p.connect.egress recving magic sequence"; +// else +// clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; +// +// if (ec) +// { +// delete k; +// doHandshake(_h, ec); +// return; +// } +// +// /// capabilities handshake (encrypted magic sequence is placeholder) +// bytes decryptedMagic; +// decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); +// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) +// { +// shared_ptr p; +// p = m_peers[_h->remote]; +// +// if (!p) +// { +// p.reset(new Peer()); +// p->id = _h->remote; +// } +// p->endpoint.tcp.address(_h->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(this, move(*_h->socket), p); +// ps->start(); +// } +// +// delete k; +// }); +// }); +// } +// else +// { +// clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; +// boost::system::error_code ec; +// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); +// _h->socket->close(); +// delete _h; +// } +//} + string Host::pocHost() { vector strs; @@ -665,7 +877,8 @@ void Host::connect(std::shared_ptr const& _p) else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - doHandshake(new Handshake(s, _p->id)); + auto handshake = make_shared(m_alias, s, _p->id); + handshake->start(); } Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); diff --git a/libp2p/Host.h b/libp2p/Host.h index 3ca414c88..31ca22442 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -64,6 +64,80 @@ private: Host& m_host; }; +/** + * @brief Key material and derived secrets for TCP peer connection. + */ +struct PeerSecrets +{ + friend class PeerHandshake; +protected: + Secret encryptK; + Secret macK; + h256 egressMac; + h256 ingressMac; + + bytes magicCipherAndMac; + bytes recvdMagicCipherAndMac; +}; + +struct PeerHandshake: public std::enable_shared_from_this +{ + friend class Host; + enum State + { + New, // New->AckAuth [egress: tx auth, ingress: rx auth] + AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] + Authenticating, // Authenticating [tx caps, rx caps, authenticate] + }; + + /// Handshake for ingress connection. Takes ownership of socket. + PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket): alias(_alias), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket, NodeId _remote): alias(_alias), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~PeerHandshake() { delete socket; } + +protected: + /// Returns true when the auth message is to be sent or received. + bool isNew() { return state == New; } + + /// Returns true when the ack message is to be sent or received. + bool isAcking() { return state == AckAuth; } + + /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. + bool isAuthenticating() { return state == Authenticating; } + + void start() { transition(); } + +private: + void transition(boost::system::error_code _ech = boost::system::error_code()); + + /// Current state of handshake. + State state = New; + + KeyPair const& alias; + + /// Node id of remote host for socket. + NodeId remote; + + bi::tcp::socket* socket; + bool originated = false; + + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; +}; + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. @@ -158,48 +232,6 @@ protected: void restoreNetwork(bytesConstRef _b); private: - struct Handshake - { - /// Handshake for ingress connection. Takes ownership of socket. - Handshake(bi::tcp::socket* _socket): socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - /// Handshake for egress connection to _remote. Takes ownership of socket. - Handshake(bi::tcp::socket* _socket, NodeId _remote): socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - ~Handshake() { delete socket; } - - bool started() { return auth.size() > 0; } - bool acked() { return ack.size() > 0; } - - /// If originated this is accepting (ingress) node id, otherwise it is originating (egress) node. - NodeId remote; - bi::tcp::socket *socket; - bool originated = false; - - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - Secret ss; - Secret ess; - - crypto::ECDHE ecdhe; - h256 nonce; - - Public remoteEphemeral; - h256 remoteNonce; - }; - struct PeerSecrets - { - Secret encryptK; - Secret macK; - h256 egressMac; - h256 ingressMac; - - bytes magicCipherAndMac; - bytes recvdMagicCipherAndMac; - }; - /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -213,10 +245,7 @@ private: /// Called only from startedWorking(). void runAcceptor(); - - /// Attempt to authenticate peer and establish a new session. - void doHandshake(Handshake* _h, boost::system::error_code _ec = boost::system::error_code()); - + void seal(bytes& _b); /// Called by Worker. Not thread-safe; to be called only by worker. diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 704e5c2b4..5c1aa015d 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -29,7 +29,7 @@ namespace dev namespace p2p { - + /** * @brief Representation of connectivity state and all other pertinent Peer metadata. * A Peer represents connectivity between two nodes, which in this case, are the host @@ -54,6 +54,7 @@ class Peer: public Node { friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() + friend struct PeerHandshake; public: bool isOffline() const { return !m_session.lock(); } From 4ebc2f8320c1f8f045b384712a771a7bc51bb4c6 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 18 Feb 2015 00:56:52 -0500 Subject: [PATCH 11/50] reenable sessions. more refactor. --- libp2p/Host.cpp | 23 ++++---- libp2p/Host.h | 153 ++++++++++++++++++++++++------------------------ 2 files changed, 86 insertions(+), 90 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 1b6d8d65b..d79b6bd39 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -338,7 +338,7 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - auto handshake = make_shared(m_alias, s); + auto handshake = make_shared(this, s); handshake->start(); success = true; } @@ -399,10 +399,10 @@ void PeerHandshake::transition(boost::system::error_code _ech) { bytesConstRef 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(alias.sec(), remote, ss); + crypto::ecdh::agree(host->m_alias.sec(), remote, ss); sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); sha3(ecdhe.pubkey().ref(), hepubk); - alias.pub().ref().copyTo(pubk); + host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; encrypt(remote, &auth, authCipher); @@ -423,7 +423,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { transition(ec); else { - decrypt(alias.sec(), bytesConstRef(&authCipher), auth); + decrypt(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); @@ -431,7 +431,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { pubk.copyTo(remote.ref()); nonce.copyTo(remoteNonce.ref()); - crypto::ecdh::agree(alias.sec(), remote, ss); + crypto::ecdh::agree(host->m_alias.sec(), remote, ss); remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); transition(); @@ -451,7 +451,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { transition(ec); else { - decrypt(alias.sec(), bytesConstRef(&ackCipher), ack); + decrypt(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(); @@ -551,7 +551,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { { shared_ptr p; // todo: need host -// p = m_peers[remote]; + p = host->m_peers[remote]; if (!p) { @@ -562,10 +562,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { p->m_lastDisconnect = NoDisconnect; p->m_lastConnected = std::chrono::system_clock::now(); p->m_failedAttempts = 0; - - // todo: need host -// auto ps = std::make_shared(this, move(*socket), p); -// ps->start(); + + auto ps = std::make_shared(host, move(*socket), p); + ps->start(); } // todo: PeerSession needs to take ownership of k (PeerSecrets) @@ -877,7 +876,7 @@ void Host::connect(std::shared_ptr const& _p) else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - auto handshake = make_shared(m_alias, s, _p->id); + auto handshake = make_shared(this, s, _p->id); handshake->start(); } Guard l(x_pendingNodeConns); diff --git a/libp2p/Host.h b/libp2p/Host.h index 31ca22442..9a49d2706 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -63,98 +63,21 @@ private: Host& m_host; }; - -/** - * @brief Key material and derived secrets for TCP peer connection. - */ -struct PeerSecrets -{ - friend class PeerHandshake; -protected: - Secret encryptK; - Secret macK; - h256 egressMac; - h256 ingressMac; - - bytes magicCipherAndMac; - bytes recvdMagicCipherAndMac; -}; - -struct PeerHandshake: public std::enable_shared_from_this -{ - friend class Host; - enum State - { - New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] - Authenticating, // Authenticating [tx caps, rx caps, authenticate] - }; - - /// Handshake for ingress connection. Takes ownership of socket. - PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket): alias(_alias), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - /// Handshake for egress connection to _remote. Takes ownership of socket. - PeerHandshake(KeyPair const& _alias, bi::tcp::socket* _socket, NodeId _remote): alias(_alias), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - ~PeerHandshake() { delete socket; } - -protected: - /// Returns true when the auth message is to be sent or received. - bool isNew() { return state == New; } - - /// Returns true when the ack message is to be sent or received. - bool isAcking() { return state == AckAuth; } - - /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. - bool isAuthenticating() { return state == Authenticating; } - - void start() { transition(); } - -private: - void transition(boost::system::error_code _ech = boost::system::error_code()); - - /// Current state of handshake. - State state = New; - - KeyPair const& alias; - - /// Node id of remote host for socket. - NodeId remote; - - bi::tcp::socket* socket; - bool originated = false; - - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - Secret ss; - Secret ess; - - crypto::ECDHE ecdhe; - h256 nonce; - - Public remoteEphemeral; - h256 remoteNonce; -}; /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo exceptions when nodeTable not set (prior to start) - * @todo onNodeTableEvent: move peer-connection logic into ensurePeers * @todo handshake: gracefully disconnect peer if peer already connected * @todo abstract socket -> IPConnection * @todo determinePublic: ipv6, udp * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port - * @todo write host identifier to disk w/nodes * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency - * @todo configuration-management (NetworkPrefs+Keys+Topology) */ class Host: public Worker { friend class HostNodeTableHandler; + friend struct PeerHandshake; friend class Session; friend class HostCapabilityFace; @@ -308,5 +231,79 @@ private: bool m_accepting = false; }; +/** + * @brief Key material and derived secrets for TCP peer connection. + */ +struct PeerSecrets +{ + friend struct PeerHandshake; +protected: + Secret encryptK; + Secret macK; + h256 egressMac; + h256 ingressMac; + + bytes magicCipherAndMac; + bytes recvdMagicCipherAndMac; +}; + +struct PeerHandshake: public std::enable_shared_from_this +{ + friend class Host; + enum State + { + New, // New->AckAuth [egress: tx auth, ingress: rx auth] + AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] + Authenticating, // Authenticating [tx caps, rx caps, authenticate] + }; + + /// Handshake for ingress connection. Takes ownership of socket. + PeerHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + PeerHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~PeerHandshake() { delete socket; } + +protected: + /// Returns true when the auth message is to be sent or received. + bool isNew() { return state == New; } + + /// Returns true when the ack message is to be sent or received. + bool isAcking() { return state == AckAuth; } + + /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. + bool isAuthenticating() { return state == Authenticating; } + + void start() { transition(); } + +private: + void transition(boost::system::error_code _ech = boost::system::error_code()); + + /// Current state of handshake. + State state = New; + + Host* host; + + /// Node id of remote host for socket. + NodeId remote; + + bi::tcp::socket* socket; + bool originated = false; + + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; +}; + } } From 8e8b6de2558f9001c51b2644e2191adf725cf2b5 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 18 Feb 2015 01:03:18 -0500 Subject: [PATCH 12/50] advance transition --- libp2p/Host.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index d79b6bd39..4d3998f95 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -385,6 +385,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) { auto self(shared_from_this()); if (isNew()) { + state = AckAuth; + clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); if (originated) @@ -440,6 +442,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { } } else if (isAcking()) + { + state = Authenticating; + if (originated) { clog(NetConnect) << "devp2p.connect.egress recving ack"; @@ -474,6 +479,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { transition(ec); }); } + } else if (isAuthenticating()) { if (originated) From f6fc86e992cc37699e2ce48cb91f0b6435256663 Mon Sep 17 00:00:00 2001 From: subtly Date: Wed, 18 Feb 2015 01:21:49 -0500 Subject: [PATCH 13/50] fix curlies. remove unused transition methods. --- libp2p/Host.cpp | 226 ++---------------------------------------------- libp2p/Host.h | 11 +-- 2 files changed, 10 insertions(+), 227 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 4d3998f95..73290568a 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -372,7 +372,8 @@ void Host::runAcceptor() } } -void PeerHandshake::transition(boost::system::error_code _ech) { +void PeerHandshake::transition(boost::system::error_code _ech) +{ if (_ech) { boost::system::error_code ec; @@ -383,9 +384,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { } auto self(shared_from_this()); - if (isNew()) + if (nextState == New) { - state = AckAuth; + nextState = AckAuth; clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); @@ -441,9 +442,9 @@ void PeerHandshake::transition(boost::system::error_code _ech) { }); } } - else if (isAcking()) + else if (nextState == AckAuth) { - state = Authenticating; + nextState = Authenticating; if (originated) { @@ -480,7 +481,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { }); } } - else if (isAuthenticating()) + else if (nextState == Authenticating) { if (originated) clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; @@ -517,6 +518,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); k->ingressMac = sha3(keyMaterial); + // This test will be replaced with protocol-capabilities information (was Hello packet) // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); @@ -556,9 +558,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { shared_ptr p; - // todo: need host p = host->m_peers[remote]; - if (!p) { p.reset(new Peer()); @@ -573,7 +573,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) { ps->start(); } - // todo: PeerSession needs to take ownership of k (PeerSecrets) + // todo: PeerSession will take ownership of k and use it to encrypt wireline. delete k; }); }); @@ -587,214 +587,6 @@ void PeerHandshake::transition(boost::system::error_code _ech) { } } -//void Host::doHandshake(PeerHandshake* _h, boost::system::error_code _ech) -//{ -// if (_ech) -// { -// boost::system::error_code ec; -// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); -// if (_h->socket->is_open()) -// _h->socket->close(); -// delete _h; -// return; -// } -// -// if (_h->isNew()) -// { -// clog(NetConnect) << "Authenticating connection for " << _h->socket->remote_endpoint(); -// -// if (_h->originated) -// { -// clog(NetConnect) << "devp2p.connect.egress sending auth"; -// // egress: tx auth -// asserts(_h->remote); -// _h->auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); -// bytesConstRef sig(&_h->auth[0], Signature::size); -// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); -// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); -// bytesConstRef nonce(&_h->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(m_alias.sec(), _h->remote, _h->ss); -// sign(_h->ecdhe.seckey(), _h->ss ^ _h->nonce).ref().copyTo(sig); -// sha3(_h->ecdhe.pubkey().ref(), hepubk); -// m_alias.pub().ref().copyTo(pubk); -// _h->nonce.ref().copyTo(nonce); -// _h->auth[_h->auth.size() - 1] = 0x0; -// encrypt(_h->remote, &_h->auth, _h->authCipher); -// ba::async_write(*_h->socket, ba::buffer(_h->authCipher), [=](boost::system::error_code ec, std::size_t) -// { -// doHandshake(_h, ec); -// }); -// } -// else -// { -// clog(NetConnect) << "devp2p.connect.ingress recving auth"; -// // ingress: rx auth -// _h->authCipher.resize(279); -// ba::async_read(*_h->socket, ba::buffer(_h->authCipher, 279), [=](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// doHandshake(_h, ec); -// else -// { -// decrypt(m_alias.sec(), bytesConstRef(&_h->authCipher), _h->auth); -// bytesConstRef sig(&_h->auth[0], Signature::size); -// bytesConstRef hepubk(&_h->auth[Signature::size], h256::size); -// bytesConstRef pubk(&_h->auth[Signature::size + h256::size], Public::size); -// bytesConstRef nonce(&_h->auth[Signature::size + h256::size + Public::size], h256::size); -// pubk.copyTo(_h->remote.ref()); -// nonce.copyTo(_h->remoteNonce.ref()); -// -// crypto::ecdh::agree(m_alias.sec(), _h->remote, _h->ss); -// _h->remoteEphemeral = recover(*(Signature*)sig.data(), _h->ss ^ _h->remoteNonce); -// assert(sha3(_h->remoteEphemeral) == *(h256*)hepubk.data()); -// doHandshake(_h); -// } -// }); -// } -// } -// else if (_h->isAcking()) -// if (_h->originated) -// { -// clog(NetConnect) << "devp2p.connect.egress recving ack"; -// // egress: rx ack -// _h->ackCipher.resize(182); -// ba::async_read(*_h->socket, ba::buffer(_h->ackCipher, 182), [=](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// doHandshake(_h, ec); -// else -// { -// decrypt(m_alias.sec(), bytesConstRef(&_h->ackCipher), _h->ack); -// bytesConstRef(&_h->ack).cropped(0, Public::size).copyTo(_h->remoteEphemeral.ref()); -// bytesConstRef(&_h->ack).cropped(Public::size, h256::size).copyTo(_h->remoteNonce.ref()); -// doHandshake(_h); -// } -// }); -// } -// else -// { -// clog(NetConnect) << "devp2p.connect.ingress sending ack"; -// // ingress: tx ack -// _h->ack.resize(Public::size + h256::size + 1); -// bytesConstRef epubk(&_h->ack[0], Public::size); -// bytesConstRef nonce(&_h->ack[Public::size], h256::size); -// _h->ecdhe.pubkey().ref().copyTo(epubk); -// _h->nonce.ref().copyTo(nonce); -// _h->ack[_h->ack.size() - 1] = 0x0; -// encrypt(_h->remote, &_h->ack, _h->ackCipher); -// ba::async_write(*_h->socket, ba::buffer(_h->ackCipher), [=](boost::system::error_code ec, std::size_t) -// { -// doHandshake(_h, ec); -// }); -// } -// else if (_h->isAuthenticating()) -// { -// if (_h->originated) -// clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; -// else -// clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; -// PeerSecrets* k = new PeerSecrets; -// bytes keyMaterialBytes(512); -// bytesConstRef keyMaterial(&keyMaterialBytes); -// -// _h->ecdhe.agree(_h->remoteEphemeral, _h->ess); -// _h->ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); -// _h->ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// // auto token = sha3(ssA); -// k->encryptK = sha3(keyMaterial); -// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// k->macK = sha3(keyMaterial); -// -// // 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) -// -// bytes const& egressCipher = _h->originated ? _h->authCipher : _h->ackCipher; -// keyMaterialBytes.resize(h256::size + egressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ _h->remoteNonce).ref().copyTo(keyMaterial); -// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); -// k->egressMac = sha3(keyMaterial); -// -// bytes const& ingressCipher = _h->originated ? _h->ackCipher : _h->authCipher; -// keyMaterialBytes.resize(h256::size + ingressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ _h->nonce).ref().copyTo(keyMaterial); -// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); -// k->ingressMac = sha3(keyMaterial); -// -// // TESTING: send encrypt magic sequence -// bytes magic {0x22,0x40,0x08,0x91}; -// encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); -// k->magicCipherAndMac.resize(k->magicCipherAndMac.size() + 32); -// sha3mac(k->egressMac.ref(), &magic, k->egressMac.ref()); -// k->egressMac.ref().copyTo(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); -// -// clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; -// k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); -// -// ba::async_write(*_h->socket, ba::buffer(k->magicCipherAndMac), [this, k, _h, magic](boost::system::error_code ec, std::size_t) -// { -// if (ec) -// { -// delete k; -// doHandshake(_h, ec); -// return; -// } -// -// ba::async_read(*_h->socket, ba::buffer(k->recvdMagicCipherAndMac, k->magicCipherAndMac.size()), [this, k, _h, magic](boost::system::error_code ec, std::size_t) -// { -// if (_h->originated) -// clog(NetNote) << "devp2p.connect.egress recving magic sequence"; -// else -// clog(NetNote) << "devp2p.connect.ingress recving magic sequence"; -// -// if (ec) -// { -// delete k; -// doHandshake(_h, ec); -// return; -// } -// -// /// capabilities handshake (encrypted magic sequence is placeholder) -// bytes decryptedMagic; -// decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); -// if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) -// { -// shared_ptr p; -// p = m_peers[_h->remote]; -// -// if (!p) -// { -// p.reset(new Peer()); -// p->id = _h->remote; -// } -// p->endpoint.tcp.address(_h->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(this, move(*_h->socket), p); -// ps->start(); -// } -// -// delete k; -// }); -// }); -// } -// else -// { -// clog(NetConnect) << "Disconnecting " << _h->socket->remote_endpoint() << " (Authentication Failed)"; -// boost::system::error_code ec; -// _h->socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); -// _h->socket->close(); -// delete _h; -// } -//} - string Host::pocHost() { vector strs; diff --git a/libp2p/Host.h b/libp2p/Host.h index 9a49d2706..81016333a 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -266,22 +266,13 @@ struct PeerHandshake: public std::enable_shared_from_this ~PeerHandshake() { delete socket; } protected: - /// Returns true when the auth message is to be sent or received. - bool isNew() { return state == New; } - - /// Returns true when the ack message is to be sent or received. - bool isAcking() { return state == AckAuth; } - - /// Returns true when auth and ack messages have been received and caps message is to be sent, received, and authenticated. - bool isAuthenticating() { return state == Authenticating; } - void start() { transition(); } private: void transition(boost::system::error_code _ech = boost::system::error_code()); /// Current state of handshake. - State state = New; + State nextState = New; Host* host; From 953bfd240057f231ac5d7ed57d06fe2e8ad7f282 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 1 Mar 2015 22:17:17 +0100 Subject: [PATCH 14/50] start of ecies interop with go --- libdevcrypto/Common.cpp | 37 +++++++------ libdevcrypto/Common.h | 6 +-- libdevcrypto/CryptoPP.cpp | 110 +++++++++++++++++++++++++++++++++++++- libdevcrypto/CryptoPP.h | 9 ++++ libp2p/Host.cpp | 22 ++++---- test/crypto.cpp | 78 ++++++++++++++++++++++----- 6 files changed, 214 insertions(+), 48 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 21b854ca3..7e3c403cd 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -94,24 +94,23 @@ bool dev::decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plain) return decrypt(_k, _cipher, o_plain); } -h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) +h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) { - auto iv = Nonce::get(); + h128 iv(Nonce::get()); return encryptSymNoAuth(_k, _plain, o_cipher, iv); } -h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv) +h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv) { const int c_aesBlockLen = 16; - o_cipher.resize(_plain.size() + (c_aesBlockLen - (_plain.size() % c_aesBlockLen)) % c_aesBlockLen); + size_t extraBytes = _plain.size() % c_aesBlockLen; + size_t trimmedSize = _plain.size() - extraBytes; + size_t paddedSize = _plain.size() + ((16 - extraBytes) % 16); + o_cipher.resize(paddedSize); - bytes underflowBytes(0); - auto size = _plain.size() - (_plain.size() % c_aesBlockLen); - if (o_cipher.size() > _plain.size()) - { - underflowBytes.resize(c_aesBlockLen); - _plain.cropped(size, _plain.size() - size).copyTo(&underflowBytes); - } + bytes underflowBytes(16); + if (o_cipher.size() != _plain.size()) + _plain.cropped(trimmedSize, extraBytes).copyTo(&underflowBytes); const int c_aesKeyLen = 32; SecByteBlock key(_k.data(), c_aesKeyLen); @@ -119,28 +118,28 @@ h256 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph { CTR_Mode::Encryption e; e.SetKeyWithIV(key, key.size(), _iv.data()); - if (size) - e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); - if (underflowBytes.size()) - e.ProcessData(o_cipher.data(), underflowBytes.data(), underflowBytes.size()); + if (trimmedSize) + e.ProcessData(o_cipher.data(), _plain.data(), trimmedSize); + if (extraBytes) + e.ProcessData(o_cipher.data() + trimmedSize, underflowBytes.data(), underflowBytes.size()); return _iv; } catch(CryptoPP::Exception& e) { cerr << e.what() << endl; o_cipher.resize(0); - return h256(); + return h128(); } } -bool dev::decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) +bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) { - const int c_aesKeyLen = 32; const int c_aesBlockLen = 16; asserts(_cipher.size() % c_aesBlockLen == 0); o_plaintext.resize(_cipher.size()); - SecByteBlock key(_k.data(), c_aesKeyLen); + const int c_aesKeyLen = 32; + SecByteBlock key(_k.data(), c_aesKeyLen); try { CTR_Mode::Decryption d; diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 9c33c4bb3..92a13f9a5 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -96,9 +96,9 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); -h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); -h256 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h256 const& _iv); -bool decryptSymNoAuth(Secret const& _k, h256 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); +h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); +h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); +bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index d9ccb3a35..47061d6d0 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -19,8 +19,9 @@ * @date 2014 */ -#include "CryptoPP.h" #include +#include "ECDHE.h" +#include "CryptoPP.h" using namespace std; using namespace dev; @@ -31,6 +32,113 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes."); static_assert(dev::Public::size == 64, "Public key must be 64 bytes."); static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); +bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) +{ + // similar to go ecies implementation + + if (!_s1.size()) + { + _s1.resize(1); + asserts(_s1[0] == 0); + } + + // for sha3, hash.blocksize is 1088 bits, but this might really be digest size + auto reps = ((kdBitLen + 7) * 8) / (32 * 8); + bytes ctr({0, 0, 0, 1}); + bytes k; + CryptoPP::SHA256 ctx; + while (reps--) + { + ctx.Update(ctr.data(), ctr.size()); + ctx.Update(_z.data(), Secret::size); + ctx.Update(_s1.data(), _s1.size()); + // append hash to k + bytes digest(32); + ctx.Final(digest.data()); + ctx.Restart(); + + k.reserve(k.size() + h256::size); + move(digest.begin(), digest.end(), back_inserter(k)); + + if (ctr[3]++ && ctr[3] != 0) { + continue; + } else if (ctr[2]++ && ctr[2] != 0) { + continue; + } else if (ctr[1]++ && ctr[1] != 0) { + continue; + } else + ctr[0]++; + } + + k.resize(kdBitLen / 8); + return move(k); +} + +void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) +{ + // similar to go ecies implementation + // todo: s1/s2/tag + + auto r = KeyPair::create(); + h256 z; + ecdh::agree(r.sec(), _k, z); + auto key = eciesKDF(z, bytes(), 512); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); + bytesRef mKey = bytesRef(&key).cropped(32, 32); + sha3(mKey, mKey); + + bytes cipherText; + encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); + if (!cipherText.size()) + return; + +// d := messageTag(params.Hash, Km, em, s2) +// copy(ct[len(Rb)+len(em):], d) + + bytes msg(1 + Public::size + cipherText.size() + 32); + msg[0] = 0x04; + r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); + bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size()); + bytesConstRef(&cipherText).copyTo(msgCipherRef); + + io_cipher.resize(msg.size()); + io_cipher.swap(msg); +} + +void eciesMessageTag() +{ + +} + +void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) +{ + // similar to go ecies implementation + // todo: s1/s2 + + // io_cipher[0] must be 2, 3, or 4, else invalidpublickey + if (io_text[0] < 2 || io_text[0] > 4) + // invalid message: publickey + return; + + if (io_text.size() < (1 + Public::size + h256::size + 1)) + // invalid message: length + return; + + h256 z; + ecdh::agree(_k, *(Public*)(io_text.data()+1), z); + auto key = eciesKDF(z, bytes(), 512); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); + bytesRef mKey = bytesRef(&key).cropped(32, 32); + sha3(mKey, mKey); + + bytes plain; + size_t cipherLen = io_text.size() - 1 - Public::size - h256::size; + bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen); + decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain); + io_text.resize(plain.size()); + io_text.swap(plain); +} + void Secp256k1::encrypt(Public const& _k, bytes& io_cipher) { ECIES::Encryptor e; diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index fa9d92aa1..3464606f6 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -81,6 +81,15 @@ public: /// Decrypts text (replace input). void decrypt(Secret const& _k, bytes& io_text); + /// Temporary; to replace encrypt once interop w/go is passing. + void encryptECIES(Public const& _k, bytes& io_cipher); + + /// Temporary; to replace decrypt once interop w/go is passing. + void decryptECIES(Secret const& _k, bytes& io_text); + + /// Key derivation function used by ECIES. + bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); + /// @returns siganture of message. Signature sign(Secret const& _k, bytesConstRef _message); diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 669d1eb0c..8d9994003 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -394,12 +394,12 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.egress sending auth"; // egress: tx auth - asserts(remote); + asserts((bool)remote); auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - 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); + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + 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); @@ -469,8 +469,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.ingress sending ack"; // ingress: tx ack ack.resize(Public::size + h256::size + 1); - bytesConstRef epubk(&ack[0], Public::size); - bytesConstRef nonce(&ack[Public::size], h256::size); + 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; @@ -489,7 +489,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; PeerSecrets* k = new PeerSecrets; bytes keyMaterialBytes(512); - bytesConstRef keyMaterial(&keyMaterialBytes); + bytesRef keyMaterial(&keyMaterialBytes); ecdhe.agree(remoteEphemeral, ess); ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); @@ -521,10 +521,10 @@ void PeerHandshake::transition(boost::system::error_code _ech) // This test will be replaced with protocol-capabilities information (was Hello packet) // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; - encryptSymNoAuth(k->encryptK, &magic, k->magicCipherAndMac, h256()); + 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(bytesConstRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); + k->egressMac.ref().copyTo(bytesRef(&k->magicCipherAndMac).cropped(k->magicCipherAndMac.size() - 32, 32)); clog(NetConnect) << "devp2p.connect.egress txrx magic sequence"; k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); @@ -554,7 +554,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) /// capabilities handshake (encrypted magic sequence is placeholder) bytes decryptedMagic; - decryptSymNoAuth(k->encryptK, h256(), &k->recvdMagicCipherAndMac, decryptedMagic); + decryptSymNoAuth(k->encryptK, h128(), &k->recvdMagicCipherAndMac, decryptedMagic); if (decryptedMagic[0] == 0x22 && decryptedMagic[1] == 0x40 && decryptedMagic[2] == 0x08 && decryptedMagic[3] == 0x91) { shared_ptr p; diff --git a/test/crypto.cpp b/test/crypto.cpp index 5dec3dd6d..56fb8a51c 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -228,6 +228,51 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(ecies_kdf) +{ + KeyPair local = KeyPair::create(); + KeyPair remote = KeyPair::create(); + // nonce + Secret z1; + ecdh::agree(local.sec(), remote.pub(), z1); + auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 512); + bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); + bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); + sha3(mKey1, mKey1); + + Secret z2; + ecdh::agree(remote.sec(), local.pub(), z2); + auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 512); + bytesConstRef eKey2 = bytesConstRef(&key2).cropped(0, 32); + bytesRef mKey2 = bytesRef(&key2).cropped(32, 32); + sha3(mKey2, mKey2); + + BOOST_REQUIRE(eKey1.toBytes() == eKey2.toBytes()); + BOOST_REQUIRE(mKey1.toBytes() == mKey2.toBytes()); + + BOOST_REQUIRE((u256)h256(z1) > 0); + BOOST_REQUIRE(z1 == z2); + + BOOST_REQUIRE(key1.size() > 0 && ((u512)h512(key1)) > 0); + BOOST_REQUIRE(key1 == key2); +} + +BOOST_AUTO_TEST_CASE(ecies_standard) +{ + KeyPair k = KeyPair::create(); + + string message("Now is the time for all good persons to come to the aid of humanity."); + string original = message; + bytes b = asBytes(message); + + s_secp256k1.encryptECIES(k.pub(), b); + BOOST_REQUIRE(b != asBytes(original)); + BOOST_REQUIRE(b.size() > 0 && ((u128)h128(b)) > 0); + + s_secp256k1.decryptECIES(k.sec(), b); + BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); +} + BOOST_AUTO_TEST_CASE(ecies_eckeypair) { KeyPair k = KeyPair::create(); @@ -341,10 +386,10 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytes auth(Signature::size + h256::size + Public::size + h256::size + 1); Secret ssA; { - 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); + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); crypto::ecdh::agree(nodeA.sec(), nodeB.pub(), ssA); sign(eA.seckey(), ssA ^ nonceA).ref().copyTo(sig); @@ -371,8 +416,8 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytesConstRef pubk(&authdecrypted[Signature::size + h256::size], Public::size); pubk.copyTo(node.ref()); - bytesConstRef epubk(&ack[0], Public::size); - bytesConstRef nonce(&ack[Public::size], h256::size); + bytesRef epubk(&ack[0], Public::size); + bytesRef nonce(&ack[Public::size], h256::size); eB.pubkey().ref().copyTo(epubk); nonceB.ref().copyTo(nonce); @@ -398,7 +443,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) bytesConstRef ackRef(&ackdecrypted); Public eBAck; h256 nonceBAck; - ackRef.cropped(0, Public::size).copyTo(bytesConstRef(eBAck.data(), Public::size)); + ackRef.cropped(0, Public::size).copyTo(bytesRef(eBAck.data(), Public::size)); ackRef.cropped(Public::size, h256::size).copyTo(nonceBAck.ref()); BOOST_REQUIRE_EQUAL(eBAck, eB.pubkey()); BOOST_REQUIRE_EQUAL(nonceBAck, nonceB); @@ -406,7 +451,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) // TODO: export ess and require equal to b bytes keyMaterialBytes(512); - bytesConstRef keyMaterial(&keyMaterialBytes); + bytesRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes @@ -473,7 +518,7 @@ BOOST_AUTO_TEST_CASE(handshakeNew) BOOST_REQUIRE_EQUAL(eAAuth, eA.pubkey()); bytes keyMaterialBytes(512); - bytesConstRef keyMaterial(&keyMaterialBytes); + bytesRef keyMaterial(&keyMaterialBytes); h256 ess; // todo: ecdh-agree should be able to output bytes @@ -519,24 +564,29 @@ BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) } -BOOST_AUTO_TEST_CASE(crypto_rlpxwire) +BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) { Secret encryptK(sha3("...")); h256 egressMac(sha3("+++")); // TESTING: send encrypt magic sequence bytes magic {0x22,0x40,0x08,0x91}; bytes magicCipherAndMac; - encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h256()); + encryptSymNoAuth(encryptK, &magic, magicCipherAndMac, h128()); + magicCipherAndMac.resize(magicCipherAndMac.size() + 32); sha3mac(egressMac.ref(), &magic, egressMac.ref()); - egressMac.ref().copyTo(bytesConstRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); + egressMac.ref().copyTo(bytesRef(&magicCipherAndMac).cropped(magicCipherAndMac.size() - 32, 32)); bytes plaintext; bytesConstRef cipher(&magicCipherAndMac[0], magicCipherAndMac.size() - 32); - decryptSymNoAuth(encryptK, h256(), cipher, plaintext); + decryptSymNoAuth(encryptK, h128(), cipher, plaintext); + + plaintext.resize(magic.size()); + BOOST_REQUIRE(plaintext.size() > 0); + BOOST_REQUIRE(magic == plaintext); } -BOOST_AUTO_TEST_CASE(crypto_aes128_ctr) +BOOST_AUTO_TEST_CASE(ecies_aes128_ctr) { Secret k(sha3("0xAAAA")); string m = "AAAAAAAAAAAAAAAA"; From bde85adee736ee02d5a951f66c71407bbe6a09db Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 2 Mar 2015 05:36:23 +0100 Subject: [PATCH 15/50] implement ecies mac tag --- libdevcore/vector_ref.h | 2 +- libdevcrypto/CryptoPP.cpp | 43 +++++++++++++++++++++++---------------- libdevcrypto/CryptoPP.h | 3 ++- 3 files changed, 28 insertions(+), 20 deletions(-) diff --git a/libdevcore/vector_ref.h b/libdevcore/vector_ref.h index 42633f6f1..cdf4f8478 100644 --- a/libdevcore/vector_ref.h +++ b/libdevcore/vector_ref.h @@ -40,7 +40,7 @@ public: bool empty() const { return !m_count; } vector_ref<_T> next() const { return vector_ref<_T>(m_data + m_count, m_count); } vector_ref<_T> cropped(size_t _begin, size_t _count = ~size_t(0)) const { if (m_data && _begin + std::max(size_t(0), _count) <= m_count) return vector_ref<_T>(m_data + _begin, _count == ~size_t(0) ? m_count - _begin : _count); else return vector_ref<_T>(); } - void retarget(_T const* _d, size_t _s) { m_data = _d; m_count = _s; } + void retarget(_T* _d, size_t _s) { m_data = _d; m_count = _s; } void retarget(std::vector<_T> const& _t) { m_data = _t.data(); m_count = _t.size(); } void copyTo(vector_ref::type> _t) const { memcpy(_t.data(), m_data, std::min(_t.size(), m_count) * sizeof(_T)); } void populate(vector_ref::type> _t) const { copyTo(_t); memset(_t.data() + m_count, 0, std::max(_t.size(), m_count) - m_count); } diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 47061d6d0..e3c811a14 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -34,7 +34,7 @@ static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) { - // similar to go ecies implementation + // interop w/go ecies implementation if (!_s1.size()) { @@ -76,9 +76,7 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) { - // similar to go ecies implementation - // todo: s1/s2/tag - + // interop w/go ecies implementation auto r = KeyPair::create(); h256 z; ecdh::agree(r.sec(), _k, z); @@ -91,38 +89,34 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); if (!cipherText.size()) return; - -// d := messageTag(params.Hash, Km, em, s2) -// copy(ct[len(Rb)+len(em):], d) - + bytes msg(1 + Public::size + cipherText.size() + 32); msg[0] = 0x04; r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size()); bytesConstRef(&cipherText).copyTo(msgCipherRef); + // tag message + CryptoPP::HMAC ctx(mKey.data(), mKey.size()); + ctx.Update(cipherText.data(), cipherText.size()); + ctx.Final(msg.data() + 1 + Public::size + cipherText.size()); + io_cipher.resize(msg.size()); io_cipher.swap(msg); } -void eciesMessageTag() +bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) { + // interop w/go ecies implementation -} - -void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) -{ - // similar to go ecies implementation - // todo: s1/s2 - // io_cipher[0] must be 2, 3, or 4, else invalidpublickey if (io_text[0] < 2 || io_text[0] > 4) // invalid message: publickey - return; + return false; if (io_text.size() < (1 + Public::size + h256::size + 1)) // invalid message: length - return; + return false; h256 z; ecdh::agree(_k, *(Public*)(io_text.data()+1), z); @@ -134,9 +128,22 @@ void Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) bytes plain; size_t cipherLen = io_text.size() - 1 - Public::size - h256::size; bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen); + bytesConstRef msgMac(cipher.data() + cipher.size(), h256::size); + + // verify tag + CryptoPP::HMAC ctx(mKey.data(), mKey.size()); + ctx.Update(cipher.data(), cipher.size()); + h256 mac; + ctx.Final(mac.data()); + for (unsigned i = 0; i < h256::size; i++) + if (mac[i] != msgMac[i]) + return false; + decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain); io_text.resize(plain.size()); io_text.swap(plain); + + return true; } void Secp256k1::encrypt(Public const& _k, bytes& io_cipher) diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index 3464606f6..e0f5f16ba 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -65,6 +65,7 @@ inline Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s. /** * CryptoPP secp256k1 algorithms. + * @todo Collect ECIES methods into class. */ class Secp256k1 { @@ -85,7 +86,7 @@ public: void encryptECIES(Public const& _k, bytes& io_cipher); /// Temporary; to replace decrypt once interop w/go is passing. - void decryptECIES(Secret const& _k, bytes& io_text); + bool decryptECIES(Secret const& _k, bytes& io_text); /// Key derivation function used by ECIES. bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); From 30e75e265948f4a846adb4c484bd03c6c7b3bedb Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 2 Mar 2015 06:10:42 +0100 Subject: [PATCH 16/50] initial ecies interop w/go --- libdevcrypto/Common.cpp | 16 ++++++++++++++++ libdevcrypto/Common.h | 2 ++ libdevcrypto/CryptoPP.cpp | 18 ++++++++++-------- libp2p/Host.cpp | 30 ++++++++++++++++++++++-------- libp2p/Host.h | 1 + 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 7e3c403cd..e826d6157 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -82,6 +82,22 @@ bool dev::decrypt(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) return true; } +void dev::encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher) +{ + bytes io = _plain.toBytes(); + s_secp256k1.encryptECIES(_k, io); + o_cipher = std::move(io); +} + +bool dev::decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext) +{ + bytes io = _cipher.toBytes(); + if (!s_secp256k1.decryptECIES(_k, io)) + return false; + o_plaintext = std::move(io); + return true; +} + void dev::encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher) { // TOOD: @alex @subtly do this properly. diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 92a13f9a5..2a5658991 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -96,6 +96,8 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); +bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index e3c811a14..ba53cb7a7 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -90,16 +90,17 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) if (!cipherText.size()) return; - bytes msg(1 + Public::size + cipherText.size() + 32); + bytes msg(1 + Public::size + h128::size + cipherText.size() + 32); msg[0] = 0x04; r.pub().ref().copyTo(bytesRef(&msg).cropped(1, Public::size)); - bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size, cipherText.size()); + bytesRef msgCipherRef = bytesRef(&msg).cropped(1 + Public::size + h128::size, cipherText.size()); bytesConstRef(&cipherText).copyTo(msgCipherRef); // tag message CryptoPP::HMAC ctx(mKey.data(), mKey.size()); - ctx.Update(cipherText.data(), cipherText.size()); - ctx.Final(msg.data() + 1 + Public::size + cipherText.size()); + bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size()); + ctx.Update(cipherWithIV.data(), cipherWithIV.size()); + ctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); io_cipher.resize(msg.size()); io_cipher.swap(msg); @@ -114,7 +115,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) // invalid message: publickey return false; - if (io_text.size() < (1 + Public::size + h256::size + 1)) + if (io_text.size() < (1 + Public::size + h128::size + 1 + h256::size)) // invalid message: length return false; @@ -126,13 +127,14 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) sha3(mKey, mKey); bytes plain; - size_t cipherLen = io_text.size() - 1 - Public::size - h256::size; - bytesConstRef cipher(io_text.data() + 1 + Public::size, cipherLen); + size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size; + bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen); + bytesConstRef cipher = cipherWithIV.cropped(h128::size, cipherLen); bytesConstRef msgMac(cipher.data() + cipher.size(), h256::size); // verify tag CryptoPP::HMAC ctx(mKey.data(), mKey.size()); - ctx.Update(cipher.data(), cipher.size()); + ctx.Update(cipherWithIV.data(), cipherWithIV.size()); h256 mac; ctx.Final(mac.data()); for (unsigned i = 0; i < h256::size; i++) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 8d9994003..67a701eb3 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -408,7 +408,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); auth[auth.size() - 1] = 0x0; - encrypt(remote, &auth, authCipher); + encryptECIES(remote, &auth, authCipher); ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) { @@ -419,14 +419,21 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.ingress recving auth"; // ingress: rx auth - authCipher.resize(279); - ba::async_read(*socket, ba::buffer(authCipher, 279), [this, self](boost::system::error_code ec, std::size_t) + 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 { - decrypt(host->m_alias.sec(), bytesConstRef(&authCipher), auth); + if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&authCipher), auth)) + { + clog(NetWarn) << "devp2p.connect.egress recving auth decrypt failed"; + nextState = Error; + transition(); + return; + } + bytesConstRef sig(&auth[0], Signature::size); bytesConstRef hepubk(&auth[Signature::size], h256::size); bytesConstRef pubk(&auth[Signature::size + h256::size], Public::size); @@ -450,14 +457,21 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.egress recving ack"; // egress: rx ack - ackCipher.resize(182); - ba::async_read(*socket, ba::buffer(ackCipher, 182), [this, self](boost::system::error_code ec, std::size_t) + 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 { - decrypt(host->m_alias.sec(), bytesConstRef(&ackCipher), ack); + if (!decryptECIES(host->m_alias.sec(), bytesConstRef(&ackCipher), ack)) + { + clog(NetWarn) << "devp2p.connect.egress recving ack decrypt failed"; + nextState = Error; + transition(); + return; + } + bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); transition(); @@ -474,7 +488,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) ecdhe.pubkey().ref().copyTo(epubk); this->nonce.ref().copyTo(nonce); ack[ack.size() - 1] = 0x0; - encrypt(remote, &ack, ackCipher); + encryptECIES(remote, &ack, ackCipher); ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); diff --git a/libp2p/Host.h b/libp2p/Host.h index 81016333a..86f682a1a 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -252,6 +252,7 @@ struct PeerHandshake: public std::enable_shared_from_this friend class Host; enum State { + Error = -1, New, // New->AckAuth [egress: tx auth, ingress: rx auth] AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] Authenticating, // Authenticating [tx caps, rx caps, authenticate] From 795e2fe2ae56632627c08710d48945c750ff6208 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 2 Mar 2015 06:47:48 +0100 Subject: [PATCH 17/50] doc methods --- libdevcrypto/Common.cpp | 2 +- libdevcrypto/Common.h | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index e826d6157..08eb9bd3d 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Common.cpp - * @author Gav Wood * @author Alex Leverington + * @author Gav Wood * @date 2014 */ diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 2a5658991..1bc16fa93 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -15,8 +15,8 @@ along with cpp-ethereum. If not, see . */ /** @file Common.h - * @author Gav Wood * @author Alex Leverington + * @author Gav Wood * @date 2014 * * Ethereum-specific data structures & algorithms. @@ -96,10 +96,19 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); +/// Encrypt payload using ECIES standard with AES-CTR. TODO: move into class. void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Decrypt payload using ECIES standard with AES-CTR. TODO: move into class. bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); + +/// Encrypts payload with random IV using AES-CTR. TODO: prefix IV. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); + +/// Encrypts payload with specified IV using AES-CTR TODO: prefix IV. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); + +/// Decrypts payload with specified IV TODO: prefix IV. bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); /// Recovers Public key from signed message hash. From e3fbf63f8fb9a5fad46e4bb188916e257578803a Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 02:23:48 +0100 Subject: [PATCH 18/50] ecies interop fix and tests. 128-bit keys. --- libdevcrypto/Common.cpp | 23 ++++-------------- libdevcrypto/CryptoPP.cpp | 51 ++++++++++++++++++++------------------- test/crypto.cpp | 48 ++++++++++++++++++++++++++++-------- 3 files changed, 69 insertions(+), 53 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 08eb9bd3d..4abac7eed 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -118,26 +118,15 @@ h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv) { - const int c_aesBlockLen = 16; - size_t extraBytes = _plain.size() % c_aesBlockLen; - size_t trimmedSize = _plain.size() - extraBytes; - size_t paddedSize = _plain.size() + ((16 - extraBytes) % 16); - o_cipher.resize(paddedSize); - - bytes underflowBytes(16); - if (o_cipher.size() != _plain.size()) - _plain.cropped(trimmedSize, extraBytes).copyTo(&underflowBytes); - - const int c_aesKeyLen = 32; + o_cipher.resize(_plain.size()); + + const int c_aesKeyLen = 16; SecByteBlock key(_k.data(), c_aesKeyLen); try { CTR_Mode::Encryption e; e.SetKeyWithIV(key, key.size(), _iv.data()); - if (trimmedSize) - e.ProcessData(o_cipher.data(), _plain.data(), trimmedSize); - if (extraBytes) - e.ProcessData(o_cipher.data() + trimmedSize, underflowBytes.data(), underflowBytes.size()); + e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); return _iv; } catch(CryptoPP::Exception& e) @@ -150,11 +139,9 @@ h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext) { - const int c_aesBlockLen = 16; - asserts(_cipher.size() % c_aesBlockLen == 0); o_plaintext.resize(_cipher.size()); - const int c_aesKeyLen = 32; + const size_t c_aesKeyLen = 16; SecByteBlock key(_k.data(), c_aesKeyLen); try { diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index ba53cb7a7..82d426a76 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -32,22 +32,17 @@ static_assert(dev::Secret::size == 32, "Secret key must be 32 bytes."); static_assert(dev::Public::size == 64, "Public key must be 64 bytes."); static_assert(dev::Signature::size == 65, "Signature must be 65 bytes."); -bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) +bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) { // interop w/go ecies implementation - if (!_s1.size()) - { - _s1.resize(1); - asserts(_s1[0] == 0); - } - - // for sha3, hash.blocksize is 1088 bits, but this might really be digest size - auto reps = ((kdBitLen + 7) * 8) / (32 * 8); + // for sha3, blocksize is 136 bytes + // for sha256, blocksize is 64 bytes + auto reps = ((kdByteLen + 7) * 8) / (64 * 8); bytes ctr({0, 0, 0, 1}); bytes k; CryptoPP::SHA256 ctx; - while (reps--) + for (unsigned i = 0; i <= reps; i++) { ctx.Update(ctr.data(), ctr.size()); ctx.Update(_z.data(), Secret::size); @@ -70,7 +65,7 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen) ctr[0]++; } - k.resize(kdBitLen / 8); + k.resize(kdByteLen); return move(k); } @@ -83,7 +78,9 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) auto key = eciesKDF(z, bytes(), 512); bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); bytesRef mKey = bytesRef(&key).cropped(32, 32); - sha3(mKey, mKey); + CryptoPP::SHA256 ctx; + ctx.Update(mKey.data(), mKey.size()); + ctx.Final(mKey.data()); bytes cipherText; encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); @@ -97,10 +94,10 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) bytesConstRef(&cipherText).copyTo(msgCipherRef); // tag message - CryptoPP::HMAC ctx(mKey.data(), mKey.size()); + CryptoPP::HMAC hmacctx(mKey.data(), mKey.size()); bytesConstRef cipherWithIV = bytesRef(&msg).cropped(1 + Public::size, h128::size + cipherText.size()); - ctx.Update(cipherWithIV.data(), cipherWithIV.size()); - ctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); + hmacctx.Update(cipherWithIV.data(), cipherWithIV.size()); + hmacctx.Final(msg.data() + 1 + Public::size + cipherWithIV.size()); io_cipher.resize(msg.size()); io_cipher.swap(msg); @@ -121,27 +118,31 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) h256 z; ecdh::agree(_k, *(Public*)(io_text.data()+1), z); - auto key = eciesKDF(z, bytes(), 512); + auto key = eciesKDF(z, bytes(), 64); bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); - bytesRef mKey = bytesRef(&key).cropped(32, 32); - sha3(mKey, mKey); + bytesRef mKey = bytesRef(&key).cropped(16, 16); + CryptoPP::SHA256 ctx; + ctx.Update(mKey.data(), mKey.size()); + ctx.Final(mKey.data()); bytes plain; size_t cipherLen = io_text.size() - 1 - Public::size - h128::size - h256::size; bytesConstRef cipherWithIV(io_text.data() + 1 + Public::size, h128::size + cipherLen); - bytesConstRef cipher = cipherWithIV.cropped(h128::size, cipherLen); - bytesConstRef msgMac(cipher.data() + cipher.size(), h256::size); + bytesConstRef cipherIV = cipherWithIV.cropped(0, h128::size); + bytesConstRef cipherNoIV = cipherWithIV.cropped(h128::size, cipherLen); + bytesConstRef msgMac(cipherNoIV.data() + cipherLen, h256::size); + h128 iv(cipherIV.toBytes()); // verify tag - CryptoPP::HMAC ctx(mKey.data(), mKey.size()); - ctx.Update(cipherWithIV.data(), cipherWithIV.size()); + CryptoPP::HMAC hmacctx(mKey.data(), mKey.size()); + hmacctx.Update(cipherWithIV.data(), cipherWithIV.size()); h256 mac; - ctx.Final(mac.data()); + hmacctx.Final(mac.data()); for (unsigned i = 0; i < h256::size; i++) if (mac[i] != msgMac[i]) - return false; + 0; - decryptSymNoAuth(*(Secret*)eKey.data(), h128(), cipher, plain); + decryptSymNoAuth(*(Secret*)eKey.data(), iv, cipherNoIV, plain); io_text.resize(plain.size()); io_text.swap(plain); diff --git a/test/crypto.cpp b/test/crypto.cpp index 56fb8a51c..5673dd530 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -228,6 +228,44 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(ecies_interop_test) +{ + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); + bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); + bytes test1; + test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); + BOOST_REQUIRE(test1 == expect1); + + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); + Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); + Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); + Secret agreeTest; + s_secp256k1.agree(k.sec(), p, agreeTest); + BOOST_REQUIRE(agreeExpected == agreeTest); + + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); + Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); + BOOST_REQUIRE(penc == kenc.pub()); + + bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); + bytes plainTest1 = cipher1; + bytes expectedPlain1 = asBytes("a"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); + BOOST_REQUIRE(plainTest1 == expectedPlain1); + + bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); + bytes plainTest2 = cipher2; + bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); + BOOST_REQUIRE(plainTest2 == expectedPlain2); + + bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); + bytes plainTest3 = cipher3; + bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); + BOOST_REQUIRE(plainTest3 == expectedPlain3); +} + BOOST_AUTO_TEST_CASE(ecies_kdf) { KeyPair local = KeyPair::create(); @@ -552,16 +590,6 @@ BOOST_AUTO_TEST_CASE(handshakeNew) -} - -BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) -{ - // New connections require new ECDH keypairs - // Every new connection requires a new EC keypair - // Every new trust requires a new EC keypair - // All connections should share seed for PRF (or PRNG) for nonces - - } BOOST_AUTO_TEST_CASE(ecies_aes128_ctr_unaligned) From 8e2deaebc2a1b8a197b20ed2c16b745b83ab0e52 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 13:01:47 +0100 Subject: [PATCH 19/50] ECIES interop with go is a go. --- libdevcrypto/CryptoPP.cpp | 7 +++--- test/crypto.cpp | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 82d426a76..cf758fb42 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -120,9 +120,10 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) ecdh::agree(_k, *(Public*)(io_text.data()+1), z); auto key = eciesKDF(z, bytes(), 64); bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); - bytesRef mKey = bytesRef(&key).cropped(16, 16); + bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); + bytes mKey(32); CryptoPP::SHA256 ctx; - ctx.Update(mKey.data(), mKey.size()); + ctx.Update(mKeyMaterial.data(), mKeyMaterial.size()); ctx.Final(mKey.data()); bytes plain; @@ -140,7 +141,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) hmacctx.Final(mac.data()); for (unsigned i = 0; i < h256::size; i++) if (mac[i] != msgMac[i]) - 0; + return false; decryptSymNoAuth(*(Secret*)eKey.data(), iv, cipherNoIV, plain); io_text.resize(plain.size()); diff --git a/test/crypto.cpp b/test/crypto.cpp index 5673dd530..4a9a9dc80 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -230,12 +230,52 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) BOOST_AUTO_TEST_CASE(ecies_interop_test) { + CryptoPP::SHA256 sha256ctx; + bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + bytes empty; + sha256ctx.Update(empty.data(), 0); + bytes emptyTestOut(32); + sha256ctx.Final(emptyTestOut.data()); + BOOST_REQUIRE(emptyExpected == emptyTestOut); + + bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); + bytes hash1input(fromHex("0x55a53b55afb12affff3c")); + sha256ctx.Update(hash1input.data(), hash1input.size()); + bytes hash1Out(32); + sha256ctx.Final(hash1Out.data()); + BOOST_REQUIRE(hash1Out == hash1Expected); + + h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); + CryptoPP::HMAC hmacctx(hmack.data(), h128::size); + bytes input(fromHex("0x4dcb92ed4fc67fe86832")); + hmacctx.Update(input.data(), input.size()); + bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); + bytes hmacOut(hmacExpected.size()); + hmacctx.Final(hmacOut.data()); + BOOST_REQUIRE(hmacExpected == hmacOut); + + // go messageTag + bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); + bytes tagInput(fromHex("0x3461282bcedace970df2")); + bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); + CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); + hmactagctx.Update(tagInput.data(), tagInput.size()); + h256 mac; + hmactagctx.Final(mac.data()); + BOOST_REQUIRE(mac.asBytes() == tagExpected); + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); bytes test1; test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); BOOST_REQUIRE(test1 == expect1); + Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); + bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); + bytes kdfTest2; + kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); + BOOST_REQUIRE(kdfTest2 == kdfExpect2); + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); @@ -243,6 +283,13 @@ BOOST_AUTO_TEST_CASE(ecies_interop_test) s_secp256k1.agree(k.sec(), p, agreeTest); BOOST_REQUIRE(agreeExpected == agreeTest); + KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); + bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); + bytes kmPlain = kmCipher; + bytes kmExpected(asBytes("a")); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); + BOOST_REQUIRE(kmExpected == kmPlain); + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); BOOST_REQUIRE(penc == kenc.pub()); From b0aa7ce8ef45daeb3b931a7536063a551f382a6a Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 14:01:21 +0100 Subject: [PATCH 20/50] update encrypt to use same keying as decrypt --- libdevcrypto/CryptoPP.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index cf758fb42..356317935 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -75,11 +75,12 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) auto r = KeyPair::create(); h256 z; ecdh::agree(r.sec(), _k, z); - auto key = eciesKDF(z, bytes(), 512); - bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); - bytesRef mKey = bytesRef(&key).cropped(32, 32); + auto key = eciesKDF(z, bytes(), 32); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16); + bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); CryptoPP::SHA256 ctx; - ctx.Update(mKey.data(), mKey.size()); + ctx.Update(mKeyMaterial.data(), mKeyMaterial.size()); + bytes mKey(32); ctx.Final(mKey.data()); bytes cipherText; @@ -119,7 +120,7 @@ bool Secp256k1::decryptECIES(Secret const& _k, bytes& io_text) h256 z; ecdh::agree(_k, *(Public*)(io_text.data()+1), z); auto key = eciesKDF(z, bytes(), 64); - bytesConstRef eKey = bytesConstRef(&key).cropped(0, 32); + bytesConstRef eKey = bytesConstRef(&key).cropped(0, 16); bytesRef mKeyMaterial = bytesRef(&key).cropped(16, 16); bytes mKey(32); CryptoPP::SHA256 ctx; From e79d427902cda380e233a2ee0ecbe76fad6413ba Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 20:56:05 +0100 Subject: [PATCH 21/50] update host for ecies interop. test sha3 output of interim digest. --- libp2p/Host.cpp | 16 ++++++++++++---- test/crypto.cpp | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 67a701eb3..b0bf097fb 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -419,8 +419,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.connect.ingress recving auth"; // ingress: rx auth - authCipher.resize(321); - ba::async_read(*socket, ba::buffer(authCipher, 321), [this, self](boost::system::error_code ec, std::size_t) + authCipher.resize(307); + ba::async_read(*socket, ba::buffer(authCipher, 307), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -457,8 +457,8 @@ void PeerHandshake::transition(boost::system::error_code _ech) { clog(NetConnect) << "devp2p.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) + ackCipher.resize(210); + ba::async_read(*socket, ba::buffer(ackCipher, 210), [this, self](boost::system::error_code ec, std::size_t) { if (ec) transition(ec); @@ -532,14 +532,22 @@ void PeerHandshake::transition(boost::system::error_code _ech) bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); k->ingressMac = sha3(keyMaterial); + + + // 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) << "devp2p.connect.egress txrx magic sequence"; k->recvdMagicCipherAndMac.resize(k->magicCipherAndMac.size()); diff --git a/test/crypto.cpp b/test/crypto.cpp index 4a9a9dc80..ebe8db6c5 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -228,6 +228,42 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } +BOOST_AUTO_TEST_CASE(rlpx_sha3_norestart) +{ + CryptoPP::SHA3_256 ctx; + bytes input(asBytes("test")); + ctx.Update(input.data(), 4); + CryptoPP::SHA3_256 ctxCopy(ctx); + bytes interimDigest(32); + ctx.Final(interimDigest.data()); + ctx.Update(input.data(), 4); + bytes firstDigest(32); + ctx.Final(firstDigest.data()); + BOOST_REQUIRE(interimDigest == firstDigest); + + ctxCopy.Update(input.data(), 4); + bytes finalDigest(32); + ctxCopy.Final(interimDigest.data()); + BOOST_REQUIRE(interimDigest != finalDigest); + + // we can do this another way -- copy the context for final + ctxCopy.Update(input.data(), 4); + ctxCopy.Update(input.data(), 4); + CryptoPP::SHA3_256 finalCtx(ctxCopy); + bytes finalDigest2(32); + finalCtx.Final(finalDigest2.data()); + BOOST_REQUIRE(finalDigest2 == interimDigest); + ctxCopy.Update(input.data(), 4); + bytes finalDigest3(32); + finalCtx.Final(finalDigest3.data()); + BOOST_REQUIRE(finalDigest2 != finalDigest3); +} + +BOOST_AUTO_TEST_CASE(rlpx_updatemac_aesecb_sha3) +{ + +} + BOOST_AUTO_TEST_CASE(ecies_interop_test) { CryptoPP::SHA256 sha256ctx; From 69e48c2707733418060826c1399eaf17ae46e6ae Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 21:24:31 +0100 Subject: [PATCH 22/50] initial rlpx framing classes --- libp2p/Host.cpp | 3 + libp2p/RLPxHandshake.cpp | 390 +++++++++++++++++++++++++++++++++++++++ libp2p/RLPxHandshake.h | 146 +++++++++++++++ 3 files changed, 539 insertions(+) create mode 100644 libp2p/RLPxHandshake.cpp create mode 100644 libp2p/RLPxHandshake.h diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index b0bf097fb..0062722c6 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -39,6 +39,8 @@ using namespace std; using namespace dev; using namespace dev::p2p; +#include "RLPxHandshake.h" + HostNodeTableHandler::HostNodeTableHandler(Host& _host): m_host(_host) {} void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -501,6 +503,7 @@ void PeerHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "devp2p.connect.egress sending magic sequence"; else clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; + PeerSecrets* k = new PeerSecrets; bytes keyMaterialBytes(512); bytesRef keyMaterial(&keyMaterialBytes); diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp new file mode 100644 index 000000000..37b75f1c9 --- /dev/null +++ b/libp2p/RLPxHandshake.cpp @@ -0,0 +1,390 @@ +/* + 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 RLPXHandshake.cpp + * @author Alex Leverington + * @date 2015 + */ + +#include "Host.h" +#include "Session.h" +#include "Peer.h" +#include "PeerHandshake.h" +using namespace std; +using namespace dev; +using namespace dev::p2p; +using namespace CryptoPP; + +RLPXFrameIO::RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher): m_keys(h128(), h128()), m_macUpdateEncryptor(sha3("test").data(), 16) +{ + // we need: + // originated? + // Secret == output of ecdhe agreement + // authCipher + // ackCipher + + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + +// ecdhe.agree(remoteEphemeral, ess); + _ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); +// ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// // auto token = sha3(ssA); +// k->encryptK = sha3(keyMaterial); +// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); +// k->macK = sha3(keyMaterial); +// +// // 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) +// +// bytes const& egressCipher = _originated ? authCipher : ackCipher; +// keyMaterialBytes.resize(h256::size + egressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); +// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); +// k->egressMac = sha3(keyMaterial); +// +// bytes const& ingressCipher = _originated ? ackCipher : authCipher; +// keyMaterialBytes.resize(h256::size + ingressCipher.size()); +// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); +// (k->macK ^ nonce).ref().copyTo(keyMaterial); +// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); +// k->ingressMac = sha3(keyMaterial); + +} + +void RLPXFrameIO::writeFullPacketFrame(bytesConstRef _packet) +{ + +} + +void RLPXFrameIO::writeHeader(bi::tcp::socket* _socket, h128 const& _header) +{ + +} + +void RLPXFrameIO::write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof) +{ + +} + +bool RLPXFrameIO::read(bytesConstRef _in, bytes& o_out) +{ + +} + +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(h128 const& _headerCipher) +{ + m_egressMac.Update(_headerCipher.data(), h128::size); + updateMAC(m_egressMac); +} + +void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) +{ + m_egressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_egressMac); +} + +void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) +{ + m_ingressMac.Update(_headerCipher.data(), h128::size); + updateMAC(m_ingressMac); +} + +void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) +{ + m_ingressMac.Update(_cipher.data(), _cipher.size()); + updateMAC(m_ingressMac); +} + +void RLPXFrameIO::updateMAC(SHA3_256& _mac) +{ + SHA3_256 prevDigest(_mac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + + h128 encDigest; + m_macUpdateEncryptor.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= prevDigestOut; + + // update mac for final digest + _mac.Update(encDigest.data(), h256::size); +} + + +void RLPXHandshake::generateAuth() +{ + auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); + bytesRef sig(&auth[0], Signature::size); + bytesRef hepubk(&auth[Signature::size], h256::size); + bytesRef pubk(&auth[Signature::size + h256::size], Public::size); + 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); + 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); +} + +void RLPXHandshake::generateAck() +{ + +} + +bool RLPXHandshake::decodeAuth() +{ + 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; +} + +bool RLPXHandshake::decodeAck() +{ + +} + +/// used for protocol handshake +bytes RLPXHandshake::frame(bytesConstRef _packet) +{ + +} + +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; + } + + auto self(shared_from_this()); + if (nextState == New) + { + nextState = AckAuth; + + clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); + + 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); + }); + } + else + { + 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 (decodeAuth()) + transition(); + else + { + 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; + } + + bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); + bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); + transition(); + } + }); + } + 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"; + PeerSecrets* k = new PeerSecrets; + bytes keyMaterialBytes(512); + bytesRef keyMaterial(&keyMaterialBytes); + + ecdhe.agree(remoteEphemeral, ess); + ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); + ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + // auto token = sha3(ssA); + k->encryptK = sha3(keyMaterial); + k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); + k->macK = sha3(keyMaterial); + + // 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) + + bytes const& egressCipher = originated ? authCipher : ackCipher; + keyMaterialBytes.resize(h256::size + egressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); + bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); + k->egressMac = sha3(keyMaterial); + + bytes const& ingressCipher = originated ? ackCipher : authCipher; + keyMaterialBytes.resize(h256::size + ingressCipher.size()); + keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); + (k->macK ^ nonce).ref().copyTo(keyMaterial); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + k->ingressMac = sha3(keyMaterial); + + // 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 p; + p = host->m_peers[remote]; + if (!p) + { + p.reset(new Peer()); + p->id = remote; + } + 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(host, move(*socket), p); + ps->start(); + } + + // todo: PeerSession will take ownership of k and use it to encrypt wireline. + delete k; + }); + }); + } +} diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h new file mode 100644 index 000000000..e0be14f55 --- /dev/null +++ b/libp2p/RLPxHandshake.h @@ -0,0 +1,146 @@ +/* + 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 RLPXHandshake.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 RLPXFrameIO +{ +public: + RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher); + + void writeFullPacketFrame(bytesConstRef _packet); + + void writeHeader(bi::tcp::socket* _socket, h128 const& _header); + + void write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof = false); + + bool read(bytesConstRef _in, bytes& o_out); + + h128 egressDigest(); + + h128 ingressDigest(); + + void updateEgressMACWithHeader(h128 const& _headerCipher); + + void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); + + void updateIngressMACWithHeader(bytesConstRef _headerCipher); + + void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); + +private: + struct RLPXSecrets + { + // ideally this will be passed ecdhe-shared-secret, auth cipher, ack cpiher + RLPXSecrets(h128 const& encK, h128 const& macK): frameEnc(encK.data(), h128::size), macEnc(macK.data(), h128::size) {} + + CryptoPP::CTR_Mode::Encryption frameEnc; + CryptoPP::ECB_Mode::Encryption macEnc; + + CryptoPP::SHA3_256 egressMac; + CryptoPP::SHA3_256 ingressMac; + }; + + void updateMAC(CryptoPP::SHA3_256& _mac); + + CryptoPP::ECB_Mode::Encryption m_macUpdateEncryptor; + CryptoPP::SHA3_256 m_egressMac; + CryptoPP::SHA3_256 m_ingressMac; + + RLPXSecrets m_keys; +}; + +struct RLPXHandshake: public std::enable_shared_from_this +{ + friend class Host; + enum State + { + Error = -1, + New, // New->AckAuth [egress: tx auth, ingress: rx auth] + AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] + Authenticating, // Authenticating [tx caps, rx caps, authenticate] + }; + + /// Handshake for ingress connection. Takes ownership of socket. + RLPXHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + /// Handshake for egress connection to _remote. Takes ownership of socket. + RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), remote(_remote), socket(std::move(_socket)), originated(true) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + + ~RLPXHandshake() { delete socket; } + +protected: + void start() { transition(); } + + void generateAuth(); + bool decodeAuth(); + + void generateAck(); + bool decodeAck(); + + bytes frame(bytesConstRef _packet); + +private: + void transition(boost::system::error_code _ech = boost::system::error_code()); + + /// Current state of handshake. + State nextState = New; + + Host* host; + + /// Node id of remote host for socket. + NodeId remote; + + bi::tcp::socket* socket; + bool originated = false; + + /// Buffers for encoded and decoded handshake phases + bytes auth; + bytes authCipher; + bytes ack; + bytes ackCipher; + + Secret ss; + Secret ess; + + crypto::ECDHE ecdhe; + h256 nonce; + + Public remoteEphemeral; + h256 remoteNonce; +}; + +} +} \ No newline at end of file From 924ac4ec92ba6685f0d0022b071cc95fc98265aa Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 23:24:42 +0100 Subject: [PATCH 23/50] pair-coding update --- libp2p/RLPxHandshake.cpp | 10 +++++----- libp2p/RLPxHandshake.h | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 37b75f1c9..de4e9b90f 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -22,7 +22,7 @@ #include "Host.h" #include "Session.h" #include "Peer.h" -#include "PeerHandshake.h" +#include "RLPxHandshake.h" using namespace std; using namespace dev; using namespace dev::p2p; @@ -107,7 +107,7 @@ h128 RLPXFrameIO::ingressDigest() void RLPXFrameIO::updateEgressMACWithHeader(h128 const& _headerCipher) { m_egressMac.Update(_headerCipher.data(), h128::size); - updateMAC(m_egressMac); + updateMAC(m_egressMac, *(h128*)_headerCipher.data()); } void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) @@ -119,7 +119,7 @@ void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) { m_ingressMac.Update(_headerCipher.data(), h128::size); - updateMAC(m_ingressMac); + updateMAC(m_ingressMac, *(h128*)_headerCipher.data()); } void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) @@ -128,7 +128,7 @@ void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) updateMAC(m_ingressMac); } -void RLPXFrameIO::updateMAC(SHA3_256& _mac) +void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) { SHA3_256 prevDigest(_mac); h128 prevDigestOut; @@ -136,7 +136,7 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac) h128 encDigest; m_macUpdateEncryptor.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); - encDigest ^= prevDigestOut; + encDigest ^= (!!_seed ? _seed : prevDigestOut); // update mac for final digest _mac.Update(encDigest.data(), h256::size); diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index e0be14f55..0e39dc810 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -73,7 +73,7 @@ private: CryptoPP::SHA3_256 ingressMac; }; - void updateMAC(CryptoPP::SHA3_256& _mac); + void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); CryptoPP::ECB_Mode::Encryption m_macUpdateEncryptor; CryptoPP::SHA3_256 m_egressMac; From dc1bb1d28b3e695b81f4c018ac232af474d43030 Mon Sep 17 00:00:00 2001 From: subtly Date: Tue, 3 Mar 2015 23:30:25 +0100 Subject: [PATCH 24/50] build fix --- libp2p/Host.h | 9 +++++++-- libp2p/Peer.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index 86f682a1a..c34ec9f21 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -48,7 +48,7 @@ namespace dev namespace p2p { - + class Host; class HostNodeTableHandler: public NodeTableEventHandler @@ -63,7 +63,7 @@ private: Host& m_host; }; - + /** * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. @@ -77,7 +77,10 @@ private: class Host: public Worker { friend class HostNodeTableHandler; + friend struct PeerHandshake; + friend struct RLPXHandshake; + friend class Session; friend class HostCapabilityFace; @@ -237,6 +240,8 @@ private: struct PeerSecrets { friend struct PeerHandshake; + friend struct RLPXHandshake; + protected: Secret encryptK; Secret macK; diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 5c1aa015d..e9b45845b 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -54,7 +54,9 @@ class Peer: public Node { friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() + friend struct PeerHandshake; + friend struct RLPXHandshake; public: bool isOffline() const { return !m_session.lock(); } From 53fda56158d2063b65ab8354048a6fe1a170ba4a Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 00:22:35 +0100 Subject: [PATCH 25/50] test initial secrets and ciphers, interop with go --- libdevcrypto/ECDHE.cpp | 2 +- libdevcrypto/ECDHE.h | 6 +- libp2p/RLPxHandshake.cpp | 198 +++++++++---------- libp2p/RLPxHandshake.h | 29 +-- test/crypto.cpp | 93 +-------- test/rlpx.cpp | 417 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 524 insertions(+), 221 deletions(-) create mode 100644 test/rlpx.cpp diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index 8f667f0f9..a00a92872 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -34,7 +34,7 @@ void dev::crypto::ecdh::agree(Secret const& _s, Public const& _r, h256& o_s) s_secp256k1.agree(_s, _r, o_s); } -void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) +void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) const { if (m_remoteEphemeral) // agreement can only occur once diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 6d01b0329..d3c9ae325 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -70,11 +70,11 @@ public: Secret seckey() { return m_ephemeral.sec(); } /// Input public key for dh agreement, output generated shared secret. - void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret); + void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret) const; protected: - KeyPair m_ephemeral; ///< Ephemeral keypair; generated. - Public m_remoteEphemeral; ///< Public key of remote; parameter. + KeyPair m_ephemeral; ///< Ephemeral keypair; generated. + mutable Public m_remoteEphemeral; ///< Public key of remote; parameter. Set once when agree is called, otherwise immutable. }; /** diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index de4e9b90f..18db43577 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -28,7 +28,7 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher): m_keys(h128(), h128()), m_macUpdateEncryptor(sha3("test").data(), 16) +RLPXFrameIO::RLPXFrameIO(RLPXHandshake& _init): m_macEnc() { // we need: // originated? @@ -39,37 +39,53 @@ RLPXFrameIO::RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytes bytes keyMaterialBytes(512); bytesRef keyMaterial(&keyMaterialBytes); -// ecdhe.agree(remoteEphemeral, ess); - _ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); -// ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// // auto token = sha3(ssA); -// k->encryptK = sha3(keyMaterial); -// k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); -// k->macK = sha3(keyMaterial); -// -// // 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) -// -// bytes const& egressCipher = _originated ? authCipher : ackCipher; -// keyMaterialBytes.resize(h256::size + egressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); -// bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); -// k->egressMac = sha3(keyMaterial); -// -// bytes const& ingressCipher = _originated ? ackCipher : authCipher; -// keyMaterialBytes.resize(h256::size + ingressCipher.size()); -// keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); -// (k->macK ^ nonce).ref().copyTo(keyMaterial); -// bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); -// k->ingressMac = sha3(keyMaterial); + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + _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; + 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) + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + m_frameEnc.SetKey(outRef.data(), h256::size); + // mac-secret = sha3(ecdhe-shared-secret || aes-secret) + sha3(keyMaterial, outRef); // output mac-secret + m_macEnc.SetKey(outRef.data(), 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() ^ _init.remoteNonce).ref().copyTo(keyMaterial); + bytes const& egressCipher = _init.originated ? _init.authCipher : _init.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*)outRef.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()); + bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); + m_ingressMac.Update(keyMaterial.data(), keyMaterial.size()); } void RLPXFrameIO::writeFullPacketFrame(bytesConstRef _packet) { + RLPStream header; } @@ -135,7 +151,7 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); h128 encDigest; - m_macUpdateEncryptor.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); encDigest ^= (!!_seed ? _seed : prevDigestOut); // update mac for final digest @@ -152,8 +168,8 @@ 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); +//crypto::ecdh::agree(host->m_alias.sec(), remote, ss); +//sign(ecdhe.seckey(), ss ^ this->nonce).ref().copyTo(sig); sha3(ecdhe.pubkey().ref(), hepubk); host->m_alias.pub().ref().copyTo(pubk); this->nonce.ref().copyTo(nonce); @@ -178,8 +194,8 @@ bool RLPXHandshake::decodeAuth() pubk.copyTo(remote.ref()); nonce.copyTo(remoteNonce.ref()); - crypto::ecdh::agree(host->m_alias.sec(), remote, ss); - remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); +//crypto::ecdh::agree(host->m_alias.sec(), remote, ss); +//remoteEphemeral = recover(*(Signature*)sig.data(), ss ^ remoteNonce); assert(sha3(remoteEphemeral) == *(h256*)hepubk.data()); return true; } @@ -295,77 +311,49 @@ void RLPXHandshake::transition(boost::system::error_code _ech) clog(NetConnect) << "p2p.connect.egress sending magic sequence"; else clog(NetConnect) << "p2p.connect.ingress sending magic sequence"; - PeerSecrets* k = new PeerSecrets; - bytes keyMaterialBytes(512); - bytesRef keyMaterial(&keyMaterialBytes); - - ecdhe.agree(remoteEphemeral, ess); - ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - // auto token = sha3(ssA); - k->encryptK = sha3(keyMaterial); - k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - k->macK = sha3(keyMaterial); - - // 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) - bytes const& egressCipher = originated ? authCipher : ackCipher; - keyMaterialBytes.resize(h256::size + egressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); - bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); - k->egressMac = sha3(keyMaterial); + RLPXFrameIO* io = new RLPXFrameIO(*this); - bytes const& ingressCipher = originated ? ackCipher : authCipher; - keyMaterialBytes.resize(h256::size + ingressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ nonce).ref().copyTo(keyMaterial); - bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); - k->ingressMac = sha3(keyMaterial); - - // 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) - { +// // 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 p; p = host->m_peers[remote]; if (!p) @@ -380,11 +368,11 @@ void RLPXHandshake::transition(boost::system::error_code _ech) auto ps = std::make_shared(host, move(*socket), p); ps->start(); - } +// } // todo: PeerSession will take ownership of k and use it to encrypt wireline. - delete k; - }); - }); + delete io; +// }); +// }); } } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 0e39dc810..a0b739660 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -34,11 +34,13 @@ namespace dev { namespace p2p { - + +class RLPXHandshake; + class RLPXFrameIO { public: - RLPXFrameIO(bool _originated, Secret const& _ephemeralShared, bytesConstRef _authCipher, bytesConstRef _ackCipher); + RLPXFrameIO(RLPXHandshake& _init); void writeFullPacketFrame(bytesConstRef _packet); @@ -61,29 +63,17 @@ public: void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); private: - struct RLPXSecrets - { - // ideally this will be passed ecdhe-shared-secret, auth cipher, ack cpiher - RLPXSecrets(h128 const& encK, h128 const& macK): frameEnc(encK.data(), h128::size), macEnc(macK.data(), h128::size) {} - - CryptoPP::CTR_Mode::Encryption frameEnc; - CryptoPP::ECB_Mode::Encryption macEnc; - - CryptoPP::SHA3_256 egressMac; - CryptoPP::SHA3_256 ingressMac; - }; - void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); - CryptoPP::ECB_Mode::Encryption m_macUpdateEncryptor; + CryptoPP::CTR_Mode::Encryption m_frameEnc; + CryptoPP::ECB_Mode::Encryption m_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; - - RLPXSecrets m_keys; }; struct RLPXHandshake: public std::enable_shared_from_this { + friend class RLPXFrameIO; friend class Host; enum State { @@ -131,10 +121,7 @@ private: bytes authCipher; bytes ack; bytes ackCipher; - - Secret ss; - Secret ess; - + crypto::ECDHE ecdhe; h256 nonce; diff --git a/test/crypto.cpp b/test/crypto.cpp index ebe8db6c5..c20b4c51f 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -15,6 +15,7 @@ along with cpp-ethereum. If not, see . */ /** @file crypto.cpp + * @author Alex Leverington * @author Gav Wood * @date 2014 * Crypto test functions. @@ -228,7 +229,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1) } } -BOOST_AUTO_TEST_CASE(rlpx_sha3_norestart) +BOOST_AUTO_TEST_CASE(sha3_norestart) { CryptoPP::SHA3_256 ctx; bytes input(asBytes("test")); @@ -259,96 +260,6 @@ BOOST_AUTO_TEST_CASE(rlpx_sha3_norestart) BOOST_REQUIRE(finalDigest2 != finalDigest3); } -BOOST_AUTO_TEST_CASE(rlpx_updatemac_aesecb_sha3) -{ - -} - -BOOST_AUTO_TEST_CASE(ecies_interop_test) -{ - CryptoPP::SHA256 sha256ctx; - bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); - bytes empty; - sha256ctx.Update(empty.data(), 0); - bytes emptyTestOut(32); - sha256ctx.Final(emptyTestOut.data()); - BOOST_REQUIRE(emptyExpected == emptyTestOut); - - bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); - bytes hash1input(fromHex("0x55a53b55afb12affff3c")); - sha256ctx.Update(hash1input.data(), hash1input.size()); - bytes hash1Out(32); - sha256ctx.Final(hash1Out.data()); - BOOST_REQUIRE(hash1Out == hash1Expected); - - h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); - CryptoPP::HMAC hmacctx(hmack.data(), h128::size); - bytes input(fromHex("0x4dcb92ed4fc67fe86832")); - hmacctx.Update(input.data(), input.size()); - bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); - bytes hmacOut(hmacExpected.size()); - hmacctx.Final(hmacOut.data()); - BOOST_REQUIRE(hmacExpected == hmacOut); - - // go messageTag - bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); - bytes tagInput(fromHex("0x3461282bcedace970df2")); - bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); - CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); - hmactagctx.Update(tagInput.data(), tagInput.size()); - h256 mac; - hmactagctx.Final(mac.data()); - BOOST_REQUIRE(mac.asBytes() == tagExpected); - - Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); - bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); - bytes test1; - test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); - BOOST_REQUIRE(test1 == expect1); - - Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); - bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); - bytes kdfTest2; - kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); - BOOST_REQUIRE(kdfTest2 == kdfExpect2); - - KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); - Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); - Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); - Secret agreeTest; - s_secp256k1.agree(k.sec(), p, agreeTest); - BOOST_REQUIRE(agreeExpected == agreeTest); - - KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); - bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); - bytes kmPlain = kmCipher; - bytes kmExpected(asBytes("a")); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); - BOOST_REQUIRE(kmExpected == kmPlain); - - KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); - Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); - BOOST_REQUIRE(penc == kenc.pub()); - - bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); - bytes plainTest1 = cipher1; - bytes expectedPlain1 = asBytes("a"); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); - BOOST_REQUIRE(plainTest1 == expectedPlain1); - - bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); - bytes plainTest2 = cipher2; - bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); - BOOST_REQUIRE(plainTest2 == expectedPlain2); - - bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); - bytes plainTest3 = cipher3; - bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); - BOOST_REQUIRE(plainTest3 == expectedPlain3); -} - BOOST_AUTO_TEST_CASE(ecies_kdf) { KeyPair local = KeyPair::create(); diff --git a/test/rlpx.cpp b/test/rlpx.cpp new file mode 100644 index 000000000..d845911f4 --- /dev/null +++ b/test/rlpx.cpp @@ -0,0 +1,417 @@ +/* + 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 crypto.cpp + * @author Alex Leverington + * @date 2015 + * RLPx test functions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace dev; +using namespace dev::crypto; +using namespace CryptoPP; + +BOOST_AUTO_TEST_SUITE(rlpx) + +static Secp256k1 s_secp256k1; +static CryptoPP::AutoSeededRandomPool s_rng; +static CryptoPP::OID s_curveOID(CryptoPP::ASN1::secp256k1()); +static CryptoPP::DL_GroupParameters_EC s_params(s_curveOID); +static CryptoPP::DL_GroupParameters_EC::EllipticCurve s_curve(s_params.GetCurve()); + +BOOST_AUTO_TEST_CASE(test_secrets_cpp_vectors) +{ + KeyPair init(Secret(sha3("initiator"))); + KeyPair initR(Secret(sha3("initiator-random"))); + h256 initNonce(sha3("initiator-nonce")); + + KeyPair recv(Secret(sha3("remote-recv"))); + KeyPair recvR(Secret(sha3("remote-recv-random"))); + h256 recvNonce(sha3("remote-recv-nonce")); + + bytes authCipher(fromHex("")); + bytes ackCipher(fromHex("")); + + 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; + + // when originated is true, agreement is with init secrets + // when originated is true, remoteNonce = recvNonce + // when originated is true, nonce = initNonce + bool originated = true; + auto remoteNonce = recvNonce; + auto nonce = initNonce; + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + s_secp256k1.agree(initR.sec(), recvR.pub(), ephemeralShared); + Secret expected(fromHex("20d82c1092f351dc217bd66fa183e801234af14ead40423b6ee25112201c6e5a")); + BOOST_REQUIRE(expected == 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) + + // test that keyMaterial = ecdhe-shared-secret || sha3(nonce || initiator-nonce) + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(leftNonce.data(), h256::size); + ctx.Update(rightNonce.data(), h256::size); + bytes expected(32); + ctx.Final(expected.data()); + bytes given(32); + outRef.copyTo(&given); + BOOST_REQUIRE(expected == given); + } + bytes preImage(keyMaterialBytes); + + // shared-secret <- sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + // keyMaterial = ecdhe-shared-secret || shared-secret + sha3(keyMaterial, outRef); + bytes sharedSecret(32); + outRef.copyTo(&sharedSecret); + BOOST_REQUIRE(sharedSecret == fromHex("b65319ce56e00f3be75c4d0da92b5957d5583ca25eeeedac8e29b6dfc8b1ddf7")); + + // test that keyMaterial = ecdhe-shared-secret || shared-secret + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(preImage.data(), preImage.size()); + bytes expected(32); + ctx.Final(expected.data()); + bytes test(32); + outRef.copyTo(&test); + BOOST_REQUIRE(expected == test); + } + + // token: sha3(outRef) + bytes token(32); + sha3(outRef, bytesRef(&token)); + BOOST_REQUIRE(token == fromHex("db41fe0180f372983cf19fca7ee890f7fb5481079d44683d2c027be9e71bbca2")); + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + bytes aesSecret(32); + outRef.copyTo(&aesSecret); + BOOST_REQUIRE(aesSecret == fromHex("12347b4784bcb4e74b84637940482852fe25d78e328cf5c6f7a396bf96cc20bb")); + 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 + bytes macSecret(32); + outRef.copyTo(&macSecret); + BOOST_REQUIRE(macSecret == fromHex("2ec149072353d54437422837c886b0538a9206e6c559f6b4a55f65a866867723")); + m_macEnc.SetKey(outRef.data(), 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); + bytes const& egressCipher = originated ? authCipher : 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()); + + { + bytes egressMac; + SHA3_256 h(m_egressMac); + bytes digest(16); + h.TruncatedFinal(digest.data(), 16); + BOOST_REQUIRE(digest == fromHex("23e5e8efb6e3765ecae1fca9160b18df")); + } + + // recover mac-secret by re-xoring remoteNonce + (*(h256*)keyMaterial.data() ^ remoteNonce ^ nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = originated ? ackCipher : 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()); + + { + bytes ingressMac; + SHA3_256 h(m_ingressMac); + bytes digest(16); + h.TruncatedFinal(digest.data(), 16); + BOOST_REQUIRE(digest == fromHex("ceed64135852064cbdde86e7ea05e8f5")); + } +} + +BOOST_AUTO_TEST_CASE(test_secrets_from_go) +{ + KeyPair init(Secret(fromHex("0x5e173f6ac3c669587538e7727cf19b782a4f2fda07c1eaa662c593e5e85e3051"))); + KeyPair initR(Secret(fromHex("0x19c2185f4f40634926ebed3af09070ca9e029f2edd5fae6253074896205f5f6c"))); + h256 initNonce(fromHex("0xcd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb11")); + + KeyPair recv(Secret(fromHex("0xc45f950382d542169ea207959ee0220ec1491755abe405cd7498d6b16adb6df8"))); + KeyPair recvR(Secret(fromHex("0xd25688cf0ab10afa1a0e2dba7853ed5f1e5bf1c631757ed4e103b593ff3f5620"))); + h256 recvNonce(fromHex("0xf37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a7")); + + bytes authCipher(fromHex("0x04a0274c5951e32132e7f088c9bdfdc76c9d91f0dc6078e848f8e3361193dbdc43b94351ea3d89e4ff33ddcefbc80070498824857f499656c4f79bbd97b6c51a514251d69fd1785ef8764bd1d262a883f780964cce6a14ff206daf1206aa073a2d35ce2697ebf3514225bef186631b2fd2316a4b7bcdefec8d75a1025ba2c5404a34e7795e1dd4bc01c6113ece07b0df13b69d3ba654a36e35e69ff9d482d88d2f0228e7d96fe11dccbb465a1831c7d4ad3a026924b182fc2bdfe016a6944312021da5cc459713b13b86a686cf34d6fe6615020e4acf26bf0d5b7579ba813e7723eb95b3cef9942f01a58bd61baee7c9bdd438956b426a4ffe238e61746a8c93d5e10680617c82e48d706ac4953f5e1c4c4f7d013c87d34a06626f498f34576dc017fdd3d581e83cfd26cf125b6d2bda1f1d56")); + bytes ackCipher(fromHex("0x049934a7b2d7f9af8fd9db941d9da281ac9381b5740e1f64f7092f3588d4f87f5ce55191a6653e5e80c1c5dd538169aa123e70dc6ffc5af1827e546c0e958e42dad355bcc1fcb9cdf2cf47ff524d2ad98cbf275e661bf4cf00960e74b5956b799771334f426df007350b46049adb21a6e78ab1408d5e6ccde6fb5e69f0f4c92bb9c725c02f99fa72b9cdc8dd53cff089e0e73317f61cc5abf6152513cb7d833f09d2851603919bf0fbe44d79a09245c6e8338eb502083dc84b846f2fee1cc310d2cc8b1b9334728f97220bb799376233e113")); + + bytes authPlainExpected(fromHex("0x884c36f7ae6b406637c1f61b2f57e1d2cab813d24c6559aaf843c3f48962f32f46662c066d39669b7b2e3ba14781477417600e7728399278b1b5d801a519aa570034fdb5419558137e0d44cd13d319afe5629eeccb47fd9dfe55cc6089426e46cc762dd8a0636e07a54b31169eba0c7a20a1ac1ef68596f1f283b5c676bae4064abfcce24799d09f67e392632d3ffdc12e3d6430dcb0ea19c318343ffa7aae74d4cd26fecb93657d1cd9e9eaf4f8be720b56dd1d39f190c4e1c6b7ec66f077bb1100")); + bytes ackPlainExpected(fromHex("0x802b052f8b066640bba94a4fc39d63815c377fced6fcb84d27f791c9921ddf3e9bf0108e298f490812847109cbd778fae393e80323fd643209841a3b7f110397f37ec61d84cea03dcc5e8385db93248584e8af4b4d1c832d8c7453c0089687a700")); + + bytes authPlain = authCipher; + BOOST_REQUIRE(s_secp256k1.decryptECIES(recv.sec(), authPlain)); + bytes ackPlain = ackCipher; + BOOST_REQUIRE(s_secp256k1.decryptECIES(init.sec(), ackPlain)); + + 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; + + // when originated is true, agreement is with init secrets + // when originated is true, remoteNonce = recvNonce + // when originated is true, nonce = initNonce + bool originated = true; + auto remoteNonce = recvNonce; + auto nonce = initNonce; + bytes keyMaterialBytes(64); + bytesRef keyMaterial(&keyMaterialBytes); + + // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + Secret ephemeralShared; + s_secp256k1.agree(initR.sec(), recvR.pub(), ephemeralShared); + Secret expected(fromHex("0xe3f407f83fc012470c26a93fdff534100f2c6f736439ce0ca90e9914f7d1c381")); + BOOST_REQUIRE(expected == 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) + + // test that keyMaterial = ecdhe-shared-secret || sha3(nonce || initiator-nonce) + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(leftNonce.data(), h256::size); + ctx.Update(rightNonce.data(), h256::size); + bytes expected(32); + ctx.Final(expected.data()); + bytes given(32); + outRef.copyTo(&given); + BOOST_REQUIRE(expected == given); + } + bytes preImage(keyMaterialBytes); + + // shared-secret <- sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) + // keyMaterial = ecdhe-shared-secret || shared-secret + sha3(keyMaterial, outRef); + + // test that keyMaterial = ecdhe-shared-secret || shared-secret + { + BOOST_REQUIRE(ephemeralShared == *(Secret*)keyMaterialBytes.data()); + + SHA3_256 ctx; + ctx.Update(preImage.data(), preImage.size()); + bytes expected(32); + ctx.Final(expected.data()); + bytes test(32); + outRef.copyTo(&test); + BOOST_REQUIRE(expected == test); + } + + // token: sha3(outRef) + bytes token(32); + sha3(outRef, bytesRef(&token)); + BOOST_REQUIRE(token == fromHex("0x3f9ec2592d1554852b1f54d228f042ed0a9310ea86d038dc2b401ba8cd7fdac4")); + + // aes-secret = sha3(ecdhe-shared-secret || shared-secret) + sha3(keyMaterial, outRef); // output aes-secret + bytes aesSecret(32); + outRef.copyTo(&aesSecret); + BOOST_REQUIRE(aesSecret == fromHex("0xc0458fa97a5230830e05f4f20b7c755c1d4e54b1ce5cf43260bb191eef4e418d")); + 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 + bytes macSecret(32); + outRef.copyTo(&macSecret); + BOOST_REQUIRE(macSecret == fromHex("0x48c938884d5067a1598272fcddaa4b833cd5e7d92e8228c0ecdfabbe68aef7f1")); + m_macEnc.SetKey(outRef.data(), 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); + bytes const& egressCipher = originated ? authCipher : 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(keyMaterialBytes.data(), keyMaterialBytes.size()); + + { + bytes egressMac; + SHA3_256 h(m_egressMac); + bytes digest(32); + h.Final(digest.data()); + BOOST_REQUIRE(digest == fromHex("0x09771e93b1a6109e97074cbe2d2b0cf3d3878efafe68f53c41bb60c0ec49097e")); + } + + // recover mac-secret by re-xoring remoteNonce + bytes recoverMacSecretTest(32); + (*(h256*)keyMaterial.data() ^ remoteNonce).ref().copyTo(&recoverMacSecretTest); + BOOST_REQUIRE(recoverMacSecretTest == macSecret); + + (*(h256*)keyMaterial.data() ^ remoteNonce ^ nonce).ref().copyTo(keyMaterial); + bytes const& ingressCipher = originated ? ackCipher : 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()); + + { + bytes ingressMac; + SHA3_256 h(m_ingressMac); + bytes digest(32); + h.Final(digest.data()); + BOOST_CHECK(digest == fromHex("0x75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847")); + } + +// bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); +// +// bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); +} + +BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives) +{ + CryptoPP::SHA256 sha256ctx; + bytes emptyExpected(fromHex("0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")); + bytes empty; + sha256ctx.Update(empty.data(), 0); + bytes emptyTestOut(32); + sha256ctx.Final(emptyTestOut.data()); + BOOST_REQUIRE(emptyExpected == emptyTestOut); + + bytes hash1Expected(fromHex("0x8949b278bbafb8da1aaa18cb724175c5952280f74be5d29ab4b37d1b45c84b08")); + bytes hash1input(fromHex("0x55a53b55afb12affff3c")); + sha256ctx.Update(hash1input.data(), hash1input.size()); + bytes hash1Out(32); + sha256ctx.Final(hash1Out.data()); + BOOST_REQUIRE(hash1Out == hash1Expected); + + h128 hmack(fromHex("0x07a4b6dfa06369a570f2dcba2f11a18f")); + CryptoPP::HMAC hmacctx(hmack.data(), h128::size); + bytes input(fromHex("0x4dcb92ed4fc67fe86832")); + hmacctx.Update(input.data(), input.size()); + bytes hmacExpected(fromHex("0xc90b62b1a673b47df8e395e671a68bfa68070d6e2ef039598bb829398b89b9a9")); + bytes hmacOut(hmacExpected.size()); + hmacctx.Final(hmacOut.data()); + BOOST_REQUIRE(hmacExpected == hmacOut); + + // go messageTag + bytes tagSecret(fromHex("0xaf6623e52208c596e17c72cea6f1cb09")); + bytes tagInput(fromHex("0x3461282bcedace970df2")); + bytes tagExpected(fromHex("0xb3ce623bce08d5793677ba9441b22bb34d3e8a7de964206d26589df3e8eb5183")); + CryptoPP::HMAC hmactagctx(tagSecret.data(), tagSecret.size()); + hmactagctx.Update(tagInput.data(), tagInput.size()); + h256 mac; + hmactagctx.Final(mac.data()); + BOOST_REQUIRE(mac.asBytes() == tagExpected); + + Secret input1(fromHex("0x0de72f1223915fa8b8bf45dffef67aef8d89792d116eb61c9a1eb02c422a4663")); + bytes expect1(fromHex("0x1d0c446f9899a3426f2b89a8cb75c14b")); + bytes test1; + test1 = s_secp256k1.eciesKDF(input1, bytes(), 16); + BOOST_REQUIRE(test1 == expect1); + + Secret kdfInput2(fromHex("0x961c065873443014e0371f1ed656c586c6730bf927415757f389d92acf8268df")); + bytes kdfExpect2(fromHex("0x4050c52e6d9c08755e5a818ac66fabe478b825b1836fd5efc4d44e40d04dabcc")); + bytes kdfTest2; + kdfTest2 = s_secp256k1.eciesKDF(kdfInput2, bytes(), 32); + BOOST_REQUIRE(kdfTest2 == kdfExpect2); + + KeyPair k(Secret(fromHex("0x332143e9629eedff7d142d741f896258f5a1bfab54dab2121d3ec5000093d74b"))); + Public p(fromHex("0xf0d2b97981bd0d415a843b5dfe8ab77a30300daab3658c578f2340308a2da1a07f0821367332598b6aa4e180a41e92f4ebbae3518da847f0b1c0bbfe20bcf4e1")); + Secret agreeExpected(fromHex("0xee1418607c2fcfb57fda40380e885a707f49000a5dda056d828b7d9bd1f29a08")); + Secret agreeTest; + s_secp256k1.agree(k.sec(), p, agreeTest); + BOOST_REQUIRE(agreeExpected == agreeTest); + + KeyPair kmK(Secret(fromHex("0x57baf2c62005ddec64c357d96183ebc90bf9100583280e848aa31d683cad73cb"))); + bytes kmCipher(fromHex("0x04ff2c874d0a47917c84eea0b2a4141ca95233720b5c70f81a8415bae1dc7b746b61df7558811c1d6054333907333ef9bb0cc2fbf8b34abb9730d14e0140f4553f4b15d705120af46cf653a1dc5b95b312cf8444714f95a4f7a0425b67fc064d18f4d0a528761565ca02d97faffdac23de10")); + bytes kmPlain = kmCipher; + bytes kmExpected(asBytes("a")); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kmK.sec(), kmPlain)); + BOOST_REQUIRE(kmExpected == kmPlain); + + KeyPair kenc(Secret(fromHex("0x472413e97f1fd58d84e28a559479e6b6902d2e8a0cee672ef38a3a35d263886b"))); + Public penc(Public(fromHex("0x7a2aa2951282279dc1171549a7112b07c38c0d97c0fe2c0ae6c4588ba15be74a04efc4f7da443f6d61f68a9279bc82b73e0cc8d090048e9f87e838ae65dd8d4c"))); + BOOST_REQUIRE(penc == kenc.pub()); + + bytes cipher1(fromHex("0x046f647e1bd8a5cd1446d31513bac233e18bdc28ec0e59d46de453137a72599533f1e97c98154343420d5f16e171e5107999a7c7f1a6e26f57bcb0d2280655d08fb148d36f1d4b28642d3bb4a136f0e33e3dd2e3cffe4b45a03fb7c5b5ea5e65617250fdc89e1a315563c20504b9d3a72555")); + bytes plainTest1 = cipher1; + bytes expectedPlain1 = asBytes("a"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest1)); + BOOST_REQUIRE(plainTest1 == expectedPlain1); + + bytes cipher2(fromHex("0x0443c24d6ccef3ad095140760bb143078b3880557a06392f17c5e368502d79532bc18903d59ced4bbe858e870610ab0d5f8b7963dd5c9c4cf81128d10efd7c7aa80091563c273e996578403694673581829e25a865191bdc9954db14285b56eb0043b6288172e0d003c10f42fe413222e273d1d4340c38a2d8344d7aadcbc846ee")); + bytes plainTest2 = cipher2; + bytes expectedPlain2 = asBytes("aaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest2)); + BOOST_REQUIRE(plainTest2 == expectedPlain2); + + bytes cipher3(fromHex("0x04c4e40c86bb5324e017e598c6d48c19362ae527af8ab21b077284a4656c8735e62d73fb3d740acefbec30ca4c024739a1fcdff69ecaf03301eebf156eb5f17cca6f9d7a7e214a1f3f6e34d1ee0ec00ce0ef7d2b242fbfec0f276e17941f9f1bfbe26de10a15a6fac3cda039904ddd1d7e06e7b96b4878f61860e47f0b84c8ceb64f6a900ff23844f4359ae49b44154980a626d3c73226c19e")); + bytes plainTest3 = cipher3; + bytes expectedPlain3 = asBytes("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + BOOST_REQUIRE(s_secp256k1.decryptECIES(kenc.sec(), plainTest3)); + BOOST_REQUIRE(plainTest3 == expectedPlain3); +} + +BOOST_AUTO_TEST_SUITE_END() + From d137581c7ff68ff7d7e43b106de430c001d81e6e Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 03:07:14 +0100 Subject: [PATCH 26/50] authenticated capability (hello) and handshake authentication --- libp2p/Host.cpp | 303 +++++---------------------- libp2p/Host.h | 79 +------ libp2p/Peer.h | 3 +- libp2p/RLPxHandshake.cpp | 433 ++++++++++++++++++++++----------------- libp2p/RLPxHandshake.h | 54 +++-- libp2p/Session.cpp | 94 +-------- libp2p/Session.h | 2 +- test/rlpx.cpp | 2 +- 8 files changed, 343 insertions(+), 627 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 0062722c6..5fc6ce3b9 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -34,13 +34,12 @@ #include "Common.h" #include "Capability.h" #include "UPnP.h" +#include "RLPxHandshake.h" #include "Host.h" using namespace std; using namespace dev; using namespace dev::p2p; -#include "RLPxHandshake.h" - HostNodeTableHandler::HostNodeTableHandler(Host& _host): m_host(_host) {} void HostNodeTableHandler::processEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -157,23 +156,63 @@ unsigned Host::protocolVersion() const return 3; } -void Host::registerPeer(std::shared_ptr _s, CapDescs const& _caps) +bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket) { + /// Get or create Peer + shared_ptr p; + p = m_peers[_id]; + if (!p) + { + p.reset(new Peer()); // this maybe redundant + p->id = _id; + } + p->m_lastDisconnect = NoDisconnect; + if (p->isOffline()) + 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()); + + auto protocolVersion = _rlp[1].toInt(); + auto clientVersion = _rlp[2].toString(); + auto caps = _rlp[3].toVector(); + auto listenPort = _rlp[4].toInt(); + + // clang error (previously: ... << hex << caps ...) + // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" + stringstream capslog; + for (auto cap: caps) + capslog << "(" << cap.first << "," << dec << cap.second << ")"; + 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(*_socket), p, PeerSessionInfo({_id, clientVersion, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + if (protocolVersion != this->protocolVersion()) + { + ps->disconnect(IncompatibleProtocol); + return false; + } + { - clog(NetNote) << "p2p.host.peer.register" << _s->m_peer->id.abridged(); RecursiveGuard l(x_sessions); - // TODO: temporary loose-coupling; if m_peers already has peer, - // it is same as _s->m_peer. (fixing next PR) - if (!m_peers.count(_s->m_peer->id)) - m_peers[_s->m_peer->id] = _s->m_peer; - m_sessions[_s->m_peer->id] = _s; + if (m_sessions.count(_id) && !!m_sessions[_id].lock()) + { + // Already connected. + clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); + ps->disconnect(DuplicatePeer); + return false; + } + + clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); + m_sessions[_id] = ps; } + ps->start(); unsigned o = (unsigned)UserPacket; - for (auto const& i: _caps) + for (auto const& i: caps) if (haveCapability(i)) { - _s->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(_s.get(), o)); + ps->m_capabilities[i] = shared_ptr(m_capabilities[i]->newPeerCapability(ps.get(), o)); o += m_capabilities[i]->messageCount(); } } @@ -340,7 +379,7 @@ void Host::runAcceptor() { // doHandshake takes ownersihp of *s via std::move // incoming connection; we don't yet know nodeid - auto handshake = make_shared(this, s); + auto handshake = make_shared(this, s); handshake->start(); success = true; } @@ -374,244 +413,6 @@ void Host::runAcceptor() } } -void PeerHandshake::transition(boost::system::error_code _ech) -{ - if (_ech) - { - boost::system::error_code ec; - socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - if (socket->is_open()) - socket->close(); - return; - } - - auto self(shared_from_this()); - if (nextState == New) - { - nextState = AckAuth; - - clog(NetConnect) << "Authenticating connection for " << socket->remote_endpoint(); - - if (originated) - { - clog(NetConnect) << "devp2p.connect.egress sending auth"; - // egress: tx auth - asserts((bool)remote); - auth.resize(Signature::size + h256::size + Public::size + h256::size + 1); - bytesRef sig(&auth[0], Signature::size); - bytesRef hepubk(&auth[Signature::size], h256::size); - bytesRef pubk(&auth[Signature::size + h256::size], Public::size); - 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); - 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); - - ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) - { - transition(ec); - }); - } - else - { - clog(NetConnect) << "devp2p.connect.ingress recving auth"; - // ingress: rx auth - authCipher.resize(307); - 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)) - { - clog(NetWarn) << "devp2p.connect.egress recving auth decrypt failed"; - nextState = Error; - transition(); - return; - } - - 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()); - transition(); - } - }); - } - } - else if (nextState == AckAuth) - { - nextState = Authenticating; - - if (originated) - { - clog(NetConnect) << "devp2p.connect.egress recving ack"; - // egress: rx ack - ackCipher.resize(210); - 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)) - { - clog(NetWarn) << "devp2p.connect.egress recving ack decrypt failed"; - nextState = Error; - transition(); - return; - } - - bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); - bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); - transition(); - } - }); - } - else - { - clog(NetConnect) << "devp2p.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) << "devp2p.connect.egress sending magic sequence"; - else - clog(NetConnect) << "devp2p.connect.ingress sending magic sequence"; - - PeerSecrets* k = new PeerSecrets; - bytes keyMaterialBytes(512); - bytesRef keyMaterial(&keyMaterialBytes); - - ecdhe.agree(remoteEphemeral, ess); - ess.ref().copyTo(keyMaterial.cropped(0, h256::size)); - ss.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - // auto token = sha3(ssA); - k->encryptK = sha3(keyMaterial); - k->encryptK.ref().copyTo(keyMaterial.cropped(h256::size, h256::size)); - k->macK = sha3(keyMaterial); - - // 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) - - bytes const& egressCipher = originated ? authCipher : ackCipher; - keyMaterialBytes.resize(h256::size + egressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ remoteNonce).ref().copyTo(keyMaterial); - bytesConstRef(&egressCipher).copyTo(keyMaterial.cropped(h256::size, egressCipher.size())); - k->egressMac = sha3(keyMaterial); - - bytes const& ingressCipher = originated ? ackCipher : authCipher; - keyMaterialBytes.resize(h256::size + ingressCipher.size()); - keyMaterial.retarget(keyMaterialBytes.data(), keyMaterialBytes.size()); - (k->macK ^ nonce).ref().copyTo(keyMaterial); - bytesConstRef(&ingressCipher).copyTo(keyMaterial.cropped(h256::size, ingressCipher.size())); - k->ingressMac = sha3(keyMaterial); - - - - - // 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) << "devp2p.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) << "devp2p.connect.egress recving magic sequence"; - else - clog(NetNote) << "devp2p.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 p; - p = host->m_peers[remote]; - if (!p) - { - p.reset(new Peer()); - p->id = remote; - } - 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(host, move(*socket), p); - ps->start(); - } - - // todo: PeerSession will take ownership of k and use it to encrypt wireline. - delete k; - }); - }); - } - else - { - clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Authentication Failed)"; - boost::system::error_code ec; - socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); - socket->close(); - } -} - string Host::pocHost() { vector strs; @@ -699,7 +500,7 @@ void Host::connect(std::shared_ptr const& _p) else { clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); - auto handshake = make_shared(this, s, _p->id); + auto handshake = make_shared(this, s, _p->id); handshake->start(); } Guard l(x_pendingNodeConns); diff --git a/libp2p/Host.h b/libp2p/Host.h index c34ec9f21..1d17d4518 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -77,9 +77,7 @@ private: class Host: public Worker { friend class HostNodeTableHandler; - - friend struct PeerHandshake; - friend struct RLPXHandshake; + friend class RLPXHandshake; friend class Session; friend class HostCapabilityFace; @@ -110,8 +108,6 @@ public: CapDescs caps() const { CapDescs ret; for (auto const& i: m_capabilities) ret.push_back(i.first); return ret; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(T::staticName(), T::staticVersion()))); } catch (...) { return nullptr; } } - bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } - void addNode(NodeId const& _node, std::string const& _addr, unsigned short _tcpPort, unsigned short _udpPort); /// Set ideal number of peers. @@ -149,7 +145,8 @@ public: NodeId id() const { return m_alias.pub(); } - void registerPeer(std::shared_ptr _s, CapDescs const& _caps); + /// 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); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); @@ -158,6 +155,8 @@ protected: void restoreNetwork(bytesConstRef _b); private: + bool havePeerSession(NodeId _id) { RecursiveGuard l(x_sessions); return m_sessions.count(_id) ? !!m_sessions[_id].lock() : false; } + /// Populate m_peerAddresses with available public addresses. void determinePublic(std::string const& _publicAddress, bool _upnp); @@ -234,73 +233,5 @@ private: bool m_accepting = false; }; -/** - * @brief Key material and derived secrets for TCP peer connection. - */ -struct PeerSecrets -{ - friend struct PeerHandshake; - friend struct RLPXHandshake; - -protected: - Secret encryptK; - Secret macK; - h256 egressMac; - h256 ingressMac; - - bytes magicCipherAndMac; - bytes recvdMagicCipherAndMac; -}; - -struct PeerHandshake: public std::enable_shared_from_this -{ - friend class Host; - enum State - { - Error = -1, - New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] - Authenticating, // Authenticating [tx caps, rx caps, authenticate] - }; - - /// Handshake for ingress connection. Takes ownership of socket. - PeerHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - /// Handshake for egress connection to _remote. Takes ownership of socket. - PeerHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), socket(std::move(_socket)), originated(true), remote(_remote) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } - - ~PeerHandshake() { delete socket; } - -protected: - void start() { transition(); } - -private: - void transition(boost::system::error_code _ech = boost::system::error_code()); - - /// Current state of handshake. - State nextState = New; - - Host* host; - - /// Node id of remote host for socket. - NodeId remote; - - bi::tcp::socket* socket; - bool originated = false; - - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - Secret ss; - Secret ess; - - crypto::ECDHE ecdhe; - h256 nonce; - - Public remoteEphemeral; - h256 remoteNonce; -}; - } } diff --git a/libp2p/Peer.h b/libp2p/Peer.h index e9b45845b..743d941db 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -55,8 +55,7 @@ class Peer: public Node friend class Session; /// Allows Session to update score and rating. friend class Host; /// For Host: saveNetwork(), restoreNetwork() - friend struct PeerHandshake; - friend struct RLPXHandshake; + friend class RLPXHandshake; public: bool isOffline() const { return !m_session.lock(); } diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 18db43577..77bc64e1b 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -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) +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})); -} - -void RLPXFrameIO::writeHeader(bi::tcp::socket* _socket, h128 const& _header) -{ + // 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) - { - 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); - }); - } + writeAuth(); else - { - 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 (decodeAuth()) - transition(); - else - { - clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed"; - nextState = Error; - transition(); - return; - } - }); - } + readAuth(); } else if (nextState == AckAuth) { - nextState = Authenticating; - + nextState = WriteHello; 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; - } - - bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); - bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); - transition(); - } - }); - } + readAck(); 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); - }); - } + writeAck(); } - else if (nextState == Authenticating) + else if (nextState == WriteHello) { + nextState = ReadHello; + if (originated) - clog(NetConnect) << "p2p.connect.egress sending magic sequence"; + clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; else - clog(NetConnect) << "p2p.connect.ingress sending magic sequence"; + clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - RLPXFrameIO* io = new RLPXFrameIO(*this); + 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; -// // 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 p; - p = host->m_peers[remote]; - if (!p) + // 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) + { + if (ec) + transition(ec); + else + { + /// authenticate and decrypt header + if (!io->authAndDecryptHeader(*(h256*)handshakeInBuffer.data())) + { + nextState = Error; + transition(); + return; + } + + clog(NetNote) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; + + /// 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; + } + + /// 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(); + 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(host, move(*socket), p); - ps->start(); -// } - - // todo: PeerSession will take ownership of k and use it to encrypt wireline. - delete io; -// }); -// }); + }); + } + }); } } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index a0b739660..2f8cc9094 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -40,21 +40,21 @@ class RLPXHandshake; class RLPXFrameIO { public: - RLPXFrameIO(RLPXHandshake& _init); + RLPXFrameIO(RLPXHandshake const& _init); - void writeFullPacketFrame(bytesConstRef _packet); - - void writeHeader(bi::tcp::socket* _socket, h128 const& _header); - - void write(bi::tcp::socket* _socket, bytesConstRef _in, bool _eof = false); + void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); + + /// Authenticates and decrypts header in-place. + bool authAndDecryptHeader(h256& io_cipherWithMac); - bool read(bytesConstRef _in, bytes& o_out); + /// Authenticates and decrypts frame in-place. + bool authAndDecryptFrame(bytesRef io_cipherWithMac); h128 egressDigest(); h128 ingressDigest(); - void updateEgressMACWithHeader(h128 const& _headerCipher); + void updateEgressMACWithHeader(bytesConstRef _headerCipher); void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); @@ -66,21 +66,28 @@ 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; }; -struct RLPXHandshake: public std::enable_shared_from_this +// TODO: change properties to m_ +class RLPXHandshake: public std::enable_shared_from_this { +public: friend class RLPXFrameIO; - friend class Host; + enum State { Error = -1, New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->Authenticating [egress: rx ack, ingress: tx ack] - Authenticating, // Authenticating [tx caps, rx caps, authenticate] + AckAuth, // AckAuth->WriteHello [egress: rx ack, ingress: tx ack] + WriteHello, // WriteHello [tx caps, rx caps, writehello] + ReadHello, + StartSession }; /// Handshake for ingress connection. Takes ownership of socket. @@ -91,20 +98,18 @@ struct RLPXHandshake: public std::enable_shared_from_this ~RLPXHandshake() { delete socket; } -protected: void start() { transition(); } - void generateAuth(); - bool decodeAuth(); +protected: + void writeAuth(); + void readAuth(); - void generateAck(); - bool decodeAck(); + void writeAck(); + void readAck(); - bytes frame(bytesConstRef _packet); - -private: + void error(); void transition(boost::system::error_code _ech = boost::system::error_code()); - + /// Current state of handshake. State nextState = New; @@ -121,12 +126,17 @@ private: bytes authCipher; bytes ack; bytes ackCipher; - + bytes handshakeOutBuffer; + bytes handshakeInBuffer; + crypto::ECDHE ecdhe; h256 nonce; Public remoteEphemeral; h256 remoteNonce; + + /// Frame IO is used to read frame for last step of handshake authentication. + std::unique_ptr io; }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 6ff765cf6..a9d50f6e7 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -36,11 +36,11 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n): +Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_socket(std::move(_socket)), + m_socket(move(_socket)), m_peer(_n), - m_info({NodeId(), "?", m_socket.remote_endpoint().address().to_string(), 0, chrono::steady_clock::duration(0), CapDescSet(), 0, map()}), + m_info(_info), m_ping(chrono::steady_clock::time_point::max()) { m_lastReceived = m_connect = chrono::steady_clock::now(); @@ -151,86 +151,6 @@ bool Session::interpret(RLP const& _r) switch ((PacketType)_r[0].toInt()) { - case HelloPacket: - { - // TODO: P2P first pass, implement signatures. if signature fails, drop connection. if egress, flag node's endpoint as stale. - // Move auth to Host so we consolidate authentication logic and eschew peer deduplication logic. - // Move all node-lifecycle information into Host. - // Finalize peer-lifecycle properties vs node lifecycle. - - m_protocolVersion = _r[1].toInt(); - auto clientVersion = _r[2].toString(); - auto caps = _r[3].toVector(); - auto listenPort = _r[4].toInt(); - auto id = _r[5].toHash(); - - // clang error (previously: ... << hex << caps ...) - // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" - stringstream capslog; - for (auto cap: caps) - capslog << "(" << cap.first << "," << dec << cap.second << ")"; - - clogS(NetMessageSummary) << "Hello: " << clientVersion << "V[" << m_protocolVersion << "]" << id.abridged() << showbase << capslog.str() << dec << listenPort; - - if (m_server->id() == id) - { - // Already connected. - clogS(NetWarn) << "Connected to ourself under a false pretext. We were told this peer was id" << id.abridged(); - disconnect(LocalIdentity); - return true; - } - - // if peer and connection have id, check for UnexpectedIdentity - if (!id) - { - disconnect(NullIdentity); - return true; - } - else if (!m_peer->id) - { - m_peer->id = id; - m_peer->endpoint.tcp.port(listenPort); - } - else if (m_peer->id != id) - { - // TODO p2p: FIXME. Host should catch this and reattempt adding node to table. - m_peer->id = id; - m_peer->m_score = 0; - m_peer->m_rating = 0; -// disconnect(UnexpectedIdentity); -// return true; - } - - if (m_server->havePeerSession(id)) - { - // Already connected. - clogS(NetWarn) << "Already connected to a peer with id" << id.abridged(); - // Possible that two nodes continually connect to each other with exact same timing. - this_thread::sleep_for(chrono::milliseconds(rand() % 100)); - disconnect(DuplicatePeer); - return true; - } - - if (m_peer->isOffline()) - m_peer->m_lastConnected = chrono::system_clock::now(); - - if (m_protocolVersion != m_server->protocolVersion()) - { - disconnect(IncompatibleProtocol); - return true; - } - - m_info.clientVersion = clientVersion; - m_info.host = m_socket.remote_endpoint().address().to_string(); - m_info.port = listenPort; - m_info.lastPing = std::chrono::steady_clock::duration(); - m_info.caps = _r[3].toSet(); - m_info.socket = (unsigned)m_socket.native_handle(); - m_info.notes = map(); - - m_server->registerPeer(shared_from_this(), caps); - break; - } case DisconnectPacket: { string reason = "Unspecified"; @@ -478,14 +398,6 @@ void Session::disconnect(DisconnectReason _reason) void Session::start() { - RLPStream s; - prep(s, HelloPacket, 5) - << m_server->protocolVersion() - << m_server->m_clientVersion - << m_server->caps() - << m_server->m_tcpPublic.port() - << m_server->id(); - sealAndSend(s); ping(); doRead(); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 4d55e7d9d..bfb7a2d60 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -51,7 +51,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n); + Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); diff --git a/test/rlpx.cpp b/test/rlpx.cpp index d845911f4..1cdde4320 100644 --- a/test/rlpx.cpp +++ b/test/rlpx.cpp @@ -141,7 +141,7 @@ BOOST_AUTO_TEST_CASE(test_secrets_cpp_vectors) bytes macSecret(32); outRef.copyTo(&macSecret); BOOST_REQUIRE(macSecret == fromHex("2ec149072353d54437422837c886b0538a9206e6c559f6b4a55f65a866867723")); - 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) From 7386ad26067cd53af8bf9384e38949ea3ed0f3fb Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 21:47:30 +0100 Subject: [PATCH 27/50] prep for framing. remove unused raw network send() methods from capabilities. bump protocol version. back out magic sequence packet prefix. --- libp2p/Capability.cpp | 10 ------- libp2p/Capability.h | 3 -- libp2p/Host.cpp | 16 ++++------ libp2p/Session.cpp | 69 ++++++++++++++++--------------------------- libp2p/Session.h | 5 ++-- 5 files changed, 35 insertions(+), 68 deletions(-) diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 5ef70bbc3..60f4fd7f3 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -53,16 +53,6 @@ void Capability::sealAndSend(RLPStream& _s) m_session->sealAndSend(_s); } -void Capability::send(bytesConstRef _msg) -{ - m_session->send(_msg); -} - -void Capability::send(bytes&& _msg) -{ - m_session->send(move(_msg)); -} - void Capability::addRating(unsigned _r) { m_session->addRating(_r); diff --git a/libp2p/Capability.h b/libp2p/Capability.h index 04b116aa8..ad8127bb5 100644 --- a/libp2p/Capability.h +++ b/libp2p/Capability.h @@ -52,9 +52,6 @@ protected: RLPStream& prep(RLPStream& _s, unsigned _id, unsigned _args = 0); void sealAndSend(RLPStream& _s); - void send(bytes&& _msg); - void send(bytesConstRef _msg); - void addRating(unsigned _r); private: diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 35c309a53..73e588463 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -154,7 +154,7 @@ void Host::doneWorking() unsigned Host::protocolVersion() const { - return 3; + return 4; } bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket) @@ -273,15 +273,11 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) void Host::seal(bytes& _b) { - _b[0] = 0x22; - _b[1] = 0x40; - _b[2] = 0x08; - _b[3] = 0x91; - uint32_t len = (uint32_t)_b.size() - 8; - _b[4] = (len >> 24) & 0xff; - _b[5] = (len >> 16) & 0xff; - _b[6] = (len >> 8) & 0xff; - _b[7] = len & 0xff; + uint32_t len = (uint32_t)_b.size() - 4; + _b[0] = (len >> 24) & 0xff; + _b[1] = (len >> 16) & 0xff; + _b[2] = (len >> 8) & 0xff; + _b[3] = len & 0xff; } void Host::determinePublic(string const& _publicAddress, bool _upnp) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 721bf1623..310ecea61 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -281,7 +281,7 @@ RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) RLPStream& Session::prep(RLPStream& _s) { - return _s.appendRaw(bytes(8, 0)); + return _s.appendRaw(bytes(4, 0)); } void Session::sealAndSend(RLPStream& _s) @@ -294,14 +294,12 @@ void Session::sealAndSend(RLPStream& _s) bool Session::checkPacket(bytesConstRef _msg) { - if (_msg.size() < 8) + if (_msg.size() < 5) return false; - if (!(_msg[0] == 0x22 && _msg[1] == 0x40 && _msg[2] == 0x08 && _msg[3] == 0x91)) + uint32_t len = ((_msg[0] * 256 + _msg[1]) * 256 + _msg[2]) * 256 + _msg[3]; + if (_msg.size() != len + 4) return false; - uint32_t len = ((_msg[4] * 256 + _msg[5]) * 256 + _msg[6]) * 256 + _msg[7]; - if (_msg.size() != len + 8) - return false; - RLP r(_msg.cropped(8)); + RLP r(_msg.cropped(4)); if (r.actualSize() != len) return false; return true; @@ -314,7 +312,7 @@ void Session::send(bytesConstRef _msg) void Session::send(bytes&& _msg) { - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(8)); + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(4)); if (!checkPacket(bytesConstRef(&_msg))) clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; @@ -410,68 +408,53 @@ void Session::start() void Session::doRead() { - // ignore packets received while waiting to disconnect + // ignore packets received while waiting to disconnect. if (m_dropped) return; auto self(shared_from_this()); m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) { - // If error is end of file, ignore if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) { - // got here with length of 1241... clogS(NetWarn) << "Error reading: " << ec.message(); drop(TCPError); } else if (ec && length == 0) - { return; - } else { try { m_incoming.resize(m_incoming.size() + length); memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - while (m_incoming.size() > 8) + + // 4 bytes for length header + const uint32_t c_hLen = 4; + while (m_incoming.size() > c_hLen) { - if (m_incoming[0] != 0x22 || m_incoming[1] != 0x40 || m_incoming[2] != 0x08 || m_incoming[3] != 0x91) + // break if data recvd is less than expected size of packet. + uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data(), c_hLen)); + uint32_t tlen = c_hLen + len; + if (m_incoming.size() < tlen) + break; + bytesConstRef frame(m_incoming.data(), tlen); + bytesConstRef packet = frame.cropped(c_hLen); + if (!checkPacket(frame)) { - clogS(NetWarn) << "INVALID SYNCHRONISATION TOKEN; expected = 22400891; received = " << toHex(bytesConstRef(m_incoming.data(), 4)); + cerr << "Received " << packet.size() << ": " << toHex(packet) << endl; + clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; } else { - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data() + 4, 4)); - uint32_t tlen = len + 8; - if (m_incoming.size() < tlen) - break; - - // enough has come in. - auto data = bytesConstRef(m_incoming.data(), tlen); - if (!checkPacket(data)) - { - cerr << "Received " << len << ": " << toHex(bytesConstRef(m_incoming.data() + 8, len)) << endl; - clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; - disconnect(BadProtocol); - return; - } - else - { - RLP r(data.cropped(8)); - if (!interpret(r)) - { - // error - bad protocol - clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); - // Just wasting our bandwidth - perhaps reduce rating? - //return; - } - } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); + RLP r(packet); + if (!interpret(r)) + clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); } + memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); + m_incoming.resize(m_incoming.size() - tlen); } doRead(); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 578e5e80a..9f87d386d 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -71,8 +71,6 @@ public: static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0); static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); - void send(bytes&& _msg); - void send(bytesConstRef _msg); int rating() const; void addRating(unsigned _r); @@ -85,6 +83,9 @@ public: void serviceNodesRequest(); private: + void send(bytes&& _msg); + void send(bytesConstRef _msg); + /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); From 4b198939cdf6cbeb96f708413809ad45a9eb6b0f Mon Sep 17 00:00:00 2001 From: subtly Date: Thu, 5 Mar 2015 23:27:31 +0100 Subject: [PATCH 28/50] test header macs --- test/rlpx.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/test/rlpx.cpp b/test/rlpx.cpp index 1cdde4320..593613106 100644 --- a/test/rlpx.cpp +++ b/test/rlpx.cpp @@ -323,9 +323,48 @@ BOOST_AUTO_TEST_CASE(test_secrets_from_go) BOOST_CHECK(digest == fromHex("0x75823d96e23136c89666ee025fb21a432be906512b3dd4a3049e898adb433847")); } -// bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); -// -// bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); + bytes initHello(fromHex("6ef23fcf1cec7312df623f9ae701e63b550cdb8517fefd8dd398fc2acd1d935e6e0434a2b96769078477637347b7b01924fff9ff1c06df2f804df3b0402bbb9f87365b3c6856b45e1e2b6470986813c3816a71bff9d69dd297a5dbd935ab578f6e5d7e93e4506a44f307c332d95e8a4b102585fd8ef9fc9e3e055537a5cec2e9")); + + bytes recvHello(fromHex("6ef23fcf1cec7312df623f9ae701e63be36a1cdd1b19179146019984f3625d4a6e0434a2b96769050577657247b7b02bc6c314470eca7e3ef650b98c83e9d7dd4830b3f718ff562349aead2530a8d28a8484604f92e5fced2c6183f304344ab0e7c301a0c05559f4c25db65e36820b4b909a226171a60ac6cb7beea09376d6d8")); + + /// test macs of frame headers + { + SHA3_256 egressmac(m_egressMac); + SHA3_256 prevDigest(egressmac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= *(h128*)initHello.data(); + egressmac.Update(encDigest.data(), h128::size); + egressmac.TruncatedFinal(encDigest.data(), h128::size); + + bytes provided(16); + bytesConstRef(&initHello).cropped(16, 16).copyTo(bytesRef(&provided)); + BOOST_REQUIRE(*(h128*)encDigest.data() == *(h128*)provided.data()); + } + + { + SHA3_256 ingressmac(m_ingressMac); + SHA3_256 prevDigest(ingressmac); + h128 prevDigestOut; + prevDigest.TruncatedFinal(prevDigestOut.data(), h128::size); + h128 encDigest; + m_macEnc.ProcessData(encDigest.data(), prevDigestOut.data(), h128::size); + encDigest ^= *(h128*)recvHello.data(); + ingressmac.Update(encDigest.data(), h128::size); + ingressmac.TruncatedFinal(encDigest.data(), h128::size); + + bytes provided(16); + bytesConstRef(&recvHello).cropped(16, 16).copyTo(bytesRef(&provided)); + BOOST_REQUIRE(*(h128*)encDigest.data() == *(h128*)provided.data()); + } + + // test decrypt of frame headers for recvHello + bytes plaintext(16); + m_frameDec.ProcessData(plaintext.data(), recvHello.data(), h128::size); + cout << "decrypt recv got: " << *(h128*)plaintext.data(); + } BOOST_AUTO_TEST_CASE(ecies_interop_test_primitives) From 23a64667e30d7d0e700888f442885677e0e8afe5 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 00:12:18 +0100 Subject: [PATCH 29/50] classify handshake and begin pulling rlpx into session --- libp2p/Host.cpp | 4 +- libp2p/Host.h | 6 +- libp2p/RLPxHandshake.cpp | 195 +++++++++++++++++++-------------------- libp2p/RLPxHandshake.h | 41 ++++---- libp2p/Session.cpp | 6 +- libp2p/Session.h | 6 +- 6 files changed, 131 insertions(+), 127 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 73e588463..ef2f95180 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) +bool Host::startPeerSession(Public const& _id, RLP const& _rlp, bi::tcp::socket *_socket, RLPXFrameIO* _io) { /// Get or create Peer shared_ptr p; @@ -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(*_socket), 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, _socket->remote_endpoint().address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); diff --git a/libp2p/Host.h b/libp2p/Host.h index ce11eb447..a5091ae7e 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -40,6 +40,7 @@ #include "HostCapability.h" #include "Network.h" #include "Peer.h" +#include "RLPxHandshake.h" #include "Common.h" namespace ba = boost::asio; namespace bi = ba::ip; @@ -69,8 +70,7 @@ private: * @brief The Host class * Capabilities should be registered prior to startNetwork, since m_capabilities is not thread-safe. * - * @todo handshake: gracefully disconnect peer if peer already connected - * @todo abstract socket -> IPConnection + * @todo cleanup startPeerSession * @todo determinePublic: ipv6, udp * @todo handle conflict if addNode/requireNode called and Node already exists w/conflicting tcp or udp port * @todo per-session keepalive/ping instead of broadcast; set ping-timeout via median-latency @@ -151,7 +151,7 @@ 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); + bool startPeerSession(Public const& _id, RLP const& _hello, bi::tcp::socket* _socket, RLPXFrameIO* _io); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index bfaca0dab..8bec9b6dd 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -28,7 +28,7 @@ using namespace dev; using namespace dev::p2p; using namespace CryptoPP; -RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) +RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) { // we need: // originated? @@ -41,18 +41,18 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) // shared-secret = sha3(ecdhe-shared-secret || sha3(nonce || initiator-nonce)) Secret ephemeralShared; - _init.ecdhe.agree(_init.remoteEphemeral, ephemeralShared); + _init.m_ecdhe.agree(_init.m_remoteEphemeral, ephemeralShared); ephemeralShared.ref().copyTo(keyMaterial.cropped(0, h256::size)); h512 nonceMaterial; - h256 const& leftNonce = _init.originated ? _init.remoteNonce : _init.nonce; - h256 const& rightNonce = _init.originated ? _init.nonce : _init.remoteNonce; + 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)); -> Host (to be saved to disk) + // 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 @@ -68,16 +68,16 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.socket) // 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.remoteNonce).ref().copyTo(keyMaterial); - bytes const& egressCipher = _init.originated ? _init.authCipher : _init.ackCipher; + (*(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.remoteNonce ^ _init.nonce).ref().copyTo(keyMaterial); - bytes const& ingressCipher = _init.originated ? _init.ackCipher : _init.authCipher; + (*(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())); @@ -204,28 +204,27 @@ void RLPXFrameIO::updateMAC(SHA3_256& _mac, h128 const& _seed) _mac.Update(encDigest.data(), h128::size); } - 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); - bytesRef pubk(&auth[Signature::size + h256::size], Public::size); - bytesRef nonce(&auth[Signature::size + h256::size + Public::size], h256::size); + clog(NetConnect) << "p2p.connect.egress sending auth to " << m_socket->remote_endpoint(); + 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); + bytesRef pubk(&m_auth[Signature::size + h256::size], Public::size); + bytesRef nonce(&m_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) 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); + crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, staticShared); + sign(m_ecdhe.seckey(), staticShared ^ m_nonce).ref().copyTo(sig); + sha3(m_ecdhe.pubkey().ref(), hepubk); + m_host->m_alias.pub().ref().copyTo(pubk); + m_nonce.ref().copyTo(nonce); + m_auth[m_auth.size() - 1] = 0x0; + encryptECIES(m_remote, &m_auth, m_authCipher); auto self(shared_from_this()); - ba::async_write(*socket, ba::buffer(authCipher), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(*m_socket, ba::buffer(m_authCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -233,17 +232,17 @@ void RLPXHandshake::writeAuth() 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); + clog(NetConnect) << "p2p.connect.ingress sending ack to " << m_socket->remote_endpoint(); + m_ack.resize(Public::size + h256::size + 1); + bytesRef epubk(&m_ack[0], Public::size); + bytesRef nonce(&m_ack[Public::size], h256::size); + m_ecdhe.pubkey().ref().copyTo(epubk); + m_nonce.ref().copyTo(nonce); + m_ack[m_ack.size() - 1] = 0x0; + encryptECIES(m_remote, &m_ack, m_ackCipher); auto self(shared_from_this()); - ba::async_write(*socket, ba::buffer(ackCipher), [this, self](boost::system::error_code ec, std::size_t) + ba::async_write(*m_socket, ba::buffer(m_ackCipher), [this, self](boost::system::error_code ec, std::size_t) { transition(ec); }); @@ -251,32 +250,32 @@ void RLPXHandshake::writeAck() void RLPXHandshake::readAuth() { - clog(NetConnect) << "p2p.connect.ingress recving auth from " << socket->remote_endpoint(); - authCipher.resize(307); + clog(NetConnect) << "p2p.connect.ingress recving auth from " << m_socket->remote_endpoint(); + m_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) + ba::async_read(*m_socket, ba::buffer(m_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)) + else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_authCipher), m_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()); + bytesConstRef sig(&m_auth[0], Signature::size); + bytesConstRef hepubk(&m_auth[Signature::size], h256::size); + bytesConstRef pubk(&m_auth[Signature::size + h256::size], Public::size); + bytesConstRef nonce(&m_auth[Signature::size + h256::size + Public::size], h256::size); + pubk.copyTo(m_remote.ref()); + nonce.copyTo(m_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()); + crypto::ecdh::agree(m_host->m_alias.sec(), m_remote, sharedSecret); + m_remoteEphemeral = recover(*(Signature*)sig.data(), sharedSecret ^ m_remoteNonce); + assert(sha3(m_remoteEphemeral) == *(h256*)hepubk.data()); transition(); } else { - clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << socket->remote_endpoint(); - nextState = Error; + clog(NetWarn) << "p2p.connect.egress recving auth decrypt failed for" << m_socket->remote_endpoint(); + m_nextState = Error; transition(); } }); @@ -284,23 +283,23 @@ void RLPXHandshake::readAuth() void RLPXHandshake::readAck() { - clog(NetConnect) << "p2p.connect.egress recving ack from " << socket->remote_endpoint(); - ackCipher.resize(210); + clog(NetConnect) << "p2p.connect.egress recving ack from " << m_socket->remote_endpoint(); + m_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) + ba::async_read(*m_socket, ba::buffer(m_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)) + else if (decryptECIES(m_host->m_alias.sec(), bytesConstRef(&m_ackCipher), m_ack)) { - bytesConstRef(&ack).cropped(0, Public::size).copyTo(remoteEphemeral.ref()); - bytesConstRef(&ack).cropped(Public::size, h256::size).copyTo(remoteNonce.ref()); + bytesConstRef(&m_ack).cropped(0, Public::size).copyTo(m_remoteEphemeral.ref()); + bytesConstRef(&m_ack).cropped(Public::size, h256::size).copyTo(m_remoteNonce.ref()); transition(); } else { - clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << socket->remote_endpoint(); - nextState = Error; + clog(NetWarn) << "p2p.connect.egress recving ack decrypt failed for " << m_socket->remote_endpoint(); + m_nextState = Error; transition(); } }); @@ -308,95 +307,95 @@ void RLPXHandshake::readAck() void RLPXHandshake::error() { - clog(NetConnect) << "Disconnecting " << socket->remote_endpoint() << " (Handshake Failed)"; + clog(NetConnect) << "Disconnecting " << m_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(); + m_socket->shutdown(boost::asio::ip::tcp::socket::shutdown_both, ec); + if (m_socket->is_open()) + m_socket->close(); } void RLPXHandshake::transition(boost::system::error_code _ech) { - if (_ech || nextState == Error) + if (_ech || m_nextState == Error) return error(); auto self(shared_from_this()); - if (nextState == New) + if (m_nextState == New) { - nextState = AckAuth; - if (originated) + m_nextState = AckAuth; + if (m_originated) writeAuth(); else readAuth(); } - else if (nextState == AckAuth) + else if (m_nextState == AckAuth) { - nextState = WriteHello; - if (originated) + m_nextState = WriteHello; + if (m_originated) readAck(); else writeAck(); } - else if (nextState == WriteHello) + else if (m_nextState == WriteHello) { - nextState = ReadHello; + m_nextState = ReadHello; - if (originated) + if (m_originated) clog(NetConnect) << "p2p.connect.egress sending capabilities handshake"; else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - io.reset(new RLPXFrameIO(*this)); + m_io.reset(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(); + << m_host->protocolVersion() + << m_host->m_clientVersion + << m_host->caps() + << m_host->m_tcpPublic.port() + << m_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) + 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) { transition(ec); }); } - else if (nextState == ReadHello) + else if (m_nextState == ReadHello) { // Authenticate and decrypt initial hello frame with initial RLPXFrameIO - // and request host to start session. - nextState = StartSession; + // and request m_host to start session. + m_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) + 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) { if (ec) transition(ec); else { /// authenticate and decrypt header - if (!io->authAndDecryptHeader(*(h256*)handshakeInBuffer.data())) + if (!m_io->authAndDecryptHeader(*(h256*)m_handshakeInBuffer.data())) { - nextState = Error; + m_nextState = Error; transition(); return; } - clog(NetNote) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; + clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "recvd hello header"; /// check frame size - bytes& header = handshakeInBuffer; + bytes& header = m_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; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; + m_nextState = Error; transition(); return; } @@ -406,33 +405,33 @@ void RLPXHandshake::transition(boost::system::error_code _ech) 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) + 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) { if (ec) transition(ec); else { - if (!io->authAndDecryptFrame(bytesRef(&handshakeInBuffer))) + if (!m_io->authAndDecryptFrame(bytesRef(&m_handshakeInBuffer))) { - clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; - nextState = Error; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; + m_nextState = Error; transition(); return; } - RLP rlp(handshakeInBuffer); + RLP rlp(m_handshakeInBuffer); auto packetType = (PacketType)rlp[0].toInt(); if (packetType != 0) { - clog(NetWarn) << (originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; - nextState = Error; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; + m_nextState = Error; transition(); return; } // todo: memory management of RLPFrameIO - host->startPeerSession(remote, rlp, socket); + m_host->startPeerSession(m_remote, rlp, m_socket, m_io.get()); } }); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 2f8cc9094..a50ec2718 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -35,10 +35,12 @@ namespace dev namespace p2p { +class Session; class RLPXHandshake; class RLPXFrameIO { + friend class Session; public: RLPXFrameIO(RLPXHandshake const& _init); @@ -74,7 +76,6 @@ private: bi::tcp::socket* m_socket; }; -// TODO: change properties to m_ class RLPXHandshake: public std::enable_shared_from_this { public: @@ -91,12 +92,12 @@ public: }; /// Handshake for ingress connection. Takes ownership of socket. - RLPXHandshake(Host* _host, bi::tcp::socket* _socket): host(_host), socket(std::move(_socket)), originated(false) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + 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()); } /// Handshake for egress connection to _remote. Takes ownership of socket. - RLPXHandshake(Host* _host, bi::tcp::socket* _socket, NodeId _remote): host(_host), remote(_remote), socket(std::move(_socket)), originated(true) { crypto::Nonce::get().ref().copyTo(nonce.ref()); } + 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 socket; } + ~RLPXHandshake() { delete m_socket; } void start() { transition(); } @@ -111,32 +112,32 @@ protected: void transition(boost::system::error_code _ech = boost::system::error_code()); /// Current state of handshake. - State nextState = New; + State m_nextState = New; - Host* host; + Host* m_host; /// Node id of remote host for socket. - NodeId remote; + NodeId m_remote; - bi::tcp::socket* socket; - bool originated = false; + bi::tcp::socket* m_socket; + bool m_originated = false; /// Buffers for encoded and decoded handshake phases - bytes auth; - bytes authCipher; - bytes ack; - bytes ackCipher; - bytes handshakeOutBuffer; - bytes handshakeInBuffer; + bytes m_auth; + bytes m_authCipher; + bytes m_ack; + bytes m_ackCipher; + bytes m_handshakeOutBuffer; + bytes m_handshakeInBuffer; - crypto::ECDHE ecdhe; - h256 nonce; + crypto::ECDHE m_ecdhe; + h256 m_nonce; - Public remoteEphemeral; - h256 remoteNonce; + Public m_remoteEphemeral; + h256 m_remoteNonce; /// Frame IO is used to read frame for last step of handshake authentication. - std::unique_ptr io; + std::unique_ptr m_io; }; } diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 310ecea61..e11e70e35 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -37,9 +37,11 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info): +Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_socket(move(_socket)), +#warning fixme + m_socket(move(*_io.m_socket)), + m_io(move(_io)), 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 9f87d386d..5767b7756 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -32,6 +32,7 @@ #include #include #include +#include "RLPxHandshake.h" #include "Common.h" namespace dev @@ -52,7 +53,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, bi::tcp::socket _socket, std::shared_ptr const& _n, PeerSessionInfo _info); + Session(Host* _server, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); @@ -63,7 +64,7 @@ public: bool isConnected() const { return m_socket.is_open(); } NodeId id() const; - unsigned socketId() const { return m_socket.native_handle(); } + unsigned socketId() const { return m_info.socket; } template std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } @@ -104,6 +105,7 @@ 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. Mutex x_writeQueue; ///< Mutex for the write queue. std::deque m_writeQueue; ///< The write queue. std::array m_data; ///< Buffer for ingress packet data. From a0aaf614f37ecc3acc4252c1e4c06e48e5ddaf88 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 03:14:38 +0100 Subject: [PATCH 30/50] 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. From df96fcd03bab25d74ca52b0bf533e9646bab2763 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 07:08:34 +0100 Subject: [PATCH 31/50] separate packet type. add coding. --- libethereum/EthereumPeer.cpp | 28 ++++---- libp2p/Capability.cpp | 2 +- libp2p/Host.cpp | 7 +- libp2p/RLPxFrameIO.cpp | 65 ++++++++++------- libp2p/RLPxFrameIO.h | 7 +- libp2p/RLPxHandshake.cpp | 10 +-- libp2p/RLPxHandshake.h | 2 +- libp2p/Session.cpp | 136 +++++++++++++++++++---------------- libp2p/Session.h | 11 ++- libwhisper/WhisperPeer.cpp | 2 +- test/peer.cpp | 15 ++-- 11 files changed, 156 insertions(+), 129 deletions(-) diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 95e1aadda..a546c3b22 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -297,13 +297,13 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) { case StatusPacket: { - m_protocolVersion = _r[1].toInt(); - m_networkId = _r[2].toInt(); + m_protocolVersion = _r[0].toInt(); + m_networkId = _r[1].toInt(); // a bit dirty as we're misusing these to communicate the values to transition, but harmless. - m_totalDifficulty = _r[3].toInt(); - m_latestHash = _r[4].toHash(); - auto genesisHash = _r[5].toHash(); + m_totalDifficulty = _r[2].toInt(); + m_latestHash = _r[3].toHash(); + auto genesisHash = _r[4].toHash(); clogS(NetMessageSummary) << "Status:" << m_protocolVersion << "/" << m_networkId << "/" << genesisHash.abridged() << ", TD:" << m_totalDifficulty << "=" << m_latestHash.abridged(); @@ -327,7 +327,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; addRating(_r.itemCount() - 1); Guard l(x_knownTransactions); - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = sha3(_r[i].data()); m_knownTransactions.insert(h); @@ -339,8 +339,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case GetBlockHashesPacket: { - h256 later = _r[1].toHash(); - unsigned limit = _r[2].toInt(); + h256 later = _r[0].toHash(); + unsigned limit = _r[1].toInt(); clogS(NetMessageSummary) << "GetBlockHashes (" << limit << "entries," << later.abridged() << ")"; unsigned c = min(host()->m_chain.number(later), limit); @@ -367,7 +367,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) transition(Asking::Blocks); return true; } - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = _r[i].toHash(); if (host()->m_chain.isKnown(h)) @@ -388,7 +388,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) // return the requested blocks. bytes rlp; unsigned n = 0; - for (unsigned i = 1; i < _r.itemCount() && i <= c_maxBlocks; ++i) + for (unsigned i = 0; i < _r.itemCount() && i <= c_maxBlocks; ++i) { auto b = host()->m_chain.block(_r[i].toHash()); if (b.size()) @@ -422,7 +422,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) unsigned got = 0; unsigned repeated = 0; - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { auto h = BlockInfo::headerHash(_r[i].data()); if (m_sub.noteBlock(h)) @@ -467,14 +467,14 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case NewBlockPacket: { - auto h = BlockInfo::headerHash(_r[1].data()); + auto h = BlockInfo::headerHash(_r[0].data()); clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); if (_r.itemCount() != 3) disable("NewBlock without 2 data fields."); else { - switch (host()->m_bq.import(_r[1].data(), host()->m_chain)) + switch (host()->m_bq.import(_r[0].data(), host()->m_chain)) { case ImportResult::Success: addRating(100); @@ -493,7 +493,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case ImportResult::UnknownParent: clogS(NetMessageSummary) << "Received block with no known parent. Resyncing..."; - setNeedsSyncing(h, _r[2].toInt()); + setNeedsSyncing(h, _r[1].toInt()); break; } Guard l(x_knownBlocks); diff --git a/libp2p/Capability.cpp b/libp2p/Capability.cpp index 60f4fd7f3..dccc130cd 100644 --- a/libp2p/Capability.cpp +++ b/libp2p/Capability.cpp @@ -45,7 +45,7 @@ void Capability::disable(std::string const& _problem) RLPStream& Capability::prep(RLPStream& _s, unsigned _id, unsigned _args) { - return Session::prep(_s).appendList(_args + 1).append(_id + m_idOffset); + return _s.appendRaw(bytes(1, _id + m_idOffset)).appendList(_args); } void Capability::sealAndSend(RLPStream& _s) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 057453d8f..eb5e8adc1 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io 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, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, _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); @@ -273,11 +273,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) void Host::seal(bytes& _b) { - uint32_t len = (uint32_t)_b.size() - 4; - _b[0] = (len >> 24) & 0xff; - _b[1] = (len >> 16) & 0xff; - _b[2] = (len >> 8) & 0xff; - _b[3] = len & 0xff; } void Host::determinePublic(string const& _publicAddress, bool _upnp) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index 3ddaf9143..8531759e9 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -57,12 +57,16 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) // 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()); + SecByteBlock aesSecretEnc(outRef.data(), h128::size); + SecByteBlock aesSecretDec(outRef.data(), h128::size); + SecByteBlock emptyIV(h128::size); + m_frameEnc.SetKeyWithIV(aesSecretEnc, h128::size, emptyIV); + m_frameDec.SetKeyWithIV(aesSecretDec, h128::size, emptyIV); // mac-secret = sha3(ecdhe-shared-secret || aes-secret) sha3(keyMaterial, outRef); // output mac-secret - m_macEnc.SetKey(outRef.data(), h128::size); + SecByteBlock macSecret(outRef.data(), h128::size); + m_macEnc.SetKey(macSecret, h128::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) @@ -91,16 +95,17 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) // 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())})); + uint32_t len = (uint32_t)_packet.size(); + header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)})); // 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); + + bytes headerWithMac(32); + bytes headerBytes(16); + bytesConstRef(&header.out()).copyTo(&headerBytes); + m_frameEnc.ProcessData(headerWithMac.data(), headerBytes.data(), 16); updateEgressMACWithHeader(bytesConstRef(&headerWithMac).cropped(0, 16)); egressDigest().ref().copyTo(bytesRef(&headerWithMac).cropped(h128::size,h128::size)); @@ -116,17 +121,16 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) 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) +bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) { - updateIngressMACWithHeader(io.ref()); - bytesConstRef macRef = io.ref().cropped(h128::size, h128::size); + asserts(io.size() == h256::size); + updateIngressMACWithHeader(io); + bytesConstRef macRef = io.cropped(h128::size, h128::size); if (*(h128*)macRef.data() != ingressDigest()) return false; - m_frameDec.ProcessData(io.data(), io.data(), 16); + m_frameDec.ProcessData(io.data(), io.data(), h128::size); return true; } @@ -159,7 +163,7 @@ h128 RLPXFrameIO::ingressDigest() void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) { - updateMAC(m_egressMac, *(h128*)_headerCipher.data()); + updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); } void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) @@ -170,13 +174,12 @@ void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) 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()); + updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); } void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) @@ -187,20 +190,28 @@ void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) 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) +void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) { + if (_seed.size() && _seed.size() != h128::size) + asserts(false); + 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); - + 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); } diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index 07211d4de..a8b2011eb 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -26,6 +26,7 @@ #include #include #include +#include #include "Common.h" namespace ba = boost::asio; namespace bi = boost::asio::ip; @@ -57,11 +58,12 @@ class RLPXFrameIO friend class Session; public: RLPXFrameIO(RLPXHandshake const& _init); + ~RLPXFrameIO() {} void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); /// Authenticates and decrypts header in-place. - bool authAndDecryptHeader(h256& io_cipherWithMac); + bool authAndDecryptHeader(bytesRef io_cipherWithMac); /// Authenticates and decrypts frame in-place. bool authAndDecryptFrame(bytesRef io_cipherWithMac); @@ -82,10 +84,11 @@ protected: bi::tcp::socket& socket() { return m_socket->ref(); } private: - void updateMAC(CryptoPP::SHA3_256& _mac, h128 const& _seed = h128()); + void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); CryptoPP::CTR_Mode::Encryption m_frameEnc; CryptoPP::CTR_Mode::Encryption m_frameDec; + Mutex x_macEnc; CryptoPP::ECB_Mode::Encryption m_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index bca1afd61..004880c39 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -166,7 +166,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; - m_io.reset(new RLPXFrameIO(*this)); + m_io = new RLPXFrameIO(*this); // old packet format // 5 arguments, HelloPacket @@ -193,14 +193,14 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // read frame header m_handshakeInBuffer.resize(h256::size); - ba::async_read(m_socket->ref(), 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); else { /// authenticate and decrypt header - if (!m_io->authAndDecryptHeader(*(h256*)m_handshakeInBuffer.data())) + if (!m_io->authAndDecryptHeader(bytesRef(m_handshakeInBuffer.data(), h256::size))) { m_nextState = Error; transition(); @@ -215,7 +215,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) if (frameSize > 1024) { // all future frames: 16777216 - clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large"; + clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame is too large" << frameSize; m_nextState = Error; transition(); return; @@ -251,7 +251,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) return; } - m_host->startPeerSession(m_remote, rlp, m_io.get(), m_socket->remoteEndpoint()); + m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } }); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 12abe4379..cd5e7fb83 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -94,7 +94,7 @@ protected: h256 m_remoteNonce; /// Frame IO is used to read frame for last step of handshake authentication. - std::unique_ptr m_io; + RLPXFrameIO* m_io; std::shared_ptr m_socket; }; diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index f44fa2d6e..da83c0b5e 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -38,10 +38,10 @@ using namespace dev::p2p; #endif #define clogS(X) dev::LogOutputStream(false) << "| " << std::setw(2) << m_socket.native_handle() << "] " -Session::Session(Host* _s, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info): +Session::Session(Host* _s, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info): m_server(_s), - m_io(move(_io)), - m_socket(m_io.socket()), + m_io(_io), + m_socket(m_io->socket()), m_peer(_n), m_info(_info), m_ping(chrono::steady_clock::time_point::max()) @@ -67,6 +67,7 @@ Session::~Session() } } catch (...){} + delete m_io; } NodeId Session::id() const @@ -144,21 +145,21 @@ void Session::serviceNodesRequest() addNote("peers", "done"); } -bool Session::interpret(RLP const& _r) +bool Session::interpret(PacketType _t, RLP const& _r) { m_lastReceived = chrono::steady_clock::now(); - clogS(NetRight) << _r; + clogS(NetRight) << _t << _r; try // Generic try-catch block designed to capture RLP format errors - TODO: give decent diagnostics, make a bit more specific over what is caught. { - switch ((PacketType)_r[0].toInt()) + switch (_t) { case DisconnectPacket: { string reason = "Unspecified"; - auto r = (DisconnectReason)_r[1].toInt(); - if (!_r[1].isInt()) + auto r = (DisconnectReason)_r[0].toInt(); + if (!_r[0].isInt()) drop(BadProtocol); else { @@ -197,7 +198,7 @@ bool Session::interpret(RLP const& _r) clogS(NetTriviaSummary) << "Peers (" << dec << (_r.itemCount() - 1) << " entries)"; m_weRequestedNodes = false; - for (unsigned i = 1; i < _r.itemCount(); ++i) + for (unsigned i = 0; i < _r.itemCount(); ++i) { bi::address peerAddress; if (_r[i][0].size() == 16) @@ -247,12 +248,11 @@ bool Session::interpret(RLP const& _r) break; default: { - auto id = _r[0].toInt(); for (auto const& i: m_capabilities) - if (id >= i.second->m_idOffset && id - i.second->m_idOffset < i.second->hostCapability()->messageCount()) + if (_t >= i.second->m_idOffset && _t - i.second->m_idOffset < i.second->hostCapability()->messageCount()) { if (i.second->m_enabled) - return i.second->interpret(id - i.second->m_idOffset, _r); + return i.second->interpret(_t - i.second->m_idOffset, _r); else return true; } @@ -278,12 +278,7 @@ void Session::ping() RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) { - return prep(_s).appendList(_args + 1).append((unsigned)_id); -} - -RLPStream& Session::prep(RLPStream& _s) -{ - return _s.appendRaw(bytes(4, 0)); + return _s.appendRaw(bytes(1, _id)).appendList(_args); } void Session::sealAndSend(RLPStream& _s) @@ -296,32 +291,32 @@ void Session::sealAndSend(RLPStream& _s) bool Session::checkPacket(bytesConstRef _msg) { - if (_msg.size() < 5) + if (_msg.size() < 2) return false; - uint32_t len = ((_msg[0] * 256 + _msg[1]) * 256 + _msg[2]) * 256 + _msg[3]; - if (_msg.size() != len + 4) + if (_msg[0] > 0x7f) return false; - RLP r(_msg.cropped(4)); - if (r.actualSize() != len) + RLP r(_msg.cropped(1)); + if (r.actualSize() + 1 != _msg.size()) return false; return true; } -void Session::send(bytesConstRef _msg) -{ - send(_msg.toBytes()); -} +//void Session::send(bytesConstRef _msg) +//{ +// send(_msg.toBytes()); +//} void Session::send(bytes&& _msg) { - clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(4)); + clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1)); - if (!checkPacket(bytesConstRef(&_msg))) + bytesConstRef msg(&_msg); + if (!checkPacket(msg)) clogS(NetWarn) << "INVALID PACKET CONSTRUCTED!"; if (!m_socket.is_open()) return; - + bool doWrite = false; { Guard l(x_writeQueue); @@ -336,6 +331,7 @@ void Session::send(bytes&& _msg) void Session::write() { const bytes& bytes = m_writeQueue[0]; + m_io->writeSingleFramePacket(&bytes, m_writeQueue[0]); auto self(shared_from_this()); ba::async_write(m_socket, ba::buffer(bytes), [this, self](boost::system::error_code ec, std::size_t /*length*/) { @@ -415,7 +411,7 @@ void Session::doRead() return; auto self(shared_from_this()); - m_socket.async_read_some(boost::asio::buffer(m_data), [this,self](boost::system::error_code ec, std::size_t length) + ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) { @@ -426,50 +422,66 @@ void Session::doRead() return; else { - try + /// authenticate and decrypt header + bytesRef header(m_data.data(), h256::size); + if (!m_io->authAndDecryptHeader(header)) + { + clog(NetWarn) << "header decrypt failed"; + drop(BadProtocol); // todo: better error + return; + } + + /// check frame size + uint32_t frameSize = (m_data[0] * 256 + m_data[1]) * 256 + m_data[2]; + if (frameSize > 16777216) + { + clog(NetWarn) << "frame size too large"; + drop(BadProtocol); + return; + } + + /// rlp of header has protocol-type, sequence-id[, total-packet-size] + bytes headerRLP(13); + bytesConstRef(m_data.data(), h128::size).cropped(3).copyTo(&headerRLP); + + /// read padded frame and mac + auto tlen = frameSize + ((16 - (frameSize % 16)) % 16) + h128::size; + ba::async_read(m_socket, boost::asio::buffer(m_data, tlen), [this, self, headerRLP, frameSize, tlen](boost::system::error_code ec, std::size_t length) { - m_incoming.resize(m_incoming.size() + length); - memcpy(m_incoming.data() + m_incoming.size() - length, m_data.data(), length); - - // 4 bytes for length header - const uint32_t c_hLen = 4; - while (m_incoming.size() > c_hLen) + if (ec && ec.category() != boost::asio::error::get_misc_category() && ec.value() != boost::asio::error::eof) + { + clogS(NetWarn) << "Error reading: " << ec.message(); + drop(TCPError); + } + else if (ec && length == 0) + return; + else { - // break if data recvd is less than expected size of packet. - uint32_t len = fromBigEndian(bytesConstRef(m_incoming.data(), c_hLen)); - uint32_t tlen = c_hLen + len; - if (m_incoming.size() < tlen) - break; - bytesConstRef frame(m_incoming.data(), tlen); - bytesConstRef packet = frame.cropped(c_hLen); + if (!m_io->authAndDecryptFrame(bytesRef(m_data.data(), tlen))) + { + clog(NetWarn) << "frame decrypt failed"; + drop(BadProtocol); // todo: better error + return; + } + + bytesConstRef frame(m_data.data(), frameSize); if (!checkPacket(frame)) { - cerr << "Received " << packet.size() << ": " << toHex(packet) << endl; + cerr << "Received " << frame.size() << ": " << toHex(frame) << endl; clogS(NetWarn) << "INVALID MESSAGE RECEIVED"; disconnect(BadProtocol); return; } else { - RLP r(packet); - if (!interpret(r)) + auto packetType = (PacketType)RLP(frame.cropped(0, 1)).toInt(); + RLP r(frame.cropped(1)); + if (!interpret(packetType, r)) clogS(NetWarn) << "Couldn't interpret packet." << RLP(r); } - memmove(m_incoming.data(), m_incoming.data() + tlen, m_incoming.size() - tlen); - m_incoming.resize(m_incoming.size() - tlen); + doRead(); } - doRead(); - } - catch (Exception const& _e) - { - clogS(NetWarn) << "ERROR: " << diagnostic_information(_e); - drop(BadProtocol); - } - catch (std::exception const& _e) - { - clogS(NetWarn) << "ERROR: " << _e.what(); - drop(BadProtocol); - } + }); } }); } diff --git a/libp2p/Session.h b/libp2p/Session.h index 89ef5fd74..77b7de7bb 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -54,7 +54,7 @@ class Session: public std::enable_shared_from_this friend class HostCapabilityFace; public: - Session(Host* _server, RLPXFrameIO _io, std::shared_ptr const& _n, PeerSessionInfo _info); + Session(Host* _server, RLPXFrameIO* _io, std::shared_ptr const& _n, PeerSessionInfo _info); virtual ~Session(); void start(); @@ -71,7 +71,6 @@ public: std::shared_ptr cap() const { try { return std::static_pointer_cast(m_capabilities.at(std::make_pair(PeerCap::name(), PeerCap::version()))); } catch (...) { return nullptr; } } static RLPStream& prep(RLPStream& _s, PacketType _t, unsigned _args = 0); - static RLPStream& prep(RLPStream& _s); void sealAndSend(RLPStream& _s); int rating() const; @@ -86,7 +85,7 @@ public: private: void send(bytes&& _msg); - void send(bytesConstRef _msg); +// void send(bytesConstRef _msg); /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); @@ -98,18 +97,18 @@ private: void write(); /// Interpret an incoming message. - bool interpret(RLP const& _r); + bool interpret(PacketType _t, RLP const& _r); /// @returns true iff the _msg forms a valid message for sending or receiving on the network. static bool checkPacket(bytesConstRef _msg); Host* m_server; ///< The host that owns us. Never null. - RLPXFrameIO m_io; ///< Transport over which packets are sent. + 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. + std::array m_data; ///< Buffer for ingress packet data. bytes m_incoming; ///< Read buffer for ingress bytes. unsigned m_protocolVersion = 0; ///< The protocol version of the peer. diff --git a/libwhisper/WhisperPeer.cpp b/libwhisper/WhisperPeer.cpp index 7480a104e..53ea91a9e 100644 --- a/libwhisper/WhisperPeer.cpp +++ b/libwhisper/WhisperPeer.cpp @@ -55,7 +55,7 @@ bool WhisperPeer::interpret(unsigned _id, RLP const& _r) { case StatusPacket: { - auto protocolVersion = _r[1].toInt(); + auto protocolVersion = _r[0].toInt(); clogS(NetMessageSummary) << "Status: " << protocolVersion; diff --git a/test/peer.cpp b/test/peer.cpp index 7f3c19e1e..0fe3fd1ed 100644 --- a/test/peer.cpp +++ b/test/peer.cpp @@ -32,6 +32,9 @@ BOOST_AUTO_TEST_SUITE(p2p) BOOST_AUTO_TEST_CASE(host) { + auto oldLogVerbosity = g_logVerbosity; + g_logVerbosity = 10; + NetworkPreferences host1prefs(30301, "127.0.0.1", true, true); NetworkPreferences host2prefs(30302, "127.0.0.1", true, true); @@ -44,10 +47,14 @@ BOOST_AUTO_TEST_CASE(host) host1.addNode(node2, "127.0.0.1", host2prefs.listenPort, host2prefs.listenPort); - this_thread::sleep_for(chrono::seconds(1)); + this_thread::sleep_for(chrono::seconds(3)); + + auto host1peerCount = host1.peerCount(); + auto host2peerCount = host2.peerCount(); + BOOST_REQUIRE_EQUAL(host1peerCount, 1); + BOOST_REQUIRE_EQUAL(host2peerCount, 1); - BOOST_REQUIRE_EQUAL(host1.peerCount(), 1); - BOOST_REQUIRE_EQUAL(host2.peerCount(), host1.peerCount()); + g_logVerbosity = oldLogVerbosity; } BOOST_AUTO_TEST_CASE(save_nodes) @@ -71,7 +78,7 @@ BOOST_AUTO_TEST_CASE(save_nodes) for (auto const& h: hosts) host2.addNode(h->id(), "127.0.0.1", h->listenPort(), h->listenPort()); - this_thread::sleep_for(chrono::milliseconds(1000)); + this_thread::sleep_for(chrono::milliseconds(2000)); bytes firstHostNetwork(host.saveNetwork()); bytes secondHostNetwork(host.saveNetwork()); From d6c37b7562e9be68d1d8a4570354738b4d46c3ad Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 16:14:02 +0100 Subject: [PATCH 32/50] don't deallocate keys --- libp2p/RLPxFrameIO.cpp | 17 ++++++++++------- libp2p/RLPxFrameIO.h | 7 +++++-- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index 8531759e9..e9d3715a2 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -57,16 +57,19 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret - SecByteBlock aesSecretEnc(outRef.data(), h128::size); - SecByteBlock aesSecretDec(outRef.data(), h128::size); - SecByteBlock emptyIV(h128::size); - m_frameEnc.SetKeyWithIV(aesSecretEnc, h128::size, emptyIV); - m_frameDec.SetKeyWithIV(aesSecretDec, h128::size, emptyIV); + m_frameEncKey.resize(h128::size); + memcpy(m_frameEncKey.data(), outRef.data(), h128::size); + m_frameDecKey.resize(h128::size); + memcpy(m_frameDecKey.data(), outRef.data(), h128::size); + h128 iv; + m_frameEnc.SetKeyWithIV(m_frameEncKey, h128::size, iv.data()); + m_frameDec.SetKeyWithIV(m_frameDecKey, h128::size, iv.data()); // mac-secret = sha3(ecdhe-shared-secret || aes-secret) sha3(keyMaterial, outRef); // output mac-secret - SecByteBlock macSecret(outRef.data(), h128::size); - m_macEnc.SetKey(macSecret, h128::size); + m_macEncKey.resize(h128::size); + memcpy(m_macEncKey.data(), outRef.data(), h128::size); + m_macEnc.SetKey(m_macEncKey, h128::size); // Initiator egress-mac: sha3(mac-secret^recipient-nonce || auth-sent-init) // ingress-mac: sha3(mac-secret^initiator-nonce || auth-recvd-ack) diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index a8b2011eb..2bd2218aa 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -86,15 +86,18 @@ protected: private: void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); + CryptoPP::SecByteBlock m_frameEncKey; + CryptoPP::SecByteBlock m_frameDecKey; CryptoPP::CTR_Mode::Encryption m_frameEnc; CryptoPP::CTR_Mode::Encryption m_frameDec; - Mutex x_macEnc; + CryptoPP::SecByteBlock m_macEncKey; CryptoPP::ECB_Mode::Encryption m_macEnc; + Mutex x_macEnc; CryptoPP::SHA3_256 m_egressMac; CryptoPP::SHA3_256 m_ingressMac; std::shared_ptr m_socket; }; - + } } \ No newline at end of file From 339e4aec3352e25b84a4ff48616644f9a4354298 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 16:48:58 +0100 Subject: [PATCH 33/50] offset logic for eth packets --- libethereum/EthereumPeer.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index a546c3b22..58cc5ca72 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -324,8 +324,8 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) case GetTransactionsPacket: break; // DEPRECATED. case TransactionsPacket: { - clogS(NetMessageSummary) << "Transactions (" << dec << (_r.itemCount() - 1) << "entries)"; - addRating(_r.itemCount() - 1); + clogS(NetMessageSummary) << "Transactions (" << dec << _r.itemCount() << "entries)"; + addRating(_r.itemCount()); Guard l(x_knownTransactions); for (unsigned i = 0; i < _r.itemCount(); ++i) { @@ -355,14 +355,14 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case BlockHashesPacket: { - clogS(NetMessageSummary) << "BlockHashes (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreHashes"); + clogS(NetMessageSummary) << "BlockHashes (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreHashes"); if (m_asking != Asking::Hashes) { cwarn << "Peer giving us hashes when we didn't ask for them."; break; } - if (_r.itemCount() == 1) + if (_r.itemCount() == 0) { transition(Asking::Blocks); return true; @@ -384,7 +384,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case GetBlocksPacket: { - clogS(NetMessageSummary) << "GetBlocks (" << dec << (_r.itemCount() - 1) << "entries)"; + clogS(NetMessageSummary) << "GetBlocks (" << dec << _r.itemCount() << "entries)"; // return the requested blocks. bytes rlp; unsigned n = 0; @@ -404,12 +404,12 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) } case BlocksPacket: { - clogS(NetMessageSummary) << "Blocks (" << dec << (_r.itemCount() - 1) << "entries)" << (_r.itemCount() - 1 ? "" : ": NoMoreBlocks"); + clogS(NetMessageSummary) << "Blocks (" << dec << _r.itemCount() << "entries)" << (_r.itemCount() ? "" : ": NoMoreBlocks"); if (m_asking != Asking::Blocks) clogS(NetWarn) << "Unexpected Blocks received!"; - if (_r.itemCount() == 1) + if (_r.itemCount() == 0) { // Got to this peer's latest block - just give up. transition(Asking::Nothing); @@ -470,7 +470,7 @@ bool EthereumPeer::interpret(unsigned _id, RLP const& _r) auto h = BlockInfo::headerHash(_r[0].data()); clogS(NetMessageSummary) << "NewBlock: " << h.abridged(); - if (_r.itemCount() != 3) + if (_r.itemCount() != 2) disable("NewBlock without 2 data fields."); else { From 71ccb9c62b36a5ca4289968d3b6bcc40a2dd51f6 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 17:04:29 +0100 Subject: [PATCH 34/50] curlies --- libdevcrypto/CryptoPP.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 356317935..77893736f 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -55,13 +55,13 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) k.reserve(k.size() + h256::size); move(digest.begin(), digest.end(), back_inserter(k)); - if (ctr[3]++ && ctr[3] != 0) { + if (ctr[3]++ && ctr[3] != 0) continue; - } else if (ctr[2]++ && ctr[2] != 0) { + else if (ctr[2]++ && ctr[2] != 0) continue; - } else if (ctr[1]++ && ctr[1] != 0) { + else if (ctr[1]++ && ctr[1] != 0) continue; - } else + else ctr[0]++; } @@ -85,7 +85,7 @@ void Secp256k1::encryptECIES(Public const& _k, bytes& io_cipher) bytes cipherText; encryptSymNoAuth(*(Secret*)eKey.data(), bytesConstRef(&io_cipher), cipherText, h128()); - if (!cipherText.size()) + if (cipherText.empty()) return; bytes msg(1 + Public::size + h128::size + cipherText.size() + 32); From 4955f33797598c6c853c6dac360a4c79398b5d29 Mon Sep 17 00:00:00 2001 From: subtly Date: Fri, 6 Mar 2015 17:07:15 +0100 Subject: [PATCH 35/50] -whitespace --- libp2p/Peer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Peer.h b/libp2p/Peer.h index 743d941db..8774b6578 100644 --- a/libp2p/Peer.h +++ b/libp2p/Peer.h @@ -29,7 +29,7 @@ namespace dev namespace p2p { - + /** * @brief Representation of connectivity state and all other pertinent Peer metadata. * A Peer represents connectivity between two nodes, which in this case, are the host From fc1ea097aab2120f9e38f296c948d6a6904038da Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 00:09:36 +0100 Subject: [PATCH 36/50] packet-type interop w/go --- libp2p/Host.cpp | 19 ++++++------------- libp2p/Host.h | 2 -- libp2p/HostCapability.cpp | 5 ----- libp2p/HostCapability.h | 2 -- libp2p/RLPxFrameIO.cpp | 12 ++++++------ libp2p/RLPxHandshake.cpp | 11 +++++++---- libp2p/Session.cpp | 8 +------- 7 files changed, 20 insertions(+), 39 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index eb5e8adc1..5377d80b8 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -154,7 +154,7 @@ void Host::doneWorking() unsigned Host::protocolVersion() const { - return 4; + return 3; } bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) @@ -174,10 +174,10 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io // TODO: update pendingconns w/session-weak-ptr for graceful shutdown (otherwise this line isn't safe) p->endpoint.tcp.address(_endpoint.address()); - auto protocolVersion = _rlp[1].toInt(); - auto clientVersion = _rlp[2].toString(); - auto caps = _rlp[3].toVector(); - auto listenPort = _rlp[4].toInt(); + auto protocolVersion = _rlp[0].toInt(); + auto clientVersion = _rlp[1].toString(); + auto caps = _rlp[2].toVector(); + auto listenPort = _rlp[3].toInt(); // clang error (previously: ... << hex << caps ...) // "'operator<<' should be declared prior to the call site or in an associated namespace of one of its arguments" @@ -187,7 +187,7 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io clog(NetMessageSummary) << "Hello: " << clientVersion << "V[" << protocolVersion << "]" << _id.abridged() << showbase << capslog.str() << dec << listenPort; // create session so disconnects are managed - auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[3].toSet(), 0, map()})); + auto ps = make_shared(this, _io, p, PeerSessionInfo({_id, clientVersion, _endpoint.address().to_string(), listenPort, chrono::steady_clock::duration(), _rlp[2].toSet(), 0, map()})); if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); @@ -203,8 +203,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io ps->disconnect(DuplicatePeer); return false; } - - clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); m_sessions[_id] = ps; } ps->start(); @@ -217,7 +215,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); - return true; } @@ -271,10 +268,6 @@ void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) } } -void Host::seal(bytes& _b) -{ -} - void Host::determinePublic(string const& _publicAddress, bool _upnp) { m_peerAddresses.clear(); diff --git a/libp2p/Host.h b/libp2p/Host.h index 55f4d61b0..00fcc0943 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -176,8 +176,6 @@ private: /// Called only from startedWorking(). void runAcceptor(); - void seal(bytes& _b); - /// Called by Worker. Not thread-safe; to be called only by worker. virtual void startedWorking(); /// Called by startedWorking. Not thread-safe; to be called only be Worker. diff --git a/libp2p/HostCapability.cpp b/libp2p/HostCapability.cpp index 9437cd45c..b2acdcd1b 100644 --- a/libp2p/HostCapability.cpp +++ b/libp2p/HostCapability.cpp @@ -27,11 +27,6 @@ using namespace std; using namespace dev; using namespace dev::p2p; -void HostCapabilityFace::seal(bytes& _b) -{ - m_host->seal(_b); -} - std::vector,std::shared_ptr>> HostCapabilityFace::peerSessions() const { RecursiveGuard l(m_host->x_sessions); diff --git a/libp2p/HostCapability.h b/libp2p/HostCapability.h index 9122ca1fa..93086b1c9 100644 --- a/libp2p/HostCapability.h +++ b/libp2p/HostCapability.h @@ -57,8 +57,6 @@ protected: virtual void onStarting() {} virtual void onStopping() {} - void seal(bytes& _b); - private: Host* m_host = nullptr; }; diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index e9d3715a2..b5185cd43 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -96,7 +96,6 @@ 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; uint32_t len = (uint32_t)_packet.size(); header.appendRaw(bytes({byte((len >> 16) & 0xff), byte((len >> 8) & 0xff), byte(len & 0xff)})); @@ -105,10 +104,10 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) // TODO: SECURITY check that header is <= 16 bytes - bytes headerWithMac(32); - bytes headerBytes(16); - bytesConstRef(&header.out()).copyTo(&headerBytes); - m_frameEnc.ProcessData(headerWithMac.data(), headerBytes.data(), 16); + 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)); @@ -131,7 +130,8 @@ bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) asserts(io.size() == h256::size); updateIngressMACWithHeader(io); bytesConstRef macRef = io.cropped(h128::size, h128::size); - if (*(h128*)macRef.data() != ingressDigest()) + h128 expected = ingressDigest(); + if (*(h128*)macRef.data() != expected) return false; m_frameDec.ProcessData(io.data(), io.data(), h128::size); return true; diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 004880c39..a1ede6056 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -171,7 +171,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // old packet format // 5 arguments, HelloPacket RLPStream s; - s.appendList(5 + 1).append((unsigned)0) + s.append((unsigned)0).appendList(5) << m_host->protocolVersion() << m_host->m_clientVersion << m_host->caps() @@ -233,7 +233,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) transition(ec); else { - if (!m_io->authAndDecryptFrame(bytesRef(&m_handshakeInBuffer))) + bytesRef frame(&m_handshakeInBuffer); + if (!m_io->authAndDecryptFrame(frame)) { clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: decrypt failed"; m_nextState = Error; @@ -241,8 +242,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) return; } - RLP rlp(m_handshakeInBuffer); - auto packetType = (PacketType)rlp[0].toInt(); + PacketType packetType = (PacketType)(frame[0] == 0x80 ? 0x0 : frame[0]); if (packetType != 0) { clog(NetWarn) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: invalid packet type"; @@ -251,6 +251,9 @@ void RLPXHandshake::transition(boost::system::error_code _ech) return; } + clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; + + RLP rlp(frame.cropped(1)); m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } }); diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index da83c0b5e..5ceb61f8b 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -278,14 +278,13 @@ void Session::ping() RLPStream& Session::prep(RLPStream& _s, PacketType _id, unsigned _args) { - return _s.appendRaw(bytes(1, _id)).appendList(_args); + return _s.append((unsigned)_id).appendList(_args); } void Session::sealAndSend(RLPStream& _s) { bytes b; _s.swapOut(b); - m_server->seal(b); send(move(b)); } @@ -301,11 +300,6 @@ bool Session::checkPacket(bytesConstRef _msg) return true; } -//void Session::send(bytesConstRef _msg) -//{ -// send(_msg.toBytes()); -//} - void Session::send(bytes&& _msg) { clogS(NetLeft) << RLP(bytesConstRef(&_msg).cropped(1)); From 778b1802827798c94ba3df19193a0a7c8ec7570a Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 18:36:27 +0100 Subject: [PATCH 37/50] aes256, as per the spec. --- libp2p/RLPxFrameIO.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index b5185cd43..a3b3b5613 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -57,19 +57,19 @@ RLPXFrameIO::RLPXFrameIO(RLPXHandshake const& _init): m_socket(_init.m_socket) // aes-secret = sha3(ecdhe-shared-secret || shared-secret) sha3(keyMaterial, outRef); // output aes-secret - m_frameEncKey.resize(h128::size); - memcpy(m_frameEncKey.data(), outRef.data(), h128::size); - m_frameDecKey.resize(h128::size); - memcpy(m_frameDecKey.data(), outRef.data(), h128::size); + 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, h128::size, iv.data()); - m_frameDec.SetKeyWithIV(m_frameDecKey, h128::size, iv.data()); + 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(h128::size); - memcpy(m_macEncKey.data(), outRef.data(), h128::size); - m_macEnc.SetKey(m_macEncKey, h128::size); + 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) From 5902a8c2b0676bfc9bce26d8a1d9e49a3844721d Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 21:38:21 +0100 Subject: [PATCH 38/50] docs. better method name. remove unused code. --- libp2p/RLPxFrameIO.cpp | 18 +++----------- libp2p/RLPxFrameIO.h | 56 ++++++++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index a3b3b5613..2c233c1d4 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -120,7 +120,7 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) if (padding) m_frameEnc.ProcessData(paddingRef.data(), paddingRef.data(), padding); bytesRef packetWithPaddingRef(o_bytes.data() + 32, _packet.size() + padding); - updateEgressMACWithEndOfFrame(packetWithPaddingRef); + updateEgressMACWithFrame(packetWithPaddingRef); bytesRef macRef(o_bytes.data() + 32 + _packet.size() + padding, h128::size); egressDigest().ref().copyTo(macRef); } @@ -140,7 +140,7 @@ bool RLPXFrameIO::authAndDecryptHeader(bytesRef io) bool RLPXFrameIO::authAndDecryptFrame(bytesRef io) { bytesRef cipherText(io.cropped(0, io.size() - h128::size)); - updateIngressMACWithEndOfFrame(cipherText); + updateIngressMACWithFrame(cipherText); bytesConstRef frameMac(io.data() + io.size() - h128::size, h128::size); if (*(h128*)frameMac.data() != ingressDigest()) return false; @@ -169,15 +169,10 @@ void RLPXFrameIO::updateEgressMACWithHeader(bytesConstRef _headerCipher) updateMAC(m_egressMac, _headerCipher.cropped(0, 16)); } -void RLPXFrameIO::updateEgressMACWithEndOfFrame(bytesConstRef _cipher) +void RLPXFrameIO::updateEgressMACWithFrame(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); - } } void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) @@ -185,15 +180,10 @@ void RLPXFrameIO::updateIngressMACWithHeader(bytesConstRef _headerCipher) updateMAC(m_ingressMac, _headerCipher.cropped(0, 16)); } -void RLPXFrameIO::updateIngressMACWithEndOfFrame(bytesConstRef _cipher) +void RLPXFrameIO::updateIngressMACWithFrame(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); - } } void RLPXFrameIO::updateMAC(SHA3_256& _mac, bytesConstRef _seed) diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index 2bd2218aa..3a0fd35e0 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -38,6 +38,10 @@ namespace p2p class RLPXHandshake; +/** + * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO. + */ class RLPXSocket: public std::enable_shared_from_this { public: @@ -53,48 +57,68 @@ protected: bi::tcp::socket m_socket; }; +/** + * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + */ class RLPXFrameIO { friend class Session; public: + /// Constructor. + /// Requires instance of RLPXHandshake which has completed first two phases of handshake. RLPXFrameIO(RLPXHandshake const& _init); ~RLPXFrameIO() {} + /// Encrypt _packet as RLPx frame. void writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes); - /// Authenticates and decrypts header in-place. + /// Authenticate and decrypt header in-place. bool authAndDecryptHeader(bytesRef io_cipherWithMac); - /// Authenticates and decrypts frame in-place. + /// Authenticate and decrypt frame in-place. bool authAndDecryptFrame(bytesRef io_cipherWithMac); + /// Return first 16 bytes of current digest from egress mac. h128 egressDigest(); - + + /// Return first 16 bytes of current digest from ingress mac. h128 ingressDigest(); +protected: + /// Update state of egress MAC with frame header. void updateEgressMACWithHeader(bytesConstRef _headerCipher); + + /// Update state of egress MAC with frame. + void updateEgressMACWithFrame(bytesConstRef _cipher); - void updateEgressMACWithEndOfFrame(bytesConstRef _cipher); - + /// Update state of ingress MAC with frame header. void updateIngressMACWithHeader(bytesConstRef _headerCipher); - void updateIngressMACWithEndOfFrame(bytesConstRef _cipher); + /// Update state of ingress MAC with frame. + void updateIngressMACWithFrame(bytesConstRef _cipher); -protected: bi::tcp::socket& socket() { return m_socket->ref(); } private: + /// Update state of _mac. void updateMAC(CryptoPP::SHA3_256& _mac, bytesConstRef _seed = bytesConstRef()); - CryptoPP::SecByteBlock m_frameEncKey; - CryptoPP::SecByteBlock m_frameDecKey; - CryptoPP::CTR_Mode::Encryption m_frameEnc; - CryptoPP::CTR_Mode::Encryption m_frameDec; - CryptoPP::SecByteBlock m_macEncKey; - CryptoPP::ECB_Mode::Encryption m_macEnc; - Mutex x_macEnc; - CryptoPP::SHA3_256 m_egressMac; - CryptoPP::SHA3_256 m_ingressMac; + CryptoPP::SecByteBlock m_frameEncKey; ///< Key for m_frameEnc + CryptoPP::CTR_Mode::Encryption m_frameEnc; ///< Encoder for egress plaintext. + + CryptoPP::SecByteBlock m_frameDecKey; ///< Key for m_frameDec + CryptoPP::CTR_Mode::Encryption m_frameDec; ///< Decoder for egress plaintext. + + CryptoPP::SecByteBlock m_macEncKey; /// Key for m_macEnd + CryptoPP::ECB_Mode::Encryption m_macEnc; /// One-way coder used by updateMAC for ingress and egress MAC updates. + Mutex x_macEnc; /// Mutex + + CryptoPP::SHA3_256 m_egressMac; ///< State of MAC for egress ciphertext. + CryptoPP::SHA3_256 m_ingressMac; ///< State of MAC for ingress ciphertext. std::shared_ptr m_socket; }; From 41109e2b6c7111da603a7e6441329b48358bbb66 Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 22:28:38 +0100 Subject: [PATCH 39/50] docs. ensure non-random memory allocation headermac bytes. --- libp2p/RLPxFrameIO.cpp | 5 ++- libp2p/RLPxFrameIO.h | 6 ++++ libp2p/RLPxHandshake.cpp | 5 +-- libp2p/RLPxHandshake.h | 75 ++++++++++++++++++++++++++-------------- 4 files changed, 60 insertions(+), 31 deletions(-) diff --git a/libp2p/RLPxFrameIO.cpp b/libp2p/RLPxFrameIO.cpp index 2c233c1d4..cb768bfeb 100644 --- a/libp2p/RLPxFrameIO.cpp +++ b/libp2p/RLPxFrameIO.cpp @@ -104,9 +104,8 @@ void RLPXFrameIO::writeSingleFramePacket(bytesConstRef _packet, bytes& o_bytes) // TODO: SECURITY check that header is <= 16 bytes - bytes headerWithMac; - header.swapOut(headerWithMac); - headerWithMac.resize(32); + 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)); diff --git a/libp2p/RLPxFrameIO.h b/libp2p/RLPxFrameIO.h index 3a0fd35e0..0f0504e48 100644 --- a/libp2p/RLPxFrameIO.h +++ b/libp2p/RLPxFrameIO.h @@ -41,6 +41,12 @@ class RLPXHandshake; /** * @brief Encoder/decoder transport for RLPx connections established by RLPXHandshake. * Managed (via shared_ptr) socket for use by RLPXHandshake and RLPXFrameIO. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + * * an instance method must not be called concurrently + * * a writeSingleFramePacket can be called concurrent to authAndDecryptHeader OR authAndDecryptFrame */ class RLPXSocket: public std::enable_shared_from_this { diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index a1ede6056..801848107 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -133,6 +133,8 @@ void RLPXHandshake::error() { clog(NetConnect) << "Disconnecting " << m_socket->remoteEndpoint() << " (Handshake Failed)"; m_socket->close(); + if (m_io != nullptr) + delete m_io; } void RLPXHandshake::transition(boost::system::error_code _ech) @@ -175,7 +177,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) << m_host->protocolVersion() << m_host->m_clientVersion << m_host->caps() - << m_host->m_tcpPublic.port() + << m_host->listenPort() << m_host->id(); bytes packet; s.swapOut(packet); @@ -252,7 +254,6 @@ void RLPXHandshake::transition(boost::system::error_code _ech) } clog(NetNote) << (m_originated ? "p2p.connect.egress" : "p2p.connect.ingress") << "hello frame: success. starting session."; - RLP rlp(frame.cropped(1)); m_host->startPeerSession(m_remote, rlp, m_io, m_socket->remoteEndpoint()); } diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index cd5e7fb83..7ea95173f 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -36,66 +36,89 @@ namespace dev namespace p2p { +/** + * @brief Setup inbound or outbound connection for communication over RLPXFrameIO. + * RLPx Spec: https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake + * + * @todo Implement StartSession transition via lambda which is passed to constructor. + * + * Thread Safety + * Distinct Objects: Safe. + * Shared objects: Unsafe. + */ class RLPXHandshake: public std::enable_shared_from_this { friend class RLPXFrameIO; + + /// Sequential states of handshake enum State { Error = -1, - New, // New->AckAuth [egress: tx auth, ingress: rx auth] - AckAuth, // AckAuth->WriteHello [egress: rx ack, ingress: tx ack] - WriteHello, // WriteHello [tx caps, rx caps, writehello] + New, + AckAuth, + WriteHello, ReadHello, StartSession }; public: - /// Handshake for ingress connection. Takes ownership of socket. + /// Setup incoming connection. 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. + /// Setup outbound connection. 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() {} + /// Start handshake. void start() { transition(); } protected: + /// Write Auth message to socket and transitions to AckAuth. void writeAuth(); + + /// Reads Auth message from socket and transitions to AckAuth. void readAuth(); + /// Write Ack message to socket and transitions to WriteHello. void writeAck(); + + /// Reads Auth message from socket and transitions to WriteHello. void readAck(); + /// Closes connection and ends transitions. void error(); + + /// Performs transition for m_nextState. void transition(boost::system::error_code _ech = boost::system::error_code()); - /// Current state of handshake. - State m_nextState = New; + State m_nextState = New; ///< Current or expected state of transition. - Host* m_host; + Host* m_host; ///< Host which provides m_alias, protocolVersion(), m_clientVersion, caps(), and TCP listenPort(). /// Node id of remote host for socket. - NodeId m_remote; - bool m_originated = false; + NodeId m_remote; ///< Public address of remote host. + bool m_originated = false; ///< True if connection is outbound. /// Buffers for encoded and decoded handshake phases - bytes m_auth; - bytes m_authCipher; - bytes m_ack; - bytes m_ackCipher; - bytes m_handshakeOutBuffer; - bytes m_handshakeInBuffer; - - crypto::ECDHE m_ecdhe; - h256 m_nonce; - - Public m_remoteEphemeral; - h256 m_remoteNonce; - - /// Frame IO is used to read frame for last step of handshake authentication. - RLPXFrameIO* m_io; - std::shared_ptr m_socket; + bytes m_auth; ///< Plaintext of egress or ingress Auth message. + bytes m_authCipher; ///< Ciphertext of egress or ingress Auth message. + bytes m_ack; ///< Plaintext of egress or ingress Ack message. + bytes m_ackCipher; ///< Ciphertext of egress or ingress Ack message. + bytes m_handshakeOutBuffer; ///< Frame buffer for egress Hello packet. + bytes m_handshakeInBuffer; ///< Frame buffer for ingress Hello packet. + + crypto::ECDHE m_ecdhe; ///< Ephemeral ECDH secret and agreement. + h256 m_nonce; ///< Nonce generated by this host for handshake. + + Public m_remoteEphemeral; ///< Remote ephemeral public key. + h256 m_remoteNonce; ///< Nonce generated by remote host for handshake. + + /// Used to read and write RLPx encrypted frames for last step of handshake authentication. + /// Passed onto Host which will take ownership. + RLPXFrameIO* m_io = nullptr; + + std::shared_ptr m_socket; ///< Socket. }; } From fada98b8d967d0739b7f39709ff6440ce5eb9d8c Mon Sep 17 00:00:00 2001 From: subtly Date: Sat, 7 Mar 2015 22:40:15 +0100 Subject: [PATCH 40/50] update headers, docs, remove unused length argument from fixed-length read --- libdevcrypto/Common.h | 10 +++++----- libdevcrypto/CryptoPP.h | 10 +++++----- libp2p/RLPxHandshake.cpp | 4 ++-- libp2p/RLPxHandshake.h | 1 - libp2p/Session.h | 3 +-- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 1bc16fa93..38e5649fb 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -96,19 +96,19 @@ void encryptSym(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); /// Symmetric decryption. bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); -/// Encrypt payload using ECIES standard with AES-CTR. TODO: move into class. +/// Encrypt payload using ECIES standard with AES128-CTR. void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); -/// Decrypt payload using ECIES standard with AES-CTR. TODO: move into class. +/// Decrypt payload using ECIES standard with AES128-CTR. bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); -/// Encrypts payload with random IV using AES-CTR. TODO: prefix IV. +/// Encrypts payload with random IV/ctr using AES128-CTR. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher); -/// Encrypts payload with specified IV using AES-CTR TODO: prefix IV. +/// Encrypts payload with specified IV/ctr using AES128-CTR. h128 encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_cipher, h128 const& _iv); -/// Decrypts payload with specified IV TODO: prefix IV. +/// Decrypts payload with specified IV/ctr. bool decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cipher, bytes& o_plaintext); /// Recovers Public key from signed message hash. diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index e0f5f16ba..b673c44a6 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -76,19 +76,19 @@ public: void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); } - /// Encrypts text (replace input). + /// Encrypts text (replace input). (ECIES w/XOR) void encrypt(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). + /// Decrypts text (replace input). (ECIES w/XOR) void decrypt(Secret const& _k, bytes& io_text); - /// Temporary; to replace encrypt once interop w/go is passing. + /// Encrypts text (replace input). (ECIES w/AES128-CTR) void encryptECIES(Public const& _k, bytes& io_cipher); - /// Temporary; to replace decrypt once interop w/go is passing. + /// Decrypts text (replace input). (ECIES w/AES128-CTR) bool decryptECIES(Secret const& _k, bytes& io_text); - /// Key derivation function used by ECIES. + /// Key derivation function used by encryptECIES and decryptECIES. bytes eciesKDF(Secret _z, bytes _s1, unsigned kdBitLen = 256); /// @returns siganture of message. diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index 801848107..fa346ec09 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -195,7 +195,7 @@ void RLPXHandshake::transition(boost::system::error_code _ech) // read frame header m_handshakeInBuffer.resize(h256::size); - ba::async_read(m_socket->ref(), 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) { if (ec) transition(ec); @@ -229,7 +229,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->ref(), 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) { if (ec) transition(ec); diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 7ea95173f..8459de053 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -25,7 +25,6 @@ #include #include #include -#include #include "RLPxFrameIO.h" #include "Common.h" namespace ba = boost::asio; diff --git a/libp2p/Session.h b/libp2p/Session.h index 77b7de7bb..51db5adc3 100644 --- a/libp2p/Session.h +++ b/libp2p/Session.h @@ -85,8 +85,7 @@ public: private: void send(bytes&& _msg); -// void send(bytesConstRef _msg); - + /// Drop the connection for the reason @a _r. void drop(DisconnectReason _r); From 6b5c3acf267a803a7c483f6702d0717817985163 Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 8 Mar 2015 13:45:23 +0100 Subject: [PATCH 41/50] fix kdf test --- test/crypto.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/crypto.cpp b/test/crypto.cpp index c20b4c51f..a844cbf09 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -267,14 +267,14 @@ BOOST_AUTO_TEST_CASE(ecies_kdf) // nonce Secret z1; ecdh::agree(local.sec(), remote.pub(), z1); - auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 512); + auto key1 = s_secp256k1.eciesKDF(z1, bytes(), 64); bytesConstRef eKey1 = bytesConstRef(&key1).cropped(0, 32); bytesRef mKey1 = bytesRef(&key1).cropped(32, 32); sha3(mKey1, mKey1); Secret z2; ecdh::agree(remote.sec(), local.pub(), z2); - auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 512); + auto key2 = s_secp256k1.eciesKDF(z2, bytes(), 64); bytesConstRef eKey2 = bytesConstRef(&key2).cropped(0, 32); bytesRef mKey2 = bytesRef(&key2).cropped(32, 32); sha3(mKey2, mKey2); From d78da4ca8fd72f0f96ab0109a7b2ab8e8b4bd6df Mon Sep 17 00:00:00 2001 From: subtly Date: Sun, 8 Mar 2015 13:48:49 +0100 Subject: [PATCH 42/50] coding standards --- libdevcrypto/Common.cpp | 8 ++++---- test/crypto.cpp | 12 ++++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 4abac7eed..e108b230f 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -129,9 +129,9 @@ h128 dev::encryptSymNoAuth(Secret const& _k, bytesConstRef _plain, bytes& o_ciph e.ProcessData(o_cipher.data(), _plain.data(), _plain.size()); return _iv; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; o_cipher.resize(0); return h128(); } @@ -150,9 +150,9 @@ bool dev::decryptSymNoAuth(Secret const& _k, h128 const& _iv, bytesConstRef _cip d.ProcessData(o_plaintext.data(), _cipher.data(), _cipher.size()); return true; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; o_plaintext.resize(0); return false; } diff --git a/test/crypto.cpp b/test/crypto.cpp index a844cbf09..2d7121f89 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -658,9 +658,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) BOOST_REQUIRE(text != original); cipherCopy = text; } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } try @@ -670,9 +670,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) d.ProcessData(out, in, text.size()); BOOST_REQUIRE(text == original); } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } @@ -690,9 +690,9 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) // yep, ctr mode. BOOST_REQUIRE(cipherCopy == original); } - catch(CryptoPP::Exception& e) + catch (CryptoPP::Exception& _e) { - cerr << e.what() << endl; + cerr << _e.what() << endl; } } From 529fda3a957aecb7f4ece6307225b7b5cfd89067 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 12:51:24 +0100 Subject: [PATCH 43/50] lifecycle management of handshake sessions --- libp2p/Host.cpp | 86 ++++++++++++++++++++++------------------ libp2p/Host.h | 5 ++- libp2p/RLPxHandshake.cpp | 2 + libp2p/RLPxHandshake.h | 2 + 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index 5377d80b8..e8e206233 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -121,6 +121,23 @@ void Host::doneWorking() for (auto const& h: m_capabilities) h.second->onStopping(); + // disconnect pending handshake, before peers, as a handshake may create a peer + for (unsigned n = 0;; n = 0) + { + { + Guard l(x_connecting); + for (auto i: m_connecting) + if (auto h = i.lock()) + { + h->cancel(); + n++; + } + } + if (!n) + break; + m_ioService.poll(); + } + // disconnect peers for (unsigned n = 0;; n = 0) { @@ -157,7 +174,7 @@ unsigned Host::protocolVersion() const return 3; } -bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) +void Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint) { /// Get or create Peer shared_ptr p; @@ -171,7 +188,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io if (p->isOffline()) 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(_endpoint.address()); auto protocolVersion = _rlp[0].toInt(); @@ -191,18 +207,20 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io if (protocolVersion != this->protocolVersion()) { ps->disconnect(IncompatibleProtocol); - return false; + return; } { RecursiveGuard l(x_sessions); if (m_sessions.count(_id) && !!m_sessions[_id].lock()) - { - // Already connected. - clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); - ps->disconnect(DuplicatePeer); - return false; - } + if (auto s = m_sessions[_id].lock()) + if(s->isConnected()) + { + // Already connected. + clog(NetWarn) << "Session already exists for peer with id" << _id.abridged(); + ps->disconnect(DuplicatePeer); + return; + } m_sessions[_id] = ps; } ps->start(); @@ -215,7 +233,6 @@ bool Host::startPeerSession(Public const& _id, RLP const& _rlp, RLPXFrameIO* _io } clog(NetNote) << "p2p.host.peer.register" << _id.abridged(); StructuredLogger::p2pConnected(_id.abridged(), ps->m_peer->peerEndpoint(), ps->m_peer->m_lastConnected, clientVersion, peerCount()); - return true; } void Host::onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e) @@ -340,22 +357,6 @@ void Host::runAcceptor() clog(NetConnect) << "Listening on local port " << m_listenPort << " (public: " << m_tcpPublic << ")"; m_accepting = true; - // socket is created outside of acceptor-callback - // An allocated socket is necessary as asio can use the socket - // until the callback succeeds or fails. - // - // Until callback succeeds or fails, we can't dealloc it. - // - // Callback is guaranteed to be called via asio or when - // m_tcp4Acceptor->stop() is called by Host. - // - // All exceptions are caught so they don't halt asio and so the - // socket is deleted. - // - // It's possible for an accepted connection to return an error in which - // case the socket may be open and must be closed to prevent asio from - // processing socket events after socket is deallocated. - auto socket = make_shared(new bi::tcp::socket(m_ioService)); m_tcp4Acceptor.async_accept(socket->ref(), [=](boost::system::error_code ec) { @@ -367,6 +368,7 @@ void Host::runAcceptor() { // incoming connection; we don't yet know nodeid auto handshake = make_shared(this, socket); + m_connecting.push_back(handshake); handshake->start(); success = true; } @@ -435,15 +437,14 @@ void Host::addNode(NodeId const& _node, std::string const& _addr, unsigned short void Host::connect(std::shared_ptr const& _p) { - for (unsigned i = 0; i < 200; i++) - if (isWorking() && !m_run) - this_thread::sleep_for(chrono::milliseconds(50)); if (!m_run) return; + _p->m_lastAttempted = std::chrono::system_clock::now(); + if (havePeerSession(_p->id)) { - clog(NetWarn) << "Aborted connect. Node already connected."; + clog(NetConnect) << "Aborted connect. Node already connected."; return; } @@ -475,10 +476,15 @@ void Host::connect(std::shared_ptr const& _p) } else { - clog(NetConnect) << "Connected to" << _p->id.abridged() << "@" << _p->peerEndpoint(); + clog(NetConnect) << "Connecting to" << _p->id.abridged() << "@" << _p->peerEndpoint(); auto handshake = make_shared(this, socket, _p->id); + { + Guard l(x_connecting); + m_connecting.push_back(handshake); + } handshake->start(); } + Guard l(x_pendingNodeConns); m_pendingPeerConns.erase(nptr); }); @@ -527,26 +533,30 @@ void Host::run(boost::system::error_code const&) m_nodeTable->processEvents(); + // cleanup zombies + { + Guard l(x_connecting); + m_connecting.remove_if([](std::weak_ptr h){ return h.lock(); }); + } + for (auto p: m_sessions) if (auto pp = p.second.lock()) pp->serviceNodesRequest(); -// keepAlivePeers(); + keepAlivePeers(); // disconnectLatePeers(); - auto c = peerCount(); - if (m_idealPeerCount && !c) + if (peerCount() < m_idealPeerCount) + { for (auto p: m_peers) if (p.second->shouldReconnect()) { - // TODO p2p: fixme - p.second->m_lastAttempted = std::chrono::system_clock::now(); connect(p.second); break; } - - if (c < m_idealPeerCount) + m_nodeTable->discover(); + } auto runcb = [this](boost::system::error_code const& error) { run(error); }; m_timer->expires_from_now(boost::posix_time::milliseconds(c_timerInterval)); diff --git a/libp2p/Host.h b/libp2p/Host.h index 00fcc0943..ccb34b064 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -151,7 +151,7 @@ public: NodeId id() const { return m_alias.pub(); } /// 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); + void startPeerSession(Public const& _id, RLP const& _hello, RLPXFrameIO* _io, bi::tcp::endpoint _endpoint); protected: void onNodeTableEvent(NodeId const& _n, NodeTableEventType const& _e); @@ -224,6 +224,9 @@ private: /// Mutable because we flush zombie entries (null-weakptrs) as regular maintenance from a const method. mutable std::map> m_sessions; mutable RecursiveMutex x_sessions; + + std::list> m_connecting; ///< Pending connections. + Mutex x_connecting; unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. diff --git a/libp2p/RLPxHandshake.cpp b/libp2p/RLPxHandshake.cpp index fa346ec09..4f5c70802 100644 --- a/libp2p/RLPxHandshake.cpp +++ b/libp2p/RLPxHandshake.cpp @@ -168,6 +168,8 @@ void RLPXHandshake::transition(boost::system::error_code _ech) else clog(NetConnect) << "p2p.connect.ingress sending capabilities handshake"; + /// This pointer will be freed if there is an error otherwise + /// it will be passed to Host which will take ownership. m_io = new RLPXFrameIO(*this); // old packet format diff --git a/libp2p/RLPxHandshake.h b/libp2p/RLPxHandshake.h index 8459de053..aac8f4b5a 100644 --- a/libp2p/RLPxHandshake.h +++ b/libp2p/RLPxHandshake.h @@ -72,6 +72,8 @@ public: /// Start handshake. void start() { transition(); } + void cancel() { m_nextState = Error; } + protected: /// Write Auth message to socket and transitions to AckAuth. void writeAuth(); From 3292c58599b36cba2f32c3e0728851f6886db421 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 18:14:49 +0100 Subject: [PATCH 44/50] bugfix and doc clarification. fix test. --- libdevcrypto/CryptoPP.cpp | 7 ++++--- libdevcrypto/CryptoPP.h | 8 ++++---- libp2p/Host.h | 2 +- test/crypto.cpp | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 77893736f..87da88742 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -319,13 +319,14 @@ bool Secp256k1::verifySecret(Secret const& _s, Public& _p) void Secp256k1::agree(Secret const& _s, Public const& _r, h256& o_s) { - (void)o_s; - (void)_s; + // TODO: mutex ASN1::secp256k1() singleton + // Creating Domain is non-const for m_oid and m_oid is not thread-safe ECDH::Domain d(ASN1::secp256k1()); assert(d.AgreedValueLength() == sizeof(o_s)); byte remote[65] = {0x04}; memcpy(&remote[1], _r.data(), 64); - assert(d.Agree(o_s.data(), _s.data(), remote)); + bool result = d.Agree(o_s.data(), _s.data(), remote); + assert(result); } void Secp256k1::exportPublicKey(CryptoPP::DL_PublicKey_EC const& _k, Public& o_p) diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index b673c44a6..4991e3713 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -76,16 +76,16 @@ public: void toPublic(Secret const& _s, Public& o_public) { exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public); } - /// Encrypts text (replace input). (ECIES w/XOR) + /// Encrypts text (replace input). (ECIES w/XOR-SHA1) void encrypt(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). (ECIES w/XOR) + /// Decrypts text (replace input). (ECIES w/XOR-SHA1) void decrypt(Secret const& _k, bytes& io_text); - /// Encrypts text (replace input). (ECIES w/AES128-CTR) + /// Encrypts text (replace input). (ECIES w/AES128-CTR-SHA256) void encryptECIES(Public const& _k, bytes& io_cipher); - /// Decrypts text (replace input). (ECIES w/AES128-CTR) + /// Decrypts text (replace input). (ECIES w/AES128-CTR-SHA256) bool decryptECIES(Secret const& _k, bytes& io_text); /// Key derivation function used by encryptECIES and decryptECIES. diff --git a/libp2p/Host.h b/libp2p/Host.h index ccb34b064..3b2ebe6aa 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -226,7 +226,7 @@ private: mutable RecursiveMutex x_sessions; std::list> m_connecting; ///< Pending connections. - Mutex x_connecting; + Mutex x_connecting; ///< Mutex for m_connecting. unsigned m_idealPeerCount = 5; ///< Ideal number of peers to be connected to. diff --git a/test/crypto.cpp b/test/crypto.cpp index 2d7121f89..dbbc2dfa0 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -299,7 +299,7 @@ BOOST_AUTO_TEST_CASE(ecies_standard) s_secp256k1.encryptECIES(k.pub(), b); BOOST_REQUIRE(b != asBytes(original)); - BOOST_REQUIRE(b.size() > 0 && ((u128)h128(b)) > 0); + BOOST_REQUIRE(b.size() > 0 && b[0] == 0x04); s_secp256k1.decryptECIES(k.sec(), b); BOOST_REQUIRE(bytesConstRef(&b).cropped(0, original.size()).toBytes() == asBytes(original)); From 80edf6163030f3bfe82b3c44d4531d71585831fe Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 18:37:50 +0100 Subject: [PATCH 45/50] changes from code review --- libp2p/Host.h | 2 +- libp2p/Session.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libp2p/Host.h b/libp2p/Host.h index 3b2ebe6aa..219316c58 100644 --- a/libp2p/Host.h +++ b/libp2p/Host.h @@ -50,7 +50,7 @@ namespace dev namespace p2p { - + class Host; class HostNodeTableHandler: public NodeTableEventHandler diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 5ceb61f8b..5caf5bd44 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -403,7 +403,7 @@ void Session::doRead() // ignore packets received while waiting to disconnect. if (m_dropped) return; - + auto self(shared_from_this()); ba::async_read(m_socket, boost::asio::buffer(m_data, h256::size), [this,self](boost::system::error_code ec, std::size_t length) { @@ -427,7 +427,7 @@ void Session::doRead() /// check frame size uint32_t frameSize = (m_data[0] * 256 + m_data[1]) * 256 + m_data[2]; - if (frameSize > 16777216) + if (frameSize >= (uint32_t)1 << 24) { clog(NetWarn) << "frame size too large"; drop(BadProtocol); From eeeb8421926406ac8d5cea0b4c947cb47f98fb18 Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 18:42:03 +0100 Subject: [PATCH 46/50] update doc for disabled code --- libp2p/Host.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libp2p/Host.cpp b/libp2p/Host.cpp index e8e206233..8f4f8a69d 100644 --- a/libp2p/Host.cpp +++ b/libp2p/Host.cpp @@ -544,7 +544,11 @@ void Host::run(boost::system::error_code const&) pp->serviceNodesRequest(); keepAlivePeers(); -// disconnectLatePeers(); + + // At this time peers will be disconnected based on natural TCP timeout. + // disconnectLatePeers needs to be updated for the assumption that Session + // is always live and to ensure reputation and fallback timers are properly + // updated. // disconnectLatePeers(); if (peerCount() < m_idealPeerCount) { From 7d1de2b4022dfedd4c4380494ae074526b682fcd Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 19:06:01 +0100 Subject: [PATCH 47/50] replace go counter logic. remove dead test code. --- libdevcrypto/CryptoPP.cpp | 8 +------- test/rlpx.cpp | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 87da88742..11e4c1472 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -55,14 +55,8 @@ bytes Secp256k1::eciesKDF(Secret _z, bytes _s1, unsigned kdByteLen) k.reserve(k.size() + h256::size); move(digest.begin(), digest.end(), back_inserter(k)); - if (ctr[3]++ && ctr[3] != 0) + if (++ctr[3] || ++ctr[2] || ++ctr[1] || ++ctr[0]) continue; - else if (ctr[2]++ && ctr[2] != 0) - continue; - else if (ctr[1]++ && ctr[1] != 0) - continue; - else - ctr[0]++; } k.resize(kdByteLen); diff --git a/test/rlpx.cpp b/test/rlpx.cpp index 593613106..6a86652fb 100644 --- a/test/rlpx.cpp +++ b/test/rlpx.cpp @@ -363,7 +363,6 @@ BOOST_AUTO_TEST_CASE(test_secrets_from_go) // test decrypt of frame headers for recvHello bytes plaintext(16); m_frameDec.ProcessData(plaintext.data(), recvHello.data(), h128::size); - cout << "decrypt recv got: " << *(h128*)plaintext.data(); } From f1efb7c3f9e3034fa9bea6421698505e2f06bf0c Mon Sep 17 00:00:00 2001 From: subtly Date: Mon, 9 Mar 2015 19:39:39 +0100 Subject: [PATCH 48/50] fix coding standards --- libp2p/Session.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libp2p/Session.cpp b/libp2p/Session.cpp index 5caf5bd44..2c6530a2d 100644 --- a/libp2p/Session.cpp +++ b/libp2p/Session.cpp @@ -310,7 +310,7 @@ void Session::send(bytes&& _msg) if (!m_socket.is_open()) return; - + bool doWrite = false; { Guard l(x_writeQueue); From 667af3a13648859a05c2bdc6783a54dd5e68082e Mon Sep 17 00:00:00 2001 From: winsvega Date: Tue, 10 Mar 2015 14:38:17 +0300 Subject: [PATCH 49/50] State Init And Transaction Tests fixed to poc9 --- test/stInitCodeTestFiller.json | 46 +++++----- test/stTransactionTestFiller.json | 147 +++++++++++++++++++++++++++--- test/ttTransactionTestFiller.json | 6 +- 3 files changed, 162 insertions(+), 37 deletions(-) diff --git a/test/stInitCodeTestFiller.json b/test/stInitCodeTestFiller.json index 902290121..cb1b4d14a 100644 --- a/test/stInitCodeTestFiller.json +++ b/test/stInitCodeTestFiller.json @@ -1,5 +1,5 @@ { - "TransactionContractCreation" : { + "TransactionCreateRandomInitCode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -58,7 +58,7 @@ "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", - "value" : "1" + "value" : "100" } }, @@ -66,7 +66,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -74,7 +74,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "70000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -84,7 +84,7 @@ "transaction" : { "data" : "0x600a80600c6000396000f200600160008035811a8100", - "gasLimit" : "21590", + "gasLimit" : "22000", "gasPrice" : "3", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -123,7 +123,7 @@ } }, - "TransactionSuicideInitCode" : { + "TransactionCreateAutoSuicideContract" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -150,11 +150,11 @@ "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", "to" : "", - "value" : "1" + "value" : "15" } }, - "TransactionStopInitCode" : { + "TransactionCreateStopInInitcode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -185,7 +185,7 @@ } }, - "TransactionCreateSuicideContract" : { + "TransactionCreateSuicideInInitcode" : { "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", @@ -220,7 +220,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -235,7 +235,7 @@ }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -245,7 +245,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "40000", + "gasLimit" : "100000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -258,7 +258,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "1000000", + "currentGasLimit" : "100000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -273,7 +273,7 @@ }, "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -283,7 +283,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "40000", + "gasLimit" : "400000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -347,7 +347,7 @@ "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 500 (SLOAD 0) 1 0 0 0 0)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 50000 (SLOAD 0) 1 0 0 0 0)}", "storage": {} }, @@ -427,7 +427,7 @@ "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 0 (SLOAD 0) 1 0 0 0 0)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1 11 21)(CALL 100000 (SLOAD 0) 1 0 0 0 0)}", "storage": {} }, @@ -442,7 +442,7 @@ "transaction" : { "data" : "0x00", - "gasLimit" : "20000000", + "gasLimit" : "1000000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -463,11 +463,11 @@ "pre" : { "095e7baea6a6c7c4c2dfeb977efac326af552d87": { - "balance": "1000", + "balance": "10000", "nonce": "0", "//": "(CREATE 0 64 32)", "//": "{[[0]] 12 (MSTORE 32 0x602060406000f0)(RETURN 57 7)}", - "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 1001 11 21)}", + "code": "{(MSTORE 0 0x600c60005566602060406000f060205260076039f3)[[0]](CREATE 100000 11 21)}", "storage": {} }, @@ -534,7 +534,7 @@ "env" : { "currentCoinbase" : "2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", "currentDifficulty" : "45678256", - "currentGasLimit" : "100000000", + "currentGasLimit" : "1000000000", "currentNumber" : "0", "currentTimestamp" : 1, "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" @@ -542,7 +542,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "100000", + "balance" : "1000000", "code" : "{(MSTORE 0 0x15)(CALL 7000 0xb94f5374fce5edbc8e2a8697c15331677e6ebf0b 0 0 32 32 32) (RETURN 0 64)}", "nonce" : "0", "storage" : { @@ -560,7 +560,7 @@ "transaction" : { "data" : "", - "gasLimit" : "25000", + "gasLimit" : "250000", "gasPrice" : "1", "nonce" : "0", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index 9bd7436bc..a69f0df27 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -514,7 +514,7 @@ "transaction" : { "data" : "", - "gasLimit" : "21100", + "gasLimit" : "23000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -535,7 +535,7 @@ "pre" : { "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { - "balance" : "22000", + "balance" : "1000000", "code" : "", "nonce" : "0", "storage" : { @@ -545,7 +545,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", "//" : "gas = 19 going OOG, gas = 20 fine", - "code" : "{ (CALL 19 0 1 0 0 0 0) }", + "code" : "{ (CALL 40000 0 1 0 0 0 0) }", "nonce" : "0", "storage" : { } @@ -574,7 +574,67 @@ "transaction" : { "data" : "", - "gasLimit" : "21100", + "gasLimit" : "160000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "InternlCallStoreClearsSucces" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "1000000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "//" : "gas = 19 going OOG, gas = 20 fine", + "code" : "{ (CALL 100000 0 1 0 0 0 0) }", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0)(SSTORE 4 0)(SSTORE 5 0)(SSTORE 6 0)(SSTORE 7 0)(SSTORE 8 0)(SSTORE 9 0)}", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c", + "0x05" : "0x0c", + "0x06" : "0x0c", + "0x07" : "0x0c", + "0x08" : "0x0c", + "0x09" : "0x0c" + } + } + + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "160000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -605,7 +665,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", "//" : "gas = 19 going OOG, gas = 20 fine", - "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 19 0 1 0 0 0 0) }", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 20000 0 1 0 0 0 0) }", "nonce" : "0", "storage" : { "0x" : "0x0c", @@ -639,7 +699,72 @@ "transaction" : { "data" : "", - "gasLimit" : "23000", + "gasLimit" : "200000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "StoreClearsAndInternlCallStoreClearsSuccess" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "10000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "500000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "//" : "gas = 19 going OOG, gas = 20 fine", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0) (CALL 50000 0 1 0 0 0 0) }", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c" + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SSTORE 0 0)(SSTORE 1 0)(SSTORE 2 0)(SSTORE 3 0)(SSTORE 4 0)(SSTORE 5 0)(SSTORE 6 0)(SSTORE 7 0)(SSTORE 8 0)(SSTORE 9 0)}", + "nonce" : "0", + "storage" : { + "0x" : "0x0c", + "0x01" : "0x0c", + "0x02" : "0x0c", + "0x03" : "0x0c", + "0x04" : "0x0c", + "0x05" : "0x0c", + "0x06" : "0x0c", + "0x07" : "0x0c", + "0x08" : "0x0c", + "0x09" : "0x0c" + } + } + + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "200000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -669,7 +794,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10", - "code" : "{(CALL 20000 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", + "code" : "{(CALL 22000 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", "nonce" : "0", "storage" : { } @@ -697,7 +822,7 @@ } }, - "SuicidesAndInternlCallSuicides" : { + "SuicidesAndInternlCallSuicidesSuccess" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", "currentDifficulty" : "45678256", @@ -737,7 +862,7 @@ "transaction" : { "data" : "", - "gasLimit" : "50000", + "gasLimit" : "150000", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", @@ -767,7 +892,7 @@ "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { "balance" : "10000", - "code" : "{(SUICIDE 0) (CALL 2000 0x0000000000000000000000000000000000000000 0 0 0 0 0) }", + "code" : "{(SUICIDE 0) (CALL 30000 0x0000000000000000000000000000000000000000 0 0 0 0 0) }", "nonce" : "0", "storage" : { } @@ -785,7 +910,7 @@ "transaction" : { "data" : "", - "gasLimit" : "33700", + "gasLimit" : "83700", "gasPrice" : "1", "nonce" : "", "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", diff --git a/test/ttTransactionTestFiller.json b/test/ttTransactionTestFiller.json index 96e4c78a6..105b64abc 100644 --- a/test/ttTransactionTestFiller.json +++ b/test/ttTransactionTestFiller.json @@ -14,7 +14,7 @@ } }, - "WrongVRSTestVl26" : { + "WrongVRSTestVEqual26" : { "transaction" : { "data" : "", @@ -29,7 +29,7 @@ } }, - "WrongVRSTestVl29" : { + "WrongVRSTestVEqual29" : { "transaction" : { "data" : "", @@ -44,7 +44,7 @@ } }, - "WrongVRSTestVge31" : { + "WrongVRSTestVEqual31" : { "transaction" : { "data" : "", From 3cb340bae6c284b5ce4275b48c42f38e055cb550 Mon Sep 17 00:00:00 2001 From: winsvega Date: Tue, 10 Mar 2015 23:05:59 +0300 Subject: [PATCH 50/50] State Transaction Tests bonus gas at call with value transfer --- test/stTransactionTestFiller.json | 97 ++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/test/stTransactionTestFiller.json b/test/stTransactionTestFiller.json index a69f0df27..5d07bf763 100644 --- a/test/stTransactionTestFiller.json +++ b/test/stTransactionTestFiller.json @@ -773,6 +773,102 @@ } }, + "SuicidesAndInternlCallSuicidesBonusGasAtCall" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "code" : "{(CALL 0 0x0000000000000000000000000000000000000000 1 0 0 0 0) (SUICIDE 0)}", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SUICIDE 0x0000000000000000000000000000000000000001)}", + "nonce" : "0", + "storage" : { + } + } + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + + "SuicidesAndInternlCallSuicidesBonusGasAtCallFailed" : { + "env" : { + "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "currentDifficulty" : "45678256", + "currentGasLimit" : "1000000", + "currentNumber" : "0", + "currentTimestamp" : 1, + "previousHash" : "5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6" + }, + "pre" : + { + "a94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "100000", + "code" : "", + "nonce" : "0", + "storage" : { + } + }, + + "c94f5374fce5edbc8e2a8697c15331677e6ebf0b" : { + "balance" : "10", + "code" : "{(CALL 0 0x0000000000000000000000000000000000000000 0 0 0 0 0) (SUICIDE 0)}", + "nonce" : "0", + "storage" : { + } + }, + + "0000000000000000000000000000000000000000" : { + "balance" : "0", + "code" : "{(SUICIDE 0x0000000000000000000000000000000000000001)}", + "nonce" : "0", + "storage" : { + } + } + }, + + "transaction" : + { + "data" : "", + "gasLimit" : "50000", + "gasPrice" : "1", + "nonce" : "", + "secretKey" : "45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8", + "to" : "c94f5374fce5edbc8e2a8697c15331677e6ebf0b", + "value" : "10" + } + }, + "SuicidesAndInternlCallSuicidesOOG" : { "env" : { "currentCoinbase" : "b94f5374fce5edbc8e2a8697c15331677e6ebf0b", @@ -807,7 +903,6 @@ "storage" : { } } - }, "transaction" :