Browse Source

Merge pull request #424 from ethereum/crypto

Signature verification, fix keypair constructor, cleanup.
cl-refactor
subtly 10 years ago
parent
commit
4cba80a769
  1. 1
      libdevcore/Exceptions.h
  2. 144
      libdevcrypto/Common.cpp
  3. 31
      libdevcrypto/Common.h
  4. 47
      libdevcrypto/CryptoPP.cpp
  5. 42
      libdevcrypto/CryptoPP.h
  6. 168
      libdevcrypto/EC.cpp
  7. 23
      libdevcrypto/EC.h
  8. 1
      libdevcrypto/FileSystem.h
  9. 3
      libdevcrypto/SHA3MAC.cpp
  10. 1
      libwhisper/Message.h
  11. 297
      test/crypto.cpp

1
libdevcore/Exceptions.h

@ -24,7 +24,6 @@
#include <exception>
#include <boost/exception/all.hpp>
#include <boost/throw_exception.hpp>
#include <libdevcrypto/Common.h>
#include "CommonData.h"
#include "FixedHash.h"

144
libdevcrypto/Common.cpp

@ -14,53 +14,33 @@
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 CommonEth.cpp
/** @file Common.cpp
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*/
#include "Common.h"
#include <random>
#include <secp256k1/secp256k1.h>
#include <mutex>
#include "EC.h"
#include "SHA3.h"
#include "FileSystem.h"
#include "Common.h"
using namespace std;
using namespace dev;
using namespace crypto;
//#define ETH_ADDRESS_DEBUG 1
Address dev::toAddress(Secret _private)
Address dev::toAddress(Secret _secret)
{
secp256k1_start();
byte pubkey[65];
int pubkeylen = 65;
int ok = secp256k1_ecdsa_seckey_verify(_private.data());
if (!ok)
return Address();
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _private.data(), 0);
if (asserts(pubkeylen == 65))
return Address();
if (!ok)
return Address();
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65);
if (!ok)
return Address();
auto ret = right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
#if ETH_ADDRESS_DEBUG
cout << "---- ADDRESS -------------------------------" << endl;
cout << "SEC: " << _private << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
cout << "ADR: " << ret << endl;
#endif
return ret;
return KeyPair(_secret).address();
}
KeyPair KeyPair::create()
{
secp256k1_start();
static std::mt19937_64 s_eng(time(0));
std::uniform_int_distribution<uint16_t> d(0, 255);
static mt19937_64 s_eng(time(0));
uniform_int_distribution<uint16_t> d(0, 255);
for (int i = 0; i < 100; ++i)
{
@ -78,24 +58,10 @@ KeyPair KeyPair::create()
KeyPair::KeyPair(h256 _sec):
m_secret(_sec)
{
int ok = secp256k1_ecdsa_seckey_verify(m_secret.data());
if (!ok)
return;
byte pubkey[65];
int pubkeylen = 65;
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, m_secret.data(), 0);
if (!ok || pubkeylen != 65)
return;
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65);
if (!ok)
return;
m_secret = m_secret;
memcpy(m_public.data(), &(pubkey[1]), 64);
m_address = right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
toPublic(m_secret, m_public);
if (verifySecret(m_secret, m_public))
m_address = right160(dev::sha3(m_public.ref()));
#if ETH_ADDRESS_DEBUG
cout << "---- ADDRESS -------------------------------" << endl;
cout << "SEC: " << m_secret << endl;
@ -128,51 +94,55 @@ bool dev::decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext)
Public dev::recover(Signature _sig, h256 _message)
{
secp256k1_start();
byte pubkey[65];
int pubkeylen = 65;
if (!secp256k1_ecdsa_recover_compact(_message.data(), 32, _sig.data(), pubkey, &pubkeylen, 0, (int)_sig[64]))
return Public();
// right160(dev::sha3(bytesConstRef(&(pubkey[1]), 64)));
#if ETH_CRYPTO_TRACE
h256* sig = (h256 const*)_sig.data();
cout << "---- RECOVER -------------------------------" << endl;
cout << "MSG: " << _message << endl;
cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(_sig[64] - 27) << "+27" << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
#endif
Public ret;
memcpy(&ret, &(pubkey[1]), sizeof(Public));
return ret;
return crypto::recover(_sig, _message.ref());
}
inline h256 kFromMessage(h256 _msg, h256 _priv)
Signature dev::sign(Secret _k, h256 _hash)
{
return _msg ^ _priv;
return crypto::sign(_k, _hash);
}
Signature dev::sign(Secret _k, h256 _message)
bool dev::verify(Public _p, Signature _s, h256 _hash)
{
int v = 0;
secp256k1_start();
SignatureStruct ret;
h256 nonce = kFromMessage(_message, _k);
return crypto::verify(_p, _s, bytesConstRef(_hash.data(), 32), true);
}
if (!secp256k1_ecdsa_sign_compact(_message.data(), 32, ret.r.data(), _k.data(), nonce.data(), &v))
return Signature();
#if ETH_ADDRESS_DEBUG
cout << "---- SIGN -------------------------------" << endl;
cout << "MSG: " << _message << endl;
cout << "SEC: " << _k << endl;
cout << "NON: " << nonce << endl;
cout << "R S V: " << ret.r << " " << ret.s << " " << v << "+27" << endl;
#endif
h256 Nonce::get(bool _commit)
{
// todo: atomic efface bit, periodic save, kdf, rr, rng
// todo: encrypt
static h256 s_seed;
static string s_seedFile(getDataDir() + "/seed");
static mutex s_x;
lock_guard<mutex> l(s_x);
if (!s_seed)
{
static Nonce s_nonce;
bytes b = contents(s_seedFile);
if (b.size() == 32)
memcpy(s_seed.data(), b.data(), 32);
else
{
// todo: replace w/entropy from user and system
std::mt19937_64 s_eng(time(0));
std::uniform_int_distribution<uint16_t> d(0, 255);
for (unsigned i = 0; i < 32; ++i)
s_seed[i] = (byte)d(s_eng);
}
if (!s_seed)
BOOST_THROW_EXCEPTION(InvalidState());
// prevent seed reuse if process terminates abnormally
writeFile(s_seedFile, bytes());
}
h256 prev(s_seed);
sha3(prev.ref(), s_seed.ref());
if (_commit)
writeFile(s_seedFile, s_seed.asBytes());
return std::move(s_seed);
}
ret.v = v;
return *(Signature const*)&ret;
Nonce::~Nonce()
{
Nonce::get(true);
}

