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";