diff --git a/libdevcore/Common.h b/libdevcore/Common.h index 87cc069b3..198119f24 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -59,6 +59,7 @@ using bytesConstRef = vector_ref; // Numeric types. using bigint = boost::multiprecision::number>; +using u128 = boost::multiprecision::number>; using u256 = boost::multiprecision::number>; using s256 = boost::multiprecision::number>; using u160 = boost::multiprecision::number>; diff --git a/libdevcore/FixedHash.h b/libdevcore/FixedHash.h index 2353a100c..0e387ab8a 100644 --- a/libdevcore/FixedHash.h +++ b/libdevcore/FixedHash.h @@ -240,6 +240,7 @@ using h520 = FixedHash<65>; using h512 = FixedHash<64>; using h256 = FixedHash<32>; using h160 = FixedHash<20>; +using h128 = FixedHash<16>; using h512s = std::vector; using h256s = std::vector; using h160s = std::vector; diff --git a/libdevcrypto/AES.cpp b/libdevcrypto/AES.cpp index 109ba9646..fc0cf15c4 100644 --- a/libdevcrypto/AES.cpp +++ b/libdevcrypto/AES.cpp @@ -19,5 +19,33 @@ * @date 2014 */ +#include "CryptoPP.h" #include "AES.h" +using namespace std; +using namespace dev::crypto::aes; +using namespace dev::crypto::pp; +using namespace CryptoPP; + +Stream::Stream(StreamType _t, h128 _ckey): + m_cSecret(_ckey) +{ + (void)_t; // encrypt and decrypt are same operation w/ctr mode + cryptor = new Aes128Ctr(_ckey); +} + +Stream::~Stream() +{ + delete cryptor; +} + +void Stream::update(bytesRef io_bytes) +{ + +} + +size_t Stream::streamOut(bytes& o_bytes) +{ + +} + diff --git a/libdevcrypto/AES.h b/libdevcrypto/AES.h index 95525685b..753dcd14b 100644 --- a/libdevcrypto/AES.h +++ b/libdevcrypto/AES.h @@ -29,10 +29,10 @@ namespace dev { namespace crypto { +namespace pp { struct Aes128Ctr; } namespace aes { -using Secret128 = FixedHash<16>; enum StreamType { Encrypt, Decrypt }; /** @@ -41,18 +41,20 @@ enum StreamType { Encrypt, Decrypt }; class Stream { public: - Stream(StreamType _t, Secret128 _encS, bool _zero = true): m_type(_t), m_zeroInput(_zero), m_encSecret(_encS) {}; + // streamtype maybe irrelevant w/ctr + Stream(StreamType _t, h128 _ckey); + ~Stream(); - virtual void update(bytesRef io_bytes) {}; + virtual void update(bytesRef io_bytes); /// Move ciphertext to _bytes. - virtual size_t streamOut(bytes& o_bytes) {}; + virtual size_t streamOut(bytes& o_bytes); private: - StreamType m_type; - bool m_zeroInput; - Secret128 m_encSecret; + h128 m_cSecret; bytes m_text; + + pp::Aes128Ctr* cryptor; }; /** @@ -61,16 +63,16 @@ private: class AuthenticatedStream: public Stream { public: - AuthenticatedStream(StreamType _t, Secret128 _encS, Secret128 _macS, unsigned _interval, bool _zero = true): Stream(_t, _encS, _zero), m_macSecret(_macS) { m_macInterval = _interval; } + AuthenticatedStream(StreamType _t, h128 _ckey, h128 _mackey, unsigned _interval): Stream(_t, _ckey), m_macSecret(_mackey) { m_macInterval = _interval; } - AuthenticatedStream(StreamType _t, Secret const& _s, unsigned _interval, bool _zero = true): Stream(_t, Secret128(_s), _zero), m_macSecret(FixedHash<16>(_s[0]+16)) { m_macInterval = _interval; } + AuthenticatedStream(StreamType _t, Secret const& _s, unsigned _interval): Stream(_t, h128(_s)), m_macSecret(FixedHash<16>(_s[0]+16)) { m_macInterval = _interval; } /// Adjust mac interval. Next mac will be xored with value. void adjustInterval(unsigned _interval) { m_macInterval = _interval; }; private: std::atomic m_macInterval; - Secret128 m_macSecret; + h128 m_macSecret; }; } diff --git a/libdevcrypto/All.h b/libdevcrypto/All.h index db6d7c615..8018db4fb 100644 --- a/libdevcrypto/All.h +++ b/libdevcrypto/All.h @@ -1,7 +1,6 @@ #pragma once #include "Common.h" -#include "CryptoPP.h" #include "EC.h" #include "FileSystem.h" #include "MemoryDB.h" diff --git a/libdevcrypto/CryptoPP.cpp b/libdevcrypto/CryptoPP.cpp index 87859ef9c..0694699ae 100644 --- a/libdevcrypto/CryptoPP.cpp +++ b/libdevcrypto/CryptoPP.cpp @@ -45,9 +45,11 @@ void pp::exponentToPublic(Integer const& _e, Public& _p) pp::exportPublicKey(pk, _p); } -void pp::ecdhAgree(Secret _s, Public _r, h256& o_s) +void pp::ecdhAgree(Secret const& _s, Public const& _r, h256& o_s) { ECDH::Domain d(secp256k1Curve); assert(d.AgreedValueLength() == sizeof(o_s)); - d.Agree(o_s.data(), _s.data(), _r.data()); + byte remote[65] = {0x04}; + memcpy(&remote[1], _r.data(), 64); + assert(d.Agree(o_s.data(), _s.data(), remote)); } diff --git a/libdevcrypto/CryptoPP.h b/libdevcrypto/CryptoPP.h index daf26bc3f..1ae4bee74 100644 --- a/libdevcrypto/CryptoPP.h +++ b/libdevcrypto/CryptoPP.h @@ -77,13 +77,19 @@ static void exportPrivateKey(CryptoPP::DL_PrivateKey_EC const& _k void exponentToPublic(Integer const& _e, Public& _p); -void ecdhAgree(Secret _s, Public _r, h256& o_s); +void ecdhAgree(Secret const& _s, Public const& _r, h256& o_s); template void initializeDLScheme(Secret const& _s, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, secretToExponent(_s)); } template void initializeDLScheme(Public const& _p, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, publicToPoint(_p)); } + +struct Aes128Ctr +{ + Aes128Ctr(h128 _k) { mode.SetKeyWithIV(_k.data(), sizeof(h128), Nonce::get().data()); } + CTR_Mode::Encryption mode; +}; } } diff --git a/libdevcrypto/ECDHE.cpp b/libdevcrypto/ECDHE.cpp index d785f467e..6b3979874 100644 --- a/libdevcrypto/ECDHE.cpp +++ b/libdevcrypto/ECDHE.cpp @@ -29,15 +29,24 @@ using namespace dev; using namespace dev::crypto; using namespace dev::crypto::pp; -void ECDHE::agree(Public _remote) +void ECDHE::agree(Public const& _remote, Secret& o_sharedSecret) { + if (m_remoteEphemeral) + // agreement can only occur once + BOOST_THROW_EXCEPTION(InvalidState()); + m_remoteEphemeral = _remote; - ecdhAgree(m_ephemeral.sec(), m_remoteEphemeral, m_sharedSecret); + ecdhAgree(m_ephemeral.sec(), m_remoteEphemeral, o_sharedSecret); +} + +void ECDHEKeyExchange::agree(Public const& _remoteEphemeral) +{ + ecdhAgree(m_ephemeral.sec(), _remoteEphemeral, m_ephemeralSecret); } void ECDHEKeyExchange::exchange(bytes& o_exchange) { - if (!m_sharedSecret) + if (!m_ephemeralSecret) // didn't agree on public remote BOOST_THROW_EXCEPTION(InvalidState()); @@ -60,12 +69,12 @@ void ECDHEKeyExchange::exchange(bytes& o_exchange) memcpy(exchange.data() - v.size(), v.data(), v.size()); h256 auth; - sha3mac(m_alias.m_secret.ref(), m_sharedSecret.ref(), auth.ref()); + sha3mac(m_alias.m_secret.ref(), m_ephemeralSecret.ref(), auth.ref()); Signature sig = crypto::sign(m_alias.m_secret, auth); exchange.resize(exchange.size() + sizeof(sig)); memcpy(exchange.data() - sizeof(sig), sig.data(), sizeof(sig)); - aes::AuthenticatedStream aes(aes::Encrypt, m_sharedSecret, 0); + aes::AuthenticatedStream aes(aes::Encrypt, m_ephemeralSecret, 0); h256 prefix(sha3((h256)(m_known.second|m_remoteEphemeral))); aes.update(prefix.ref()); diff --git a/libdevcrypto/ECDHE.h b/libdevcrypto/ECDHE.h index 11a5afa5c..88e5ba764 100644 --- a/libdevcrypto/ECDHE.h +++ b/libdevcrypto/ECDHE.h @@ -48,6 +48,7 @@ private: /** * @brief Derive DH shared secret from EC keypairs. + * As ephemeral keys are single-use, agreement is limited to a single occurence. */ class ECDHE { @@ -58,13 +59,12 @@ public: /// Public key sent to remote. Public pubkey() { return m_ephemeral.pub(); } - /// Provide public key for dh agreement to generated shared secret. - void agree(Public _remoteEphemeral); + /// Input public key for dh agreement, output generated shared secret. + void agree(Public const& _remoteEphemeral, Secret& o_sharedSecret); protected: KeyPair m_ephemeral; ///< Ephemeral keypair; generated. Public m_remoteEphemeral; ///< Public key of remote; parameter. - Secret m_sharedSecret; ///< Derived secret; derived by agree. }; /** @@ -73,7 +73,7 @@ protected: * * Usage: Agree -> Exchange -> Authenticate */ -class ECDHEKeyExchange: public ECDHE +class ECDHEKeyExchange: private ECDHE { public: /// Exchange with unknown remote (pass public key for ingress exchange) @@ -82,6 +82,9 @@ public: /// Exchange with known remote 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); + /// @returns encrypted payload of key exchange void exchange(bytes& o_exchange); @@ -89,6 +92,7 @@ public: bool authenticate(bytes _exchangeIn); private: + Secret m_ephemeralSecret; Alias m_alias; AliasSession m_known; Secret m_sharedAliasSecret; diff --git a/libethcore/CryptoHeaders.h b/libethcore/CryptoHeaders.h deleted file mode 100644 index 4ff63f1d7..000000000 --- a/libethcore/CryptoHeaders.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - 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 CryptoHeaders.h - * @author Tim Hughes - * @date 2014 - */ -#pragma once - -// need to leave this one disabled -#pragma GCC diagnostic ignored "-Wunused-function" - -#pragma warning(push) -#pragma warning(disable:4100 4244) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wconversion" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#include -#include -#include -#include -#pragma warning(pop) -#pragma GCC diagnostic pop diff --git a/test/TestHelperCrypto.h b/test/TestHelperCrypto.h deleted file mode 100644 index 01e97c21f..000000000 --- a/test/TestHelperCrypto.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - 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 TestHelperCrypto.h - * @author Alex Leverington - * @date 2014 - */ - -#pragma once - -#include - -using namespace std; -using namespace CryptoPP; - -void SavePrivateKey(const PrivateKey& key, const string& file = "ecies.private.key") -{ - FileSink sink(file.c_str()); - key.Save(sink); -} - -void SavePublicKey(const PublicKey& key, const string& file = "ecies.public.key") -{ - FileSink sink(file.c_str()); - key.Save(sink); -} - -void LoadPrivateKey(PrivateKey& key, const string& file = "ecies.private.key") -{ - FileSource source(file.c_str(), true); - key.Load(source); -} - -void LoadPublicKey(PublicKey& key, const string& file = "ecies.public.key") -{ - FileSource source(file.c_str(), true); - key.Load(source); -} diff --git a/test/crypto.cpp b/test/crypto.cpp index 06e55658a..a84c1fbb5 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -27,9 +27,10 @@ #include #include #include -#include #include -#include "TestHelperCrypto.h" +#include +#include +#include using namespace std; using namespace dev; @@ -40,7 +41,7 @@ BOOST_AUTO_TEST_SUITE(devcrypto) BOOST_AUTO_TEST_CASE(common_encrypt_decrypt) { - string message("Now is the time for all good persons to come to the aide of humanity."); + string message("Now is the time for all good persons to come to the aid of humanity."); bytes m = asBytes(message); bytesConstRef bcr(&m); @@ -267,7 +268,7 @@ BOOST_AUTO_TEST_CASE(ecies_eckeypair) { KeyPair k = KeyPair::create(); - string message("Now is the time for all good persons to come to the aide of humanity."); + string message("Now is the time for all good persons to come to the aid of humanity."); string original = message; bytes b = asBytes(message); @@ -278,61 +279,87 @@ BOOST_AUTO_TEST_CASE(ecies_eckeypair) BOOST_REQUIRE(b == asBytes(original)); } -BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac) +BOOST_AUTO_TEST_CASE(ecdh) { - // 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 + cnote << "Testing ecdh..."; + + ECDH::Domain dhLocal(pp::secp256k1Curve); + SecByteBlock privLocal(dhLocal.PrivateKeyLength()); + SecByteBlock pubLocal(dhLocal.PublicKeyLength()); + dhLocal.GenerateKeyPair(pp::PRNG, privLocal, pubLocal); + + ECDH::Domain dhRemote(pp::secp256k1Curve); + SecByteBlock privRemote(dhRemote.PrivateKeyLength()); + SecByteBlock pubRemote(dhRemote.PublicKeyLength()); + dhRemote.GenerateKeyPair(pp::PRNG, privRemote, pubRemote); + assert(dhLocal.AgreedValueLength() == dhRemote.AgreedValueLength()); + // local: send public to remote; remote: send public to local + + // Local + SecByteBlock sharedLocal(dhLocal.AgreedValueLength()); + assert(dhLocal.Agree(sharedLocal, privLocal, pubRemote)); + + // Remote + SecByteBlock sharedRemote(dhRemote.AgreedValueLength()); + assert(dhRemote.Agree(sharedRemote, privRemote, pubLocal)); + + // Test + Integer ssLocal, ssRemote; + ssLocal.Decode(sharedLocal.BytePtr(), sharedLocal.SizeInBytes()); + ssRemote.Decode(sharedRemote.BytePtr(), sharedRemote.SizeInBytes()); + + assert(ssLocal != 0); + assert(ssLocal == ssRemote); + + + // Now use our keys + KeyPair a = KeyPair::create(); + byte puba[65] = {0x04}; + memcpy(&puba[1], a.pub().data(), 64); + + KeyPair b = KeyPair::create(); + byte pubb[65] = {0x04}; + memcpy(&pubb[1], b.pub().data(), 64); + + ECDH::Domain dhA(pp::secp256k1Curve); + Secret shared; + BOOST_REQUIRE(dhA.Agree(shared.data(), a.sec().data(), pubb)); + BOOST_REQUIRE(shared); } -BOOST_AUTO_TEST_CASE(cryptopp_ecies_message) +BOOST_AUTO_TEST_CASE(ecdhe) { - cnote << "Testing cryptopp_ecies_message..."; - - string const message("Now is the time for all good persons to come to the aide of humanity."); - - ECIES::Decryptor localDecryptor(pp::PRNG, pp::secp256k1Curve); - SavePrivateKey(localDecryptor.GetPrivateKey()); + cnote << "Testing ecdhe..."; - ECIES::Encryptor localEncryptor(localDecryptor); - SavePublicKey(localEncryptor.GetPublicKey()); - - ECIES::Decryptor futureDecryptor; - LoadPrivateKey(futureDecryptor.AccessPrivateKey()); - futureDecryptor.GetPrivateKey().ThrowIfInvalid(pp::PRNG, 3); + ECDHE a, b; + BOOST_CHECK_NE(a.pubkey(), b.pubkey()); - ECIES::Encryptor futureEncryptor; - LoadPublicKey(futureEncryptor.AccessPublicKey()); - futureEncryptor.GetPublicKey().ThrowIfInvalid(pp::PRNG, 3); - - // encrypt/decrypt with local - string cipherLocal; - StringSource ss1 (message, true, new PK_EncryptorFilter(pp::PRNG, localEncryptor, new StringSink(cipherLocal) ) ); - string plainLocal; - StringSource ss2 (cipherLocal, true, new PK_DecryptorFilter(pp::PRNG, localDecryptor, new StringSink(plainLocal) ) ); - - // encrypt/decrypt with future - string cipherFuture; - StringSource ss3 (message, true, new PK_EncryptorFilter(pp::PRNG, futureEncryptor, new StringSink(cipherFuture) ) ); - string plainFuture; - StringSource ss4 (cipherFuture, true, new PK_DecryptorFilter(pp::PRNG, futureDecryptor, new StringSink(plainFuture) ) ); + ECDHE local; + ECDHE remote; - // decrypt local w/future - string plainFutureFromLocal; - StringSource ss5 (cipherLocal, true, new PK_DecryptorFilter(pp::PRNG, futureDecryptor, new StringSink(plainFutureFromLocal) ) ); + // local tx pubkey -> remote + Secret sremote; + remote.agree(local.pubkey(), sremote); - // decrypt future w/local - string plainLocalFromFuture; - StringSource ss6 (cipherFuture, true, new PK_DecryptorFilter(pp::PRNG, localDecryptor, new StringSink(plainLocalFromFuture) ) ); + // remote tx pbukey -> local + Secret slocal; + local.agree(remote.pubkey(), slocal); + + BOOST_REQUIRE(sremote); + BOOST_REQUIRE(slocal); + BOOST_REQUIRE_EQUAL(sremote, slocal); +} + +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_REQUIRE(plainLocal == message); - BOOST_REQUIRE(plainFuture == plainLocal); - BOOST_REQUIRE(plainFutureFromLocal == plainLocal); - BOOST_REQUIRE(plainLocalFromFuture == plainLocal); } BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) @@ -346,21 +373,29 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) rng.GenerateBlock(key, key.size()); // cryptopp uses IV as nonce/counter which is same as using nonce w/0 ctr - byte ctr[AES::BLOCKSIZE]; - rng.GenerateBlock(ctr, sizeof(ctr)); + FixedHash ctr; + rng.GenerateBlock(ctr.data(), sizeof(ctr)); + + // used for decrypt + FixedHash ctrcopy(ctr); - string text = "Now is the time for all good persons to come to the aide of humanity."; - // c++11 ftw + string text = "Now is the time for all good persons to come to the aid of humanity."; unsigned char const* in = (unsigned char*)&text[0]; unsigned char* out = (unsigned char*)&text[0]; string original = text; + string doublespeak = text + text; string cipherCopy; try { CTR_Mode::Encryption e; - e.SetKeyWithIV(key, key.size(), ctr); + e.SetKeyWithIV(key, key.size(), ctr.data()); + + // 68 % 255 should be difference of counter e.ProcessData(out, in, text.size()); + + (u128)ctr += (u128)(text.size() % 16); + BOOST_REQUIRE(text != original); cipherCopy = text; } @@ -372,7 +407,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) try { CTR_Mode< AES >::Decryption d; - d.SetKeyWithIV(key, key.size(), ctr); + d.SetKeyWithIV(key, key.size(), ctrcopy.data()); d.ProcessData(out, in, text.size()); BOOST_REQUIRE(text == original); } @@ -390,7 +425,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr) out = (unsigned char*)&cipherCopy[0]; CTR_Mode::Encryption e; - e.SetKeyWithIV(key, key.size(), ctr); + e.SetKeyWithIV(key, key.size(), ctrcopy.data()); e.ProcessData(out, in, text.size()); // yep, ctr mode.