31
libdevcrypto/Common.h

@ -14,8 +14,9 @@
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 CommonEth.h
/** @file Common.h
* @author Gav Wood <i@gavwood.com>
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Ethereum-specific data structures & algorithms.
@ -25,6 +26,7 @@
#include <libdevcore/Common.h>
#include <libdevcore/FixedHash.h>
#include <libdevcore/Exceptions.h>
namespace dev
{
@ -62,15 +64,18 @@ Address toAddress(Secret _secret);
/// Encrypts plain text using Public key.
void encrypt(Public _k, bytesConstRef _plain, bytes& o_cipher);
/// Decrypts cipher using Secret key.
bool decrypt(Secret _k, bytesConstRef _cipher, bytes& o_plaintext);
/// Recovers Public key from signed message.
Public recover(Signature _sig, h256 _message);
/// Recovers Public key from signed message hash.
Public recover(Signature _sig, h256 _hash);
/// Returns siganture of message hash.
Signature sign(Secret _k, h256 _message);
Signature sign(Secret _k, h256 _hash);
/// Verify signature.
bool verify(Public _k, Signature _s, h256 _hash);
/// Simple class that represents a "key pair".
/// All of the data of the class can be regenerated from the secret key (m_secret) alone.
@ -110,4 +115,20 @@ private:
Address m_address;
};
namespace crypto
{
struct InvalidState: public dev::Exception {};
/**
* @brief Generator for nonce material
*/
struct Nonce
{
static h256 get(bool _commit = false);
private:
Nonce() {}
~Nonce();
};
}
}

47
libdevcrypto/CryptoPP.cpp

@ -25,50 +25,21 @@ using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
ECP::Point pp::PointFromPublic(Public const& _p)
{
ECP::Point p;
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pub;
pub.AccessGroupParameters().Initialize(pp::secp256k1());
bytes prefixedKey(pub.GetGroupParameters().GetEncodedElementSize(true));
prefixedKey[0] = 0x04;
assert(Public::size == prefixedKey.size() - 1);
memcpy(&prefixedKey[1], _p.data(), prefixedKey.size() - 1);
pub.GetGroupParameters().GetCurve().DecodePoint(p, prefixedKey.data(), prefixedKey.size());
return std::move(p);
}
Integer pp::ExponentFromSecret(Secret const& _s)
{
static_assert(Secret::size == 32, "Secret key must be 32 bytes.");
return std::move(Integer(_s.data(), Secret::size));
}
/// Integer and Point Conversion:
void pp::PublicFromExponent(Integer const& _e, Public& _p)
{
CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> k;
k.AccessGroupParameters().Initialize(secp256k1());
k.SetPrivateExponent(_e);
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> p;
p.AccessGroupParameters().Initialize(secp256k1());
k.MakePublicKey(p);
pp::PublicFromDL_PublicKey_EC(p, _p);
}
void pp::PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p)
void pp::exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p)
{
bytes prefixedKey(_k.GetGroupParameters().GetEncodedElementSize(true));
_k.GetGroupParameters().GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
static_assert(Public::size == 64, "Public key must be 64 bytes.");
secp256k1Params.GetCurve().EncodePoint(prefixedKey.data(), _k.GetPublicElement(), false);
assert(Public::size + 1 == _k.GetGroupParameters().GetEncodedElementSize(true));
memcpy(_p.data(), &prefixedKey[1], Public::size);
}
void pp::SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s)
void pp::exponentToPublic(Integer const& _e, Public& _p)
{
_k.GetPrivateExponent().Encode(_s.data(), Secret::size);
}
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pk;
pk.Initialize(secp256k1Params, secp256k1Params.ExponentiateBase(_e));
pp::exportPublicKey(pk, _p);
}

42
libdevcrypto/CryptoPP.h

