diff --git a/src/Makefile.am b/src/Makefile.am index 8651dd2987..d2edac32a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -94,7 +94,6 @@ BITCOIN_CORE_H = \ crypter.h \ db.h \ eccryptoverify.h \ - ecwrapper.h \ hash.h \ init.h \ key.h \ @@ -242,7 +241,6 @@ libbitcoin_common_a_SOURCES = \ core_read.cpp \ core_write.cpp \ eccryptoverify.cpp \ - ecwrapper.cpp \ hash.cpp \ key.cpp \ keystore.cpp \ @@ -363,7 +361,6 @@ libbitcoinconsensus_la_SOURCES = \ crypto/sha512.cpp \ crypto/ripemd160.cpp \ eccryptoverify.cpp \ - ecwrapper.cpp \ hash.cpp \ pubkey.cpp \ script/script.cpp \ @@ -378,7 +375,7 @@ if GLIBC_BACK_COMPAT endif libbitcoinconsensus_la_LDFLAGS = -no-undefined -version-info 1:0:0 $(RELDFLAGS) -libbitcoinconsensus_la_LIBADD = $(CRYPTO_LIBS) $(SSL_LIBS) +libbitcoinconsensus_la_LIBADD = $(CRYPTO_LIBS) $(LIBSECP256K1) libbitcoinconsensus_la_CPPFLAGS = $(CRYPTO_CFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL if USE_LIBSECP256K1 libbitcoinconsensus_la_LIBADD += secp256k1/libsecp256k1.la diff --git a/src/ecwrapper.cpp b/src/ecwrapper.cpp deleted file mode 100644 index 5bfa740cc6..0000000000 --- a/src/ecwrapper.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include "ecwrapper.h" - -#include "serialize.h" -#include "uint256.h" - -#include -#include -#include - -namespace { - -/** - * Perform ECDSA key recovery (see SEC1 4.1.6) for curves over (mod p)-fields - * recid selects which key is recovered - * if check is non-zero, additional checks are performed - */ -int ECDSA_SIG_recover_key_GFp(EC_KEY *eckey, ECDSA_SIG *ecsig, const unsigned char *msg, int msglen, int recid, int check) -{ - if (!eckey) return 0; - - int ret = 0; - BN_CTX *ctx = NULL; - - BIGNUM *x = NULL; - BIGNUM *e = NULL; - BIGNUM *order = NULL; - BIGNUM *sor = NULL; - BIGNUM *eor = NULL; - BIGNUM *field = NULL; - EC_POINT *R = NULL; - EC_POINT *O = NULL; - EC_POINT *Q = NULL; - BIGNUM *rr = NULL; - BIGNUM *zero = NULL; - int n = 0; - int i = recid / 2; - - const EC_GROUP *group = EC_KEY_get0_group(eckey); - if ((ctx = BN_CTX_new()) == NULL) { ret = -1; goto err; } - BN_CTX_start(ctx); - order = BN_CTX_get(ctx); - if (!EC_GROUP_get_order(group, order, ctx)) { ret = -2; goto err; } - x = BN_CTX_get(ctx); - if (!BN_copy(x, order)) { ret=-1; goto err; } - if (!BN_mul_word(x, i)) { ret=-1; goto err; } - if (!BN_add(x, x, ecsig->r)) { ret=-1; goto err; } - field = BN_CTX_get(ctx); - if (!EC_GROUP_get_curve_GFp(group, field, NULL, NULL, ctx)) { ret=-2; goto err; } - if (BN_cmp(x, field) >= 0) { ret=0; goto err; } - if ((R = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_set_compressed_coordinates_GFp(group, R, x, recid % 2, ctx)) { ret=0; goto err; } - if (check) - { - if ((O = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - if (!EC_POINT_mul(group, O, NULL, R, order, ctx)) { ret=-2; goto err; } - if (!EC_POINT_is_at_infinity(group, O)) { ret = 0; goto err; } - } - if ((Q = EC_POINT_new(group)) == NULL) { ret = -2; goto err; } - n = EC_GROUP_get_degree(group); - e = BN_CTX_get(ctx); - if (!BN_bin2bn(msg, msglen, e)) { ret=-1; goto err; } - if (8*msglen > n) BN_rshift(e, e, 8-(n & 7)); - zero = BN_CTX_get(ctx); - if (!BN_zero(zero)) { ret=-1; goto err; } - if (!BN_mod_sub(e, zero, e, order, ctx)) { ret=-1; goto err; } - rr = BN_CTX_get(ctx); - if (!BN_mod_inverse(rr, ecsig->r, order, ctx)) { ret=-1; goto err; } - sor = BN_CTX_get(ctx); - if (!BN_mod_mul(sor, ecsig->s, rr, order, ctx)) { ret=-1; goto err; } - eor = BN_CTX_get(ctx); - if (!BN_mod_mul(eor, e, rr, order, ctx)) { ret=-1; goto err; } - if (!EC_POINT_mul(group, Q, eor, R, sor, ctx)) { ret=-2; goto err; } - if (!EC_KEY_set_public_key(eckey, Q)) { ret=-2; goto err; } - - ret = 1; - -err: - if (ctx) { - BN_CTX_end(ctx); - BN_CTX_free(ctx); - } - if (R != NULL) EC_POINT_free(R); - if (O != NULL) EC_POINT_free(O); - if (Q != NULL) EC_POINT_free(Q); - return ret; -} - -} // anon namespace - -CECKey::CECKey() { - pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - assert(pkey != NULL); -} - -CECKey::~CECKey() { - EC_KEY_free(pkey); -} - -void CECKey::GetPubKey(std::vector &pubkey, bool fCompressed) { - EC_KEY_set_conv_form(pkey, fCompressed ? POINT_CONVERSION_COMPRESSED : POINT_CONVERSION_UNCOMPRESSED); - int nSize = i2o_ECPublicKey(pkey, NULL); - assert(nSize); - assert(nSize <= 65); - pubkey.clear(); - pubkey.resize(nSize); - unsigned char *pbegin(begin_ptr(pubkey)); - int nSize2 = i2o_ECPublicKey(pkey, &pbegin); - assert(nSize == nSize2); -} - -bool CECKey::SetPubKey(const unsigned char* pubkey, size_t size) { - return o2i_ECPublicKey(&pkey, &pubkey, size) != NULL; -} - -bool CECKey::Verify(const uint256 &hash, const std::vector& vchSig) { - if (vchSig.empty()) - return false; - - // New versions of OpenSSL will reject non-canonical DER signatures. de/re-serialize first. - unsigned char *norm_der = NULL; - ECDSA_SIG *norm_sig = ECDSA_SIG_new(); - const unsigned char* sigptr = &vchSig[0]; - assert(norm_sig); - if (d2i_ECDSA_SIG(&norm_sig, &sigptr, vchSig.size()) == NULL) - { - /* As of OpenSSL 1.0.0p d2i_ECDSA_SIG frees and nulls the pointer on - * error. But OpenSSL's own use of this function redundantly frees the - * result. As ECDSA_SIG_free(NULL) is a no-op, and in the absence of a - * clear contract for the function behaving the same way is more - * conservative. - */ - ECDSA_SIG_free(norm_sig); - return false; - } - int derlen = i2d_ECDSA_SIG(norm_sig, &norm_der); - ECDSA_SIG_free(norm_sig); - if (derlen <= 0) - return false; - - // -1 = error, 0 = bad sig, 1 = good - bool ret = ECDSA_verify(0, (unsigned char*)&hash, sizeof(hash), norm_der, derlen, pkey) == 1; - OPENSSL_free(norm_der); - return ret; -} - -bool CECKey::Recover(const uint256 &hash, const unsigned char *p64, int rec) -{ - if (rec<0 || rec>=3) - return false; - ECDSA_SIG *sig = ECDSA_SIG_new(); - BN_bin2bn(&p64[0], 32, sig->r); - BN_bin2bn(&p64[32], 32, sig->s); - bool ret = ECDSA_SIG_recover_key_GFp(pkey, sig, (unsigned char*)&hash, sizeof(hash), rec, 0) == 1; - ECDSA_SIG_free(sig); - return ret; -} - -bool CECKey::TweakPublic(const unsigned char vchTweak[32]) { - bool ret = true; - BN_CTX *ctx = BN_CTX_new(); - BN_CTX_start(ctx); - BIGNUM *bnTweak = BN_CTX_get(ctx); - BIGNUM *bnOrder = BN_CTX_get(ctx); - BIGNUM *bnOne = BN_CTX_get(ctx); - const EC_GROUP *group = EC_KEY_get0_group(pkey); - EC_GROUP_get_order(group, bnOrder, ctx); // what a grossly inefficient way to get the (constant) group order... - BN_bin2bn(vchTweak, 32, bnTweak); - if (BN_cmp(bnTweak, bnOrder) >= 0) - ret = false; // extremely unlikely - EC_POINT *point = EC_POINT_dup(EC_KEY_get0_public_key(pkey), group); - BN_one(bnOne); - EC_POINT_mul(group, point, bnTweak, point, bnOne, ctx); - if (EC_POINT_is_at_infinity(group, point)) - ret = false; // ridiculously unlikely - EC_KEY_set_public_key(pkey, point); - EC_POINT_free(point); - BN_CTX_end(ctx); - BN_CTX_free(ctx); - return ret; -} - -bool CECKey::SanityCheck() -{ - EC_KEY *pkey = EC_KEY_new_by_curve_name(NID_secp256k1); - if(pkey == NULL) - return false; - EC_KEY_free(pkey); - - // TODO Is there more EC functionality that could be missing? - return true; -} diff --git a/src/ecwrapper.h b/src/ecwrapper.h deleted file mode 100644 index 4efde51650..0000000000 --- a/src/ecwrapper.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2009-2014 The Bitcoin developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#ifndef BITCOIN_ECWRAPPER_H -#define BITCOIN_ECWRAPPER_H - -#include -#include - -#include - -class uint256; - -/** RAII Wrapper around OpenSSL's EC_KEY */ -class CECKey { -private: - EC_KEY *pkey; - -public: - CECKey(); - ~CECKey(); - - void GetPubKey(std::vector& pubkey, bool fCompressed); - bool SetPubKey(const unsigned char* pubkey, size_t size); - bool Verify(const uint256 &hash, const std::vector& vchSig); - - /** - * reconstruct public key from a compact signature - * This is only slightly more CPU intensive than just verifying it. - * If this function succeeds, the recovered public key is guaranteed to be valid - * (the signature is a valid signature of the given data for that key) - */ - bool Recover(const uint256 &hash, const unsigned char *p64, int rec); - - bool TweakPublic(const unsigned char vchTweak[32]); - static bool SanityCheck(); -}; - -#endif // BITCOIN_ECWRAPPER_H diff --git a/src/init.cpp b/src/init.cpp index 970fb0ec1f..902c33c0c9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -17,6 +17,7 @@ #include "main.h" #include "miner.h" #include "net.h" +#include "pubkey.h" #include "rpcserver.h" #include "script/standard.h" #include "txdb.h" @@ -196,6 +197,7 @@ void Shutdown() pwalletMain = NULL; #endif ECC_Stop(); + ECC_Verify_Stop(); LogPrintf("%s: done\n", __func__); } @@ -770,6 +772,7 @@ bool AppInit2(boost::thread_group& threadGroup) // ********************************************************* Step 4: application initialization: dir lock, daemonize, pidfile, debug log // Initialize elliptic curve code + ECC_Verify_Start(); ECC_Start(); // Sanity check diff --git a/src/key.cpp b/src/key.cpp index 9674ae1c89..507361d190 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -12,7 +12,6 @@ #include "random.h" #include -#include "ecwrapper.h" static secp256k1_context_t* secp256k1_context = NULL; @@ -183,11 +182,6 @@ void CExtKey::Decode(const unsigned char code[74]) { } bool ECC_InitSanityCheck() { -#if !defined(USE_SECP256K1) - if (!CECKey::SanityCheck()) { - return false; - } -#endif CKey key; key.MakeNewKey(true); CPubKey pubkey = key.GetPubKey(); diff --git a/src/pubkey.cpp b/src/pubkey.cpp index 91979ff4dc..162be80079 100644 --- a/src/pubkey.cpp +++ b/src/pubkey.cpp @@ -4,27 +4,15 @@ #include "pubkey.h" -#include "eccryptoverify.h" - -#ifdef USE_SECP256K1 #include -#else -#include "ecwrapper.h" -#endif + +static secp256k1_context_t* secp256k1_context = NULL; bool CPubKey::Verify(const uint256 &hash, const std::vector& vchSig) const { if (!IsValid()) return false; -#ifdef USE_SECP256K1 - if (secp256k1_ecdsa_verify((const unsigned char*)&hash, 32, &vchSig[0], vchSig.size(), begin(), size()) != 1) + if (secp256k1_ecdsa_verify(secp256k1_context, (const unsigned char*)&hash, &vchSig[0], vchSig.size(), begin(), size()) != 1) return false; -#else - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; - if (!key.Verify(hash, vchSig)) - return false; -#endif return true; } @@ -33,52 +21,28 @@ bool CPubKey::RecoverCompact(const uint256 &hash, const std::vector pubkey; - key.GetPubKey(pubkey, fComp); - Set(pubkey.begin(), pubkey.end()); -#endif return true; } bool CPubKey::IsFullyValid() const { if (!IsValid()) return false; -#ifdef USE_SECP256K1 - if (!secp256k1_ecdsa_pubkey_verify(begin(), size())) + if (!secp256k1_ec_pubkey_verify(secp256k1_context, begin(), size())) return false; -#else - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; -#endif return true; } bool CPubKey::Decompress() { if (!IsValid()) return false; -#ifdef USE_SECP256K1 int clen = size(); - int ret = secp256k1_ecdsa_pubkey_decompress((unsigned char*)begin(), &clen); + int ret = secp256k1_ec_pubkey_decompress(secp256k1_context, (unsigned char*)begin(), &clen); assert(ret); assert(clen == (int)size()); -#else - CECKey key; - if (!key.SetPubKey(begin(), size())) - return false; - std::vector pubkey; - key.GetPubKey(pubkey, false); - Set(pubkey.begin(), pubkey.end()); -#endif return true; } @@ -89,17 +53,8 @@ bool CPubKey::Derive(CPubKey& pubkeyChild, unsigned char ccChild[32], unsigned i unsigned char out[64]; BIP32Hash(cc, nChild, *begin(), begin()+1, out); memcpy(ccChild, out+32, 32); -#ifdef USE_SECP256K1 pubkeyChild = *this; - bool ret = secp256k1_ecdsa_pubkey_tweak_add((unsigned char*)pubkeyChild.begin(), pubkeyChild.size(), out); -#else - CECKey key; - bool ret = key.SetPubKey(begin(), size()); - ret &= key.TweakPublic(out); - std::vector pubkey; - key.GetPubKey(pubkey, true); - pubkeyChild.Set(pubkey.begin(), pubkey.end()); -#endif + bool ret = secp256k1_ec_pubkey_tweak_add(secp256k1_context, (unsigned char*)pubkeyChild.begin(), pubkeyChild.size(), out); return ret; } @@ -128,3 +83,21 @@ bool CExtPubKey::Derive(CExtPubKey &out, unsigned int nChild) const { out.nChild = nChild; return pubkey.Derive(out.pubkey, out.vchChainCode, nChild, vchChainCode); } + +void ECC_Verify_Start() { + assert(secp256k1_context == NULL); + + secp256k1_context_t *ctx = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + assert(ctx != NULL); + + secp256k1_context = ctx; +} + +void ECC_Verify_Stop() { + secp256k1_context_t *ctx = secp256k1_context; + secp256k1_context = NULL; + + if (ctx) { + secp256k1_context_destroy(ctx); + } +} diff --git a/src/pubkey.h b/src/pubkey.h index 37351cff0e..2ce4a0ff3d 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -203,4 +203,10 @@ struct CExtPubKey { bool Derive(CExtPubKey& out, unsigned int nChild) const; }; +/** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */ +void ECC_Verify_Start(void); + +/** Deinitialize the elliptic curve support. No-op if ECC_Start wasn't called first. */ +void ECC_Verify_Stop(void); + #endif // BITCOIN_PUBKEY_H diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index 7a8f04624a..9b5ca34ce9 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -6,6 +6,7 @@ #include "key.h" #include "main.h" +#include "pubkey.h" #include "random.h" #include "txdb.h" #include "ui_interface.h" @@ -31,6 +32,7 @@ struct TestingSetup { boost::thread_group threadGroup; TestingSetup() { + ECC_Verify_Start(); ECC_Start(); SetupEnvironment(); fPrintToDebugLog = false; // don't want to write to debug.log file @@ -75,6 +77,7 @@ struct TestingSetup { #endif boost::filesystem::remove_all(pathTemp); ECC_Stop(); + ECC_Verify_Stop(); } };