Browse Source

ecdh, ecdhe, initial aes classes

cl-refactor
subtly 10 years ago
parent
commit
0f86ce7545
  1. 1
      libdevcore/Common.h
  2. 1
      libdevcore/FixedHash.h
  3. 28
      libdevcrypto/AES.cpp
  4. 22
      libdevcrypto/AES.h
  5. 1
      libdevcrypto/All.h
  6. 6
      libdevcrypto/CryptoPP.cpp
  7. 8
      libdevcrypto/CryptoPP.h
  8. 19
      libdevcrypto/ECDHE.cpp
  9. 12
      libdevcrypto/ECDHE.h
  10. 36
      libethcore/CryptoHeaders.h
  11. 51
      test/TestHelperCrypto.h
  12. 143
      test/crypto.cpp

1
libdevcore/Common.h

@ -59,6 +59,7 @@ using bytesConstRef = vector_ref<byte const>;
// Numeric types.
using bigint = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<>>;
using u128 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<128, 128, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
using u256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;
using s256 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<256, 256, boost::multiprecision::signed_magnitude, boost::multiprecision::unchecked, void>>;
using u160 = boost::multiprecision::number<boost::multiprecision::cpp_int_backend<160, 160, boost::multiprecision::unsigned_magnitude, boost::multiprecision::unchecked, void>>;

1
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<h512>;
using h256s = std::vector<h256>;
using h160s = std::vector<h160>;

28
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)
{
}

22
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<unsigned> m_macInterval;
Secret128 m_macSecret;
h128 m_macSecret;
};
}

1
libdevcrypto/All.h

@ -1,7 +1,6 @@
#pragma once
#include "Common.h"
#include "CryptoPP.h"
#include "EC.h"
#include "FileSystem.h"
#include "MemoryDB.h"

6
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<ECP>::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));
}

8
libdevcrypto/CryptoPP.h

@ -77,13 +77,19 @@ static void exportPrivateKey(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> 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 <class T>
void initializeDLScheme(Secret const& _s, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, secretToExponent(_s)); }
template <class T>
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<AES>::Encryption mode;
};
}
}

19
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());

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

36
libethcore/CryptoHeaders.h

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @file CryptoHeaders.h
* @author Tim Hughes <tim@twistedfury.com>
* @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 <sha.h>
#include <sha3.h>
#include <ripemd.h>
#include <secp256k1/secp256k1.h>
#pragma warning(pop)
#pragma GCC diagnostic pop

51
test/TestHelperCrypto.h

@ -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 <http://www.gnu.org/licenses/>.
*/
/** @file TestHelperCrypto.h
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#pragma once
#include <libdevcrypto/CryptoPP.h>
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);
}

143
test/crypto.cpp

@ -27,9 +27,10 @@
#include <libdevcore/Log.h>
#include <libethereum/Transaction.h>
#include <boost/test/unit_test.hpp>
#include <libdevcrypto/EC.h>
#include <libdevcrypto/SHA3MAC.h>
#include "TestHelperCrypto.h"
#include <libdevcrypto/EC.h>
#include <libdevcrypto/ECDHE.h>
#include <libdevcrypto/CryptoPP.h>
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<ECP>::Domain dhLocal(pp::secp256k1Curve);
SecByteBlock privLocal(dhLocal.PrivateKeyLength());
SecByteBlock pubLocal(dhLocal.PublicKeyLength());
dhLocal.GenerateKeyPair(pp::PRNG, privLocal, pubLocal);
ECDH<ECP>::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<ECP>::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<ECP>::Decryptor localDecryptor(pp::PRNG, pp::secp256k1Curve);
SavePrivateKey(localDecryptor.GetPrivateKey());
cnote << "Testing ecdhe...";
ECIES<ECP>::Encryptor localEncryptor(localDecryptor);
SavePublicKey(localEncryptor.GetPublicKey());
ECIES<ECP>::Decryptor futureDecryptor;
LoadPrivateKey(futureDecryptor.AccessPrivateKey());
futureDecryptor.GetPrivateKey().ThrowIfInvalid(pp::PRNG, 3);
ECDHE a, b;
BOOST_CHECK_NE(a.pubkey(), b.pubkey());
ECIES<ECP>::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<AES::BLOCKSIZE> ctr;
rng.GenerateBlock(ctr.data(), sizeof(ctr));
// used for decrypt
FixedHash<AES::BLOCKSIZE> 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<AES>::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<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), ctr);
e.SetKeyWithIV(key, key.size(), ctrcopy.data());
e.ProcessData(out, in, text.size());
// yep, ctr mode.

Loading…
Cancel
Save