@ -18,7 +18,7 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* CryptoPP headers and helper methods
* CryptoPP headers and primitive helper methods
*/
#pragma once
@ -45,7 +45,7 @@
#include <files.h>
#include <osrng.h>
#include <oids.h>
#include <secp256k1/secp256k1.h>
#include <dsa.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include "Common.h"
@ -54,30 +54,34 @@ namespace dev
{
namespace crypto
{
namespace pp
{
using namespace CryptoPP;
/// CryptoPP random number pool
static CryptoPP::AutoSeededRandomPool PRNG;
/// CryptoPP EC Cruve
static const CryptoPP::OID secp256k1Curve = CryptoPP::ASN1::secp256k1();
static const CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> secp256k1Params(secp256k1Curve);
static ECP::Point publicToPoint(Public const& _p) { Integer x(_p.data(), 32); Integer y(_p.data() + 32, 32); return std::move(ECP::Point(x,y)); }
static Integer secretToExponent(Secret const& _s) { return std::move(Integer(_s.data(), Secret::size)); }
/// RNG used by CryptoPP
inline CryptoPP::AutoSeededRandomPool& PRNG() { static CryptoPP::AutoSeededRandomPool prng; return prng; }
/// EC curve used by CryptoPP
inline CryptoPP::OID const& secp256k1() { static CryptoPP::OID curve = CryptoPP::ASN1::secp256k1(); return curve; }
/// Conversion from bytes to cryptopp point
CryptoPP::ECP::Point PointFromPublic(Public const& _p);
void exportPublicKey(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p);
/// Conversion from bytes to cryptopp exponent
CryptoPP::Integer ExponentFromSecret(Secret const& _s);
static void exportPrivateKey(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s) { _k.GetPrivateExponent().Encode(_s.data(), Secret::size); }
/// Conversion from cryptopp exponent Integer to bytes
void PublicFromExponent(CryptoPP::Integer const& _k, Public& _s);
void exponentToPublic(Integer const& _e, Public& _p);
/// Conversion from cryptopp public key to bytes
void PublicFromDL_PublicKey_EC(CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> const& _k, Public& _p);
template <class T>
void initializeDLScheme(Secret const& _s, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, secretToExponent(_s)); }
/// Conversion from cryptopp private key to bytes
void SecretFromDL_PrivateKey_EC(CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> const& _k, Secret& _s);
template <class T>
void initializeDLScheme(Public const& _p, T& io_operator) { io_operator.AccessKey().Initialize(pp::secp256k1Params, publicToPoint(_p)); }
}
}

168
libdevcrypto/EC.cpp

