diff --git a/test/TestHelperCrypto.h b/test/TestHelperCrypto.h new file mode 100644 index 000000000..6feeeb97f --- /dev/null +++ b/test/TestHelperCrypto.h @@ -0,0 +1,56 @@ +/* + 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 +#include +#include +#include +#include +#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 55feb1a54..25106a77f 100644 --- a/test/crypto.cpp +++ b/test/crypto.cpp @@ -27,13 +27,170 @@ #include #include #include +#include "TestHelperCrypto.h" using namespace std; using namespace dev; -using namespace dev::eth; +namespace dev +{ +namespace crypto +{ + +inline CryptoPP::AutoSeededRandomPool& PRNG() { + static CryptoPP::AutoSeededRandomPool prng; + return prng; +} + +} +} + +using namespace CryptoPP; + +BOOST_AUTO_TEST_SUITE(crypto) + +BOOST_AUTO_TEST_CASE(cryptopp_ecies_message) +{ + cnote << "Testing cryptopp_ecies_message..."; + + string const message("Now is the time for all good men to come to the aide of humanity."); + + AutoSeededRandomPool prng; + + ECIES::Decryptor localDecryptor(prng, ASN1::secp256r1()); + SavePrivateKey(localDecryptor.GetPrivateKey()); + + ECIES::Encryptor localEncryptor(localDecryptor); + SavePublicKey(localEncryptor.GetPublicKey()); -BOOST_AUTO_TEST_CASE(crypto_tests) + ECIES::Decryptor futureDecryptor; + LoadPrivateKey(futureDecryptor.AccessPrivateKey()); + futureDecryptor.GetPrivateKey().ThrowIfInvalid(prng, 3); + + ECIES::Encryptor futureEncryptor; + LoadPublicKey(futureEncryptor.AccessPublicKey()); + futureEncryptor.GetPublicKey().ThrowIfInvalid(prng, 3); + + // encrypt/decrypt with local + string cipherLocal; + StringSource ss1 (message, true, new PK_EncryptorFilter(prng, localEncryptor, new StringSink(cipherLocal) ) ); + string plainLocal; + StringSource ss2 (cipherLocal, true, new PK_DecryptorFilter(prng, localDecryptor, new StringSink(plainLocal) ) ); + + // encrypt/decrypt with future + string cipherFuture; + StringSource ss3 (message, true, new PK_EncryptorFilter(prng, futureEncryptor, new StringSink(cipherFuture) ) ); + string plainFuture; + StringSource ss4 (cipherFuture, true, new PK_DecryptorFilter(prng, futureDecryptor, new StringSink(plainFuture) ) ); + + // decrypt local w/future + string plainFutureFromLocal; + StringSource ss5 (cipherLocal, true, new PK_DecryptorFilter(prng, futureDecryptor, new StringSink(plainFutureFromLocal) ) ); + + // decrypt future w/local + string plainLocalFromFuture; + StringSource ss6 (cipherFuture, true, new PK_DecryptorFilter(prng, localDecryptor, new StringSink(plainLocalFromFuture) ) ); + + + assert(plainLocal == message); + assert(plainFuture == plainLocal); + assert(plainFutureFromLocal == plainLocal); + assert(plainLocalFromFuture == plainLocal); +} + +BOOST_AUTO_TEST_CASE(cryptopp_ecdh_prime) +{ + cnote << "Testing cryptopp_ecdh_prime..."; + + using namespace CryptoPP; + OID curve = ASN1::secp256r1(); + + ECDH::Domain dhLocal(curve); + SecByteBlock privLocal(dhLocal.PrivateKeyLength()); + SecByteBlock pubLocal(dhLocal.PublicKeyLength()); + dhLocal.GenerateKeyPair(dev::crypto::PRNG(), privLocal, pubLocal); + + ECDH::Domain dhRemote(curve); + SecByteBlock privRemote(dhRemote.PrivateKeyLength()); + SecByteBlock pubRemote(dhRemote.PublicKeyLength()); + dhRemote.GenerateKeyPair(dev::crypto::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); +} + +BOOST_AUTO_TEST_CASE(cryptopp_ecdh_aes128_cbc_noauth) +{ + // ECDH gives 256-bit shared while aes uses 128-bits + // Use first 128-bits of shared secret as symmetric key + // IV is 0 + // New connections require new ECDH keypairs + + +} + +BOOST_AUTO_TEST_CASE(cryptopp_eth_fbba) +{ + // Initial Authentication: + // + // New/Known Peer: + // pubkeyL = knownR? ? myKnown : myECDH + // pubkeyR = knownR? ? theirKnown : theirECDH + // + // Initial message = hmac(k=sha3(shared-secret[128..255]), address(pubkeyL)) || ECIES encrypt(pubkeyR, pubkeyL) + // + // Key Exchange (this could occur after handshake messages): + // If peers do not know each other they will need to exchange public keys. + // + // Drop ECDH (this could occur after handshake messages): + // After authentication and/or key exchange, both sides generate shared key + // from their 'known' keys and use this to encrypt all future messages. + // + // v2: If one side doesn't trust the other then a single-use key maybe sent. + // This will need to be tracked for future connections; when non-trusting peer + // wants to trust the other, it can request that it's old, 'new', public key be + // accepted. And, if the peer *really* doesn't trust the other side, it can request + // that a new, 'new', public key be accepted. + // + // Handshake (all or nothing, padded): + // All Peers (except blacklisted): + // + // + // New Peer: + // + // + // Known Untrusted Peer: + // + // + // Known Trusted Peer: + // + // + // Blacklisted Peeer: + // Already dropped by now. + // + // + // MAC: + // ... +} + +BOOST_AUTO_TEST_CASE(eth_keypairs) { cnote << "Testing Crypto..."; secp256k1_start(); @@ -42,7 +199,7 @@ BOOST_AUTO_TEST_CASE(crypto_tests) BOOST_REQUIRE(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f"))); BOOST_REQUIRE(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075"))); { - Transaction t; + eth::Transaction t; t.nonce = 0; t.receiveAddress = h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")); t.value = 1000; @@ -70,7 +227,7 @@ int cryptoTest() assert(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f"))); assert(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075"))); { - Transaction t; + eth::Transaction t; t.nonce = 0; t.receiveAddress = h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")); t.value = 1000; @@ -156,3 +313,5 @@ int cryptoTest() return 0; } +BOOST_AUTO_TEST_SUITE_END() +