@ -18,54 +18,65 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Shared EC classes and functions.
* ECDSA, ECIES
*/
#pragma warning(push)
#pragma warning(disable:4100 4244)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wunused-parameter"
#pragma GCC diagnostic ignored "-Wunused-variable"
#pragma GCC diagnostic ignored "-Wdelete-non-virtual-dtor"
#pragma GCC diagnostic ignored "-Wextra"
#include <files.h>
#pragma warning(pop)
#pragma GCC diagnostic pop
#include <secp256k1/secp256k1.h>
#include "CryptoPP.h"
#include "SHA3.h"
#include "SHA3MAC.h"
#include "EC.h"
// CryptoPP and dev conflict so dev and pp namespace are used explicitly
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.");
using namespace std;
using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
using namespace pp;
void crypto::toPublic(Secret const& _s, Public& o_public)
{
exponentToPublic(Integer(_s.data(), sizeof(_s)), o_public);
}
void dev::crypto::encrypt(Public const& _key, bytes& io_cipher)
h256 crypto::kdf(Secret const& _priv, h256 const& _hash)
{
// H(H(r||k)^h)
h256 s;
sha3mac(Nonce::get().ref(), _priv.ref(), s.ref());
s ^= _hash;
sha3(s.ref(), s.ref());
if (!s || !_hash || !_priv)
BOOST_THROW_EXCEPTION(InvalidState());
return std::move(s);
}
void crypto::encrypt(Public const& _k, bytes& io_cipher)
{
ECIES<ECP>::Encryptor e;
e.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1());
e.AccessKey().SetPublicElement(pp::PointFromPublic(_key));
initializeDLScheme(_k, e);
size_t plen = io_cipher.size();
bytes c;
c.resize(e.CiphertextLength(plen));
// todo: use StringSource with _plain as input and output.
e.Encrypt(pp::PRNG(), io_cipher.data(), plen, c.data());
// todo: use StringSource with io_cipher as input and output.
e.Encrypt(PRNG, io_cipher.data(), plen, c.data());
memset(io_cipher.data(), 0, io_cipher.size());
io_cipher = std::move(c);
}
void dev::crypto::decrypt(Secret const& _k, bytes& io_text)
void crypto::decrypt(Secret const& _k, bytes& io_text)
{
CryptoPP::ECIES<CryptoPP::ECP>::Decryptor d;
d.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1());
d.AccessKey().SetPrivateExponent(pp::ExponentFromSecret(_k));
initializeDLScheme(_k, d);
size_t clen = io_text.size();
bytes p;
p.resize(d.MaxPlaintextLength(io_text.size()));
// todo: use StringSource with _c as input and output.
DecodingResult r = d.Decrypt(pp::PRNG(), io_text.data(), clen, p.data());
// todo: use StringSource with io_text as input and output.
DecodingResult r = d.Decrypt(PRNG, io_text.data(), clen, p.data());
if (!r.isValidCoding)
{
io_text.clear();
@ -75,3 +86,114 @@ void dev::crypto::decrypt(Secret const& _k, bytes& io_text)
io_text = std::move(p);
}
Signature crypto::sign(Secret const& _k, bytesConstRef _message)
{
return crypto::sign(_k, sha3(_message));
}
Signature crypto::sign(Secret const& _key, h256 const& _hash)
{
ECDSA<ECP,SHA3_256>::Signer signer;
initializeDLScheme(_key, signer);
Integer const& q = secp256k1Params.GetGroupOrder();
Integer const& qs = secp256k1Params.GetSubgroupOrder();
Integer e(_hash.asBytes().data(), 32);
Integer k(kdf(_key, _hash).data(), 32);
if (k == 0)
BOOST_THROW_EXCEPTION(InvalidState());
k = 1 + (k % (qs - 1));
ECP::Point rp = secp256k1Params.ExponentiateBase(k);
Integer r = secp256k1Params.ConvertElementToInteger(rp);
int recid = ((r >= q) ? 2 : 0) | (rp.y.IsOdd() ? 1 : 0);
Integer kInv = k.InverseMod(q);
Integer s = (kInv * (Integer(_key.asBytes().data(), 32)*r + e)) % q;
assert(!!r && !!s);
if (s > qs)
{
s = q - s;
if (recid)
recid ^= 1;
}
Signature sig;
r.Encode(sig.data(), 32);
s.Encode(sig.data() + 32, 32);
sig[64] = recid;
return sig;
}
bool crypto::verify(Signature const& _signature, bytesConstRef _message)
{
return crypto::verify(crypto::recover(_signature, _message), _signature, _message);
}
bool crypto::verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed)
{
static size_t derMaxEncodingLength = 72;
if (_hashed)
{
assert(_message.size() == 32);
byte encpub[65] = {0x04};
memcpy(&encpub[1], _p.data(), 64);
byte dersig[derMaxEncodingLength];
size_t cssz = DSAConvertSignatureFormat(dersig, derMaxEncodingLength, DSA_DER, _sig.data(), 64, DSA_P1363);
assert(cssz <= derMaxEncodingLength);
return (1 == secp256k1_ecdsa_verify(_message.data(), _message.size(), dersig, cssz, encpub, 65));
}
ECDSA<ECP, SHA3_256>::Verifier verifier;
initializeDLScheme(_p, verifier);
return verifier.VerifyMessage(_message.data(), _message.size(), _sig.data(), sizeof(Signature) - 1);
}
Public crypto::recover(Signature _signature, bytesConstRef _message)
{
secp256k1_start();
int pubkeylen = 65;
byte pubkey[pubkeylen];
if (!secp256k1_ecdsa_recover_compact(_message.data(), 32, _signature.data(), pubkey, &pubkeylen, 0, (int)_signature[64]))
return Public();
#if ETH_CRYPTO_TRACE
h256* sig = (h256 const*)_signature.data();
cout << "---- RECOVER -------------------------------" << endl;
cout << "MSG: " << _message << endl;
cout << "R S V: " << sig[0] << " " << sig[1] << " " << (int)(_signature[64] - 27) << "+27" << endl;
cout << "PUB: " << toHex(bytesConstRef(&(pubkey[1]), 64)) << endl;
#endif
Public ret;
memcpy(&ret, &(pubkey[1]), sizeof(Public));
return ret;
}
bool crypto::verifySecret(Secret const& _s, Public const& _p)
{
secp256k1_start();
int ok = secp256k1_ecdsa_seckey_verify(_s.data());
if (!ok)
return false;
int pubkeylen = 65;
byte pubkey[pubkeylen];
ok = secp256k1_ecdsa_pubkey_create(pubkey, &pubkeylen, _s.data(), 0);
if (!ok || pubkeylen != 65)
return false;
ok = secp256k1_ecdsa_pubkey_verify(pubkey, 65);
if (!ok)
return false;
for (int i = 0; i < 32; i++)
if (pubkey[i+1]!=_p[i])
return false;
return true;
}

23
libdevcrypto/EC.h

@ -18,7 +18,7 @@
* @author Alex Leverington <nessence@gmail.com>
* @date 2014
*
* Shared EC classes and functions.
* ECDSA, ECIES
*/
#pragma once
@ -30,12 +30,33 @@ namespace dev
namespace crypto
{
void toPublic(Secret const& _s, Public& o_public);
h256 kdf(Secret const& _priv, h256 const& _hash);
/// Encrypts text (in place).
void encrypt(Public const& _k, bytes& io_cipher);
/// Decrypts text (in place).
void decrypt(Secret const& _k, bytes& io_text);
/// Returns siganture of message.
Signature sign(Secret const& _k, bytesConstRef _message);
/// Returns compact siganture of message hash.
Signature sign(Secret const& _k, h256 const& _hash);
/// Verify compact signature (public key is extracted from message).
bool verify(Signature const& _signature, bytesConstRef _message);
/// Verify signature.
bool verify(Public const& _p, Signature const& _sig, bytesConstRef _message, bool _hashed = false);
/// Recovers public key from compact signature. Uses libsecp256k1.
Public recover(Signature _signature, bytesConstRef _message);
bool verifySecret(Secret const& _s, Public const& _p);
}
}

1
libdevcrypto/FileSystem.h

@ -24,6 +24,7 @@
#pragma once
#include <string>
#include <libdevcore/CommonIO.h>
namespace dev
{

3
libdevcrypto/SHA3MAC.cpp

@ -28,9 +28,10 @@ using namespace dev;
using namespace dev::crypto;
using namespace CryptoPP;
void sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output)
void crypto::sha3mac(bytesConstRef _secret, bytesConstRef _plain, bytesRef _output)
{
CryptoPP::SHA3_256 ctx;
assert(_secret.size() > 0);
ctx.Update((byte*)_secret.data(), _secret.size());
ctx.Update((byte*)_plain.data(), _plain.size());
assert(_output.size() >= 32);

1
libwhisper/Message.h

@ -28,6 +28,7 @@
#include <utility>
#include <libdevcore/RLP.h>
#include <libdevcore/Guards.h>
#include <libdevcrypto/Common.h>
#include <libdevcrypto/SHA3.h>
#include "Common.h"

297
test/crypto.cpp

@ -28,6 +28,7 @@
#include <libethereum/Transaction.h>
#include <boost/test/unit_test.hpp>
#include <libdevcrypto/EC.h>
#include <libdevcrypto/SHA3MAC.h>
#include "TestHelperCrypto.h"
using namespace std;
@ -46,107 +47,220 @@ BOOST_AUTO_TEST_CASE(common_encrypt_decrypt)
KeyPair k = KeyPair::create();
bytes cipher;
encrypt(k.pub(), bcr, cipher);
assert(cipher != asBytes(message) && cipher.size() > 0);
BOOST_REQUIRE(cipher != asBytes(message) && cipher.size() > 0);
bytes plain;
decrypt(k.sec(), bytesConstRef(&cipher), plain);
assert(asString(plain) == message);
assert(plain == asBytes(message));
BOOST_REQUIRE(asString(plain) == message);
BOOST_REQUIRE(plain == asBytes(message));
}
BOOST_AUTO_TEST_CASE(cryptopp_vs_secp256k1)
{
ECIES<ECP>::Decryptor d(pp::PRNG(), pp::secp256k1());
ECIES<ECP>::Decryptor d(pp::PRNG, pp::secp256k1Curve);
ECIES<ECP>::Encryptor e(d.GetKey());
Secret s;
pp::SecretFromDL_PrivateKey_EC(d.GetKey(), s);
pp::exportPrivateKey(d.GetKey(), s);
Public p;
pp::PublicFromDL_PublicKey_EC(e.GetKey(), p);
pp::exportPublicKey(e.GetKey(), p);
assert(dev::toAddress(s) == right160(dev::sha3(p.ref())));
BOOST_REQUIRE(dev::toAddress(s) == right160(dev::sha3(p.ref())));
Secret previous = s;
for (auto i = 0; i < 30; i++)
for (auto i = 0; i < 2; i++)
{
ECIES<ECP>::Decryptor d(pp::PRNG(), pp::secp256k1());
ECIES<ECP>::Decryptor d(pp::PRNG, pp::secp256k1Curve);
ECIES<ECP>::Encryptor e(d.GetKey());
Secret s;
pp::SecretFromDL_PrivateKey_EC(d.GetKey(), s);
assert(s != previous);
pp::exportPrivateKey(d.GetKey(), s);
BOOST_REQUIRE(s != previous);
Public p;
pp::PublicFromDL_PublicKey_EC(e.GetKey(), p);
assert(dev::toAddress(s) == right160(dev::sha3(p.ref())));
pp::exportPublicKey(e.GetKey(), p);
h160 secp256k1Addr = dev::toAddress(s);
h160 cryptoppAddr = right160(dev::sha3(p.ref()));
if (secp256k1Addr != cryptoppAddr)
{
BOOST_REQUIRE(secp256k1Addr == cryptoppAddr);
break;
}
}
}
BOOST_AUTO_TEST_CASE(cryptopp_keys_cryptor_sipaseckp256k1)
BOOST_AUTO_TEST_CASE(cryptopp_cryptopp_secp256k1libport)
{
KeyPair k = KeyPair::create();
Secret s = k.sec();
// Convert secret to exponent used by pp
Integer e = pp::ExponentFromSecret(s);
// cryptopp implementation of secp256k1lib sign_compact w/recid parameter and recovery of public key from signature
// base secret
Secret secret(sha3("privacy"));
// we get ec params from signer
const CryptoPP::DL_GroupParameters_EC<CryptoPP::ECP> params = pp::secp256k1Params;
ECDSA<ECP, SHA3_256>::Signer signer;
// e := sha3(msg)
bytes e(fromHex("0x01"));
e.resize(32);
int tests = 2; // Oct 29: successful @ 1500
while (sha3(&e, &e), secret = sha3(secret.asBytes()), tests--)
{
KeyPair key(secret);
Public pkey = key.pub();
pp::initializeDLScheme(secret, signer);
h256 he(sha3(e));
Integer heInt(he.asBytes().data(), 32);
h256 k(crypto::kdf(secret, he));
Integer kInt(k.asBytes().data(), 32);
kInt %= params.GetSubgroupOrder()-1;
ECP::Point rp = params.ExponentiateBase(kInt);
Integer const& q = params.GetGroupOrder();
Integer r = params.ConvertElementToInteger(rp);
int recid = ((r >= q) ? 2 : 0) | (rp.y.IsOdd() ? 1 : 0);
Integer kInv = kInt.InverseMod(q);
Integer s = (kInv * (Integer(secret.asBytes().data(), 32)*r + heInt)) % q;
BOOST_REQUIRE(!!r && !!s);
/*
// For future reference:
// According to maths, this codepath can't be reached, however, it's in secp256k1.
// Commenting this out diverges from codebase implementation.
// To be removed after upstream PR and proof are evaulated.
if (s > params.GetSubgroupOrder())
{
// note: this rarely happens
s = params.GetGroupOrder() - s;
if (recid)
recid ^= 1;
}
*/
// Test that exported DL_EC private is same as exponent from Secret
CryptoPP::DL_PrivateKey_EC<CryptoPP::ECP> privatek;
privatek.AccessGroupParameters().Initialize(pp::secp256k1());
privatek.SetPrivateExponent(e);
assert(e == privatek.GetPrivateExponent());
// Test that exported secret is same as decryptor(privatek) secret
ECIES<ECP>::Decryptor d;
d.AccessKey().AccessGroupParameters().Initialize(pp::secp256k1());
d.AccessKey().SetPrivateExponent(e);
assert(d.AccessKey().GetPrivateExponent() == e);
// Test that decryptor->encryptor->public == private->makepublic->public
CryptoPP::DL_PublicKey_EC<CryptoPP::ECP> pubk;
pubk.AccessGroupParameters().Initialize(pp::secp256k1());
privatek.MakePublicKey(pubk);
ECIES<ECP>::Encryptor enc(d);
assert(pubk.GetPublicElement() == enc.AccessKey().GetPublicElement());
// Test against sipa/seckp256k1
Public p;
pp::PublicFromExponent(pp::ExponentFromSecret(s), p);
assert(toAddress(s) == dev::right160(dev::sha3(p.ref())));
assert(k.pub() == p);
Signature sig;
r.Encode(sig.data(), 32);
s.Encode(sig.data() + 32, 32);
sig[64] = recid;
Public p = dev::recover(sig, he);
BOOST_REQUIRE(p == pkey);
// verify w/cryptopp
BOOST_REQUIRE(crypto::verify(pkey, sig, bytesConstRef(&e)));
// verify with secp256k1lib
byte encpub[65] = {0x04};
memcpy(&encpub[1], pkey.data(), 64);
byte dersig[72];
size_t cssz = DSAConvertSignatureFormat(dersig, 72, DSA_DER, sig.data(), 64, DSA_P1363);
BOOST_CHECK(cssz <= 72);
BOOST_REQUIRE(1 == secp256k1_ecdsa_verify(he.data(), sizeof(he), dersig, cssz, encpub, 65));
}
}
BOOST_AUTO_TEST_CASE(cryptopp_ecdsa_sipaseckp256k1)
{
// cryptopp integer encoding
Integer nHex("f2ee15ea639b73fa3db9b34a245bdfa015c260c598b211bf05a1ecc4b3e3b4f2H");
Integer nB(fromHex("f2ee15ea639b73fa3db9b34a245bdfa015c260c598b211bf05a1ecc4b3e3b4f2").data(), 32);
BOOST_REQUIRE(nHex == nB);
bytes sbytes(fromHex("0x01"));
Secret secret(sha3(sbytes)); // 5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2
KeyPair key(secret);
bytes m(fromHex("0x01"));
int tests = 2;
while (m[0]++, tests--)
{
h256 hm(sha3(m));
Integer hInt(hm.asBytes().data(), 32);
h256 k(hm ^ key.sec());
Integer kInt(k.asBytes().data(), 32);
// raw sign w/cryptopp (doesn't pass through cryptopp hash filter)
ECDSA<ECP, SHA3_256>::Signer signer;
pp::initializeDLScheme(key.sec(), signer);
Integer r, s;
signer.RawSign(kInt, hInt, r, s);
// verify cryptopp raw-signature w/cryptopp
ECDSA<ECP, SHA3_256>::Verifier verifier;
pp::initializeDLScheme(key.pub(), verifier);
Signature sigppraw;
r.Encode(sigppraw.data(), 32);
s.Encode(sigppraw.data() + 32, 32);
BOOST_REQUIRE(verifier.VerifyMessage(m.data(), m.size(), sigppraw.data(), 64));
BOOST_REQUIRE(crypto::verify(key.pub(), sigppraw, bytesConstRef(&m)));
BOOST_REQUIRE(dev::verify(key.pub(), sigppraw, hm));
// sign with cryptopp, verify, recover w/sec256lib
Signature seclibsig(dev::sign(key.sec(), hm));
BOOST_REQUIRE(verifier.VerifyMessage(m.data(), m.size(), seclibsig.data(), 64));
BOOST_REQUIRE(crypto::verify(key.pub(), seclibsig, bytesConstRef(&m)));
BOOST_REQUIRE(dev::verify(key.pub(), seclibsig, hm));
BOOST_REQUIRE(dev::recover(seclibsig, hm) == key.pub());
// sign with cryptopp (w/hash filter?), verify with cryptopp
bytes sigppb(signer.MaxSignatureLength());
size_t ssz = signer.SignMessage(pp::PRNG, m.data(), m.size(), sigppb.data());
Signature sigpp;
memcpy(sigpp.data(), sigppb.data(), 64);
BOOST_REQUIRE(verifier.VerifyMessage(m.data(), m.size(), sigppb.data(), ssz));
BOOST_REQUIRE(crypto::verify(key.pub(), sigpp, bytesConstRef(&m)));
BOOST_REQUIRE(dev::verify(key.pub(), sigpp, hm));
// sign with cryptopp and stringsource hash filter
string sigstr;
StringSource ssrc(asString(m), true, new SignerFilter(pp::PRNG, signer, new StringSink(sigstr)));
FixedHash<sizeof(Signature)> retsig((byte const*)sigstr.data(), Signature::ConstructFromPointer);
BOOST_REQUIRE(verifier.VerifyMessage(m.data(), m.size(), retsig.data(), 64));
BOOST_REQUIRE(crypto::verify(key.pub(), retsig, bytesConstRef(&m)));
BOOST_REQUIRE(dev::verify(key.pub(), retsig, hm));
/// verification w/sec256lib
// requires public key and sig in standard format
byte encpub[65] = {0x04};
memcpy(&encpub[1], key.pub().data(), 64);
byte dersig[72];
// verify sec256lib sig w/sec256lib
size_t cssz = DSAConvertSignatureFormat(dersig, 72, DSA_DER, seclibsig.data(), 64, DSA_P1363);
BOOST_CHECK(cssz <= 72);
BOOST_REQUIRE(1 == secp256k1_ecdsa_verify(hm.data(), sizeof(hm), dersig, cssz, encpub, 65));
// verify cryptopp-raw sig w/sec256lib
cssz = DSAConvertSignatureFormat(dersig, 72, DSA_DER, sigppraw.data(), 64, DSA_P1363);
BOOST_CHECK(cssz <= 72);
BOOST_REQUIRE(1 == secp256k1_ecdsa_verify(hm.data(), sizeof(hm), dersig, cssz, encpub, 65));
// verify cryptopp sig w/sec256lib
cssz = DSAConvertSignatureFormat(dersig, 72, DSA_DER, sigppb.data(), 64, DSA_P1363);
BOOST_CHECK(cssz <= 72);
BOOST_REQUIRE(1 == secp256k1_ecdsa_verify(hm.data(), sizeof(hm), dersig, cssz, encpub, 65));
}
}
BOOST_AUTO_TEST_CASE(cryptopp_public_export_import)
{
ECIES<ECP>::Decryptor d(pp::PRNG(), pp::secp256k1());
ECIES<ECP>::Decryptor d(pp::PRNG, pp::secp256k1Curve);
ECIES<ECP>::Encryptor e(d.GetKey());
Secret s;
pp::SecretFromDL_PrivateKey_EC(d.GetKey(), s);
pp::exportPrivateKey(d.GetKey(), s);
Public p;
pp::PublicFromDL_PublicKey_EC(e.GetKey(), p);
pp::exportPublicKey(e.GetKey(), p);
Address addr = right160(dev::sha3(p.ref()));
assert(toAddress(s) == addr);
BOOST_REQUIRE(toAddress(s) == addr);
KeyPair l(s);
assert(l.address() == addr);
DL_PublicKey_EC<ECP> pub;
pub.Initialize(pp::secp256k1(), pp::PointFromPublic(p));
assert(pub.GetPublicElement() == e.GetKey().GetPublicElement());
KeyPair k = KeyPair::create();
Public p2;
pp::PublicFromExponent(pp::ExponentFromSecret(k.sec()), p2);
assert(k.pub() == p2);
Address a = k.address();
Address a2 = toAddress(k.sec());
assert(a2 == a);
BOOST_REQUIRE(l.address() == addr);
}
BOOST_AUTO_TEST_CASE(ecies_eckeypair)
@ -158,10 +272,10 @@ BOOST_AUTO_TEST_CASE(ecies_eckeypair)
bytes b = asBytes(message);
encrypt(k.pub(), b);
assert(b != asBytes(original));
BOOST_REQUIRE(b != asBytes(original));
decrypt(k.sec(), b);
assert(b == asBytes(original));
BOOST_REQUIRE(b == asBytes(original));
}
BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac)
@ -172,9 +286,6 @@ BOOST_AUTO_TEST_CASE(ecdhe_aes128_ctr_sha3mac)
// All connections should share seed for PRF (or PRNG) for nonces
}
BOOST_AUTO_TEST_CASE(cryptopp_ecies_message)
@ -183,7 +294,7 @@ BOOST_AUTO_TEST_CASE(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::secp256k1());
ECIES<ECP>::Decryptor localDecryptor(pp::PRNG, pp::secp256k1Curve);
SavePrivateKey(localDecryptor.GetPrivateKey());
ECIES<ECP>::Encryptor localEncryptor(localDecryptor);
@ -191,43 +302,43 @@ BOOST_AUTO_TEST_CASE(cryptopp_ecies_message)
ECIES<ECP>::Decryptor futureDecryptor;
LoadPrivateKey(futureDecryptor.AccessPrivateKey());
futureDecryptor.GetPrivateKey().ThrowIfInvalid(pp::PRNG(), 3);
futureDecryptor.GetPrivateKey().ThrowIfInvalid(pp::PRNG, 3);
ECIES<ECP>::Encryptor futureEncryptor;
LoadPublicKey(futureEncryptor.AccessPublicKey());
futureEncryptor.GetPublicKey().ThrowIfInvalid(pp::PRNG(), 3);
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) ) );
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) ) );
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) ) );
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) ) );
StringSource ss4 (cipherFuture, true, new PK_DecryptorFilter(pp::PRNG, futureDecryptor, new StringSink(plainFuture) ) );
// decrypt local w/future
string plainFutureFromLocal;
StringSource ss5 (cipherLocal, true, new PK_DecryptorFilter(pp::PRNG(), futureDecryptor, new StringSink(plainFutureFromLocal) ) );
StringSource ss5 (cipherLocal, true, new PK_DecryptorFilter(pp::PRNG, futureDecryptor, new StringSink(plainFutureFromLocal) ) );
// decrypt future w/local
string plainLocalFromFuture;
StringSource ss6 (cipherFuture, true, new PK_DecryptorFilter(pp::PRNG(), localDecryptor, new StringSink(plainLocalFromFuture) ) );
StringSource ss6 (cipherFuture, true, new PK_DecryptorFilter(pp::PRNG, localDecryptor, new StringSink(plainLocalFromFuture) ) );
assert(plainLocal == message);
assert(plainFuture == plainLocal);
assert(plainFutureFromLocal == plainLocal);
assert(plainLocalFromFuture == plainLocal);
BOOST_REQUIRE(plainLocal == message);
BOOST_REQUIRE(plainFuture == plainLocal);
BOOST_REQUIRE(plainFutureFromLocal == plainLocal);
BOOST_REQUIRE(plainLocalFromFuture == plainLocal);
}
BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr)
{
const int aesKeyLen = 16;
assert(sizeof(char) == sizeof(byte));
BOOST_REQUIRE(sizeof(char) == sizeof(byte));
// generate test key
AutoSeededRandomPool rng;
@ -250,7 +361,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr)
CTR_Mode<AES>::Encryption e;
e.SetKeyWithIV(key, key.size(), ctr);
e.ProcessData(out, in, text.size());
assert(text != original);
BOOST_REQUIRE(text != original);
cipherCopy = text;
}
catch(CryptoPP::Exception& e)
@ -263,7 +374,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr)
CTR_Mode< AES >::Decryption d;
d.SetKeyWithIV(key, key.size(), ctr);
d.ProcessData(out, in, text.size());
assert(text == original);
BOOST_REQUIRE(text == original);
}
catch(CryptoPP::Exception& e)
{
@ -274,7 +385,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr)
// reencrypt ciphertext...
try
{
assert(cipherCopy != text);
BOOST_REQUIRE(cipherCopy != text);
in = (unsigned char*)&cipherCopy[0];
out = (unsigned char*)&cipherCopy[0];
@ -283,7 +394,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr)
e.ProcessData(out, in, text.size());
// yep, ctr mode.
assert(cipherCopy == original);
BOOST_REQUIRE(cipherCopy == original);
}
catch(CryptoPP::Exception& e)
{
@ -295,7 +406,7 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_ctr)
BOOST_AUTO_TEST_CASE(cryptopp_aes128_cbc)
{
const int aesKeyLen = 16;
assert(sizeof(char) == sizeof(byte));
BOOST_REQUIRE(sizeof(char) == sizeof(byte));
AutoSeededRandomPool rng;
SecByteBlock key(0x00, aesKeyLen);
@ -310,11 +421,11 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_cbc)
CryptoPP::CBC_Mode<Rijndael>::Encryption cbcEncryption(key, key.size(), iv);
cbcEncryption.ProcessData((byte*)&string128[0], (byte*)&string128[0], string128.size());
assert(string128 != plainOriginal);
BOOST_REQUIRE(string128 != plainOriginal);
CBC_Mode<Rijndael>::Decryption cbcDecryption(key, key.size(), iv);
cbcDecryption.ProcessData((byte*)&string128[0], (byte*)&string128[0], string128.size());
assert(plainOriginal == string128);
BOOST_REQUIRE(plainOriginal == string128);
// plaintext whose size isn't divisible by block size must use stream filter for padding
@ -324,10 +435,10 @@ BOOST_AUTO_TEST_CASE(cryptopp_aes128_cbc)
string cipher;
StreamTransformationFilter* aesStream = new StreamTransformationFilter(cbcEncryption, new StringSink(cipher));
StringSource source(string192, true, aesStream);
assert(cipher.size() == 32);
BOOST_REQUIRE(cipher.size() == 32);
cbcDecryption.ProcessData((byte*)&cipher[0], (byte*)&string192[0], cipher.size());
assert(string192 == plainOriginal);
BOOST_REQUIRE(string192 == plainOriginal);
}
BOOST_AUTO_TEST_CASE(eth_keypairs)
@ -360,8 +471,8 @@ int cryptoTest()
secp256k1_start();
KeyPair p(Secret(fromHex("3ecb44df2159c26e0f995712d4f39b6f6e499b40749b1cf1246c37f9516cb6a4")));
assert(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f")));
assert(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075")));
BOOST_REQUIRE(p.pub() == Public(fromHex("97466f2b32bc3bb76d4741ae51cd1d8578b48d3f1e68da206d47321aec267ce78549b514e4453d74ef11b0cd5e4e4c364effddac8b51bcfc8de80682f952896f")));
BOOST_REQUIRE(p.address() == Address(fromHex("8a40bfaa73256b60764c1bf40675a99083efb075")));
{
eth::Transaction t(1000, 0, 0, h160(fromHex("944400f4b88ac9589a0f17ed4671da26bddb668b")), bytes(), 0, p.secret());
auto rlp = t.rlp(eth::WithoutSignature);

Loading…
Cancel